diff options
author | NoahY <yuttadhammo@yahoo.com> | 2011-07-25 23:33:48 +0530 |
---|---|---|
committer | NoahY <yuttadhammo@yahoo.com> | 2011-07-25 23:33:48 +0530 |
commit | f594b973c647f77a31daa0ce94f47bb5fb0f09ce (patch) | |
tree | 4e37199da6ddcfc212060d26ca41236095e11529 | |
parent | 559db45d7bc5cf5b994d9c5397b38ad63d5c48b6 (diff) | |
parent | 2ba5908f7dcc4b8396383472259ac2d85968cf4d (diff) | |
download | askbot-f594b973c647f77a31daa0ce94f47bb5fb0f09ce.tar.gz askbot-f594b973c647f77a31daa0ce94f47bb5fb0f09ce.tar.bz2 askbot-f594b973c647f77a31daa0ce94f47bb5fb0f09ce.zip |
Merge branch 'master', remote-tracking branch 'upstream/master'
133 files changed, 32 insertions, 27262 deletions
@@ -37,3 +37,4 @@ askbot/upfiles/avatars/ askbot/skins/common/media/mathjax/ /jinja2 *.tar.tz +run diff --git a/askbot/deps/__init__.py b/askbot/deps/__init__.py index d25583a2..497f8c9b 100644 --- a/askbot/deps/__init__.py +++ b/askbot/deps/__init__.py @@ -16,9 +16,4 @@ Some packages included here were modified with hardcoded imports like:: from askbot.deps import somepackage So these cannot be moved around at all. - -There is one package - ``python.openid`` which has not been modified this way. -In order for this one to import, PYTHONPATH variable was modified within `django.wsgi`_ -file - the connector script for Apache mod_wsgi. - """ diff --git a/askbot/deps/django_authopenid/forms.py b/askbot/deps/django_authopenid/forms.py index 91534221..065a2186 100644 --- a/askbot/deps/django_authopenid/forms.py +++ b/askbot/deps/django_authopenid/forms.py @@ -42,7 +42,7 @@ from askbot.utils.forms import NextUrlField, UserNameField, UserEmailField, SetP # needed for some linux distributions like debian try: - from askbot.deps.openid.yadis import xri + from openid.yadis import xri except ImportError: from yadis import xri @@ -322,7 +322,10 @@ class SafeClassicRegisterForm(ClassicRegisterForm): """this form uses recaptcha in addition to the base register form """ - recaptcha = ReCaptchaField() + recaptcha = ReCaptchaField( + private_key = askbot_settings.RECAPTCHA_SECRET, + public_key = askbot_settings.RECAPTCHA_KEY + ) class ChangePasswordForm(SetPasswordForm): """ change password form """ diff --git a/askbot/deps/django_authopenid/util.py b/askbot/deps/django_authopenid/util.py index 44328a25..9dfbc596 100644 --- a/askbot/deps/django_authopenid/util.py +++ b/askbot/deps/django_authopenid/util.py @@ -3,10 +3,10 @@ import cgi import urllib import functools import re -from askbot.deps.openid.store.interface import OpenIDStore -from askbot.deps.openid.association import Association as OIDAssociation -from askbot.deps.openid.extensions import sreg -from askbot.deps.openid import store as openid_store +from openid.store.interface import OpenIDStore +from openid.association import Association as OIDAssociation +from openid.extensions import sreg +from openid import store as openid_store import oauth2 as oauth from django.db.models.query import Q @@ -24,7 +24,7 @@ from askbot.conf import settings as askbot_settings # needed for some linux distributions like debian try: - from askbot.deps.openid.yadis import xri + from openid.yadis import xri except: from yadis import xri diff --git a/askbot/deps/django_authopenid/views.py b/askbot/deps/django_authopenid/views.py index c14c84f1..212e94ff 100644 --- a/askbot/deps/django_authopenid/views.py +++ b/askbot/deps/django_authopenid/views.py @@ -48,13 +48,13 @@ from django.utils.safestring import mark_safe from django.core.mail import send_mail from askbot.skins.loaders import render_into_skin, get_template -from askbot.deps.openid.consumer.consumer import Consumer, \ +from openid.consumer.consumer import Consumer, \ SUCCESS, CANCEL, FAILURE, SETUP_NEEDED -from askbot.deps.openid.consumer.discover import DiscoveryFailure -from askbot.deps.openid.extensions import sreg +from openid.consumer.discover import DiscoveryFailure +from openid.extensions import sreg # needed for some linux distributions like debian try: - from askbot.deps.openid.yadis import xri + from openid.yadis import xri except ImportError: from yadis import xri @@ -517,6 +517,8 @@ def show_signin_view( #annotate objects with extra data providers = util.get_enabled_login_providers() for login_method in existing_login_methods: + if login_method.provider_name == 'facebook': + continue#it is disabled provider_data = providers[login_method.provider_name] if provider_data['type'] == 'password': #only external password logins will not be deletable diff --git a/askbot/deps/openid/__init__.py b/askbot/deps/openid/__init__.py deleted file mode 100644 index 6be6538e..00000000 --- a/askbot/deps/openid/__init__.py +++ /dev/null @@ -1,55 +0,0 @@ -""" -This package is an implementation of the OpenID specification in -Python. It contains code for both server and consumer -implementations. For information on implementing an OpenID consumer, -see the C{L{openid.consumer.consumer}} module. For information on -implementing an OpenID server, see the C{L{openid.server.server}} -module. - -@contact: U{http://openid.net/developers/dev-mailing-lists/ - <http://openid.net/developers/dev-mailing-lists/} - -@copyright: (C) 2005-2008 JanRain, Inc. - -@license: 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 - U{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. -""" - -__version__ = '[library version:2.2.1]'[17:-1] - -__all__ = [ - 'association', - 'consumer', - 'cryptutil', - 'dh', - 'extension', - 'extensions', - 'fetchers', - 'kvform', - 'message', - 'oidutil', - 'server', - 'sreg', - 'store', - 'urinorm', - 'yadis', - ] - -# Parse the version info -try: - version_info = map(int, __version__.split('.')) -except ValueError: - version_info = (None, None, None) -else: - if len(version_info) != 3: - version_info = (None, None, None) - else: - version_info = tuple(version_info) diff --git a/askbot/deps/openid/association.py b/askbot/deps/openid/association.py deleted file mode 100644 index a5fafffc..00000000 --- a/askbot/deps/openid/association.py +++ /dev/null @@ -1,555 +0,0 @@ -# -*- test-case-name: openid.test.test_association -*- -""" -This module contains code for dealing with associations between -consumers and servers. Associations contain a shared secret that is -used to sign C{openid.mode=id_res} messages. - -Users of the library should not usually need to interact directly with -associations. The L{store<openid.store>}, -L{server<openid.server.server>} and -L{consumer<openid.consumer.consumer>} objects will create and manage -the associations. The consumer and server code will make use of a -C{L{SessionNegotiator}} when managing associations, which enables -users to express a preference for what kind of associations should be -allowed, and what kind of exchange should be done to establish the -association. - -@var default_negotiator: A C{L{SessionNegotiator}} that allows all - association types that are specified by the OpenID - specification. It prefers to use HMAC-SHA1/DH-SHA1, if it's - available. If HMAC-SHA256 is not supported by your Python runtime, - HMAC-SHA256 and DH-SHA256 will not be available. - -@var encrypted_negotiator: A C{L{SessionNegotiator}} that - does not support C{'no-encryption'} associations. It prefers - HMAC-SHA1/DH-SHA1 association types if available. -""" - -__all__ = [ - 'default_negotiator', - 'encrypted_negotiator', - 'SessionNegotiator', - 'Association', - ] - -import time - -from askbot.deps.openid import cryptutil -from askbot.deps.openid import kvform -from askbot.deps.openid import oidutil -from askbot.deps.openid.message import OPENID_NS - -all_association_types = [ - 'HMAC-SHA1', - 'HMAC-SHA256', - ] - -if hasattr(cryptutil, 'hmacSha256'): - supported_association_types = list(all_association_types) - - default_association_order = [ - ('HMAC-SHA1', 'DH-SHA1'), - ('HMAC-SHA1', 'no-encryption'), - ('HMAC-SHA256', 'DH-SHA256'), - ('HMAC-SHA256', 'no-encryption'), - ] - - only_encrypted_association_order = [ - ('HMAC-SHA1', 'DH-SHA1'), - ('HMAC-SHA256', 'DH-SHA256'), - ] -else: - supported_association_types = ['HMAC-SHA1'] - - default_association_order = [ - ('HMAC-SHA1', 'DH-SHA1'), - ('HMAC-SHA1', 'no-encryption'), - ] - - only_encrypted_association_order = [ - ('HMAC-SHA1', 'DH-SHA1'), - ] - -def getSessionTypes(assoc_type): - """Return the allowed session types for a given association type""" - assoc_to_session = { - 'HMAC-SHA1': ['DH-SHA1', 'no-encryption'], - 'HMAC-SHA256': ['DH-SHA256', 'no-encryption'], - } - return assoc_to_session.get(assoc_type, []) - -def checkSessionType(assoc_type, session_type): - """Check to make sure that this pair of assoc type and session - type are allowed""" - if session_type not in getSessionTypes(assoc_type): - raise ValueError( - 'Session type %r not valid for assocation type %r' - % (session_type, assoc_type)) - -class SessionNegotiator(object): - """A session negotiator controls the allowed and preferred - association types and association session types. Both the - C{L{Consumer<openid.consumer.consumer.Consumer>}} and - C{L{Server<openid.server.server.Server>}} use negotiators when - creating associations. - - You can create and use negotiators if you: - - - Do not want to do Diffie-Hellman key exchange because you use - transport-layer encryption (e.g. SSL) - - - Want to use only SHA-256 associations - - - Do not want to support plain-text associations over a non-secure - channel - - It is up to you to set a policy for what kinds of associations to - accept. By default, the library will make any kind of association - that is allowed in the OpenID 2.0 specification. - - Use of negotiators in the library - ================================= - - When a consumer makes an association request, it calls - C{L{getAllowedType}} to get the preferred association type and - association session type. - - The server gets a request for a particular association/session - type and calls C{L{isAllowed}} to determine if it should - create an association. If it is supported, negotiation is - complete. If it is not, the server calls C{L{getAllowedType}} to - get an allowed association type to return to the consumer. - - If the consumer gets an error response indicating that the - requested association/session type is not supported by the server - that contains an assocation/session type to try, it calls - C{L{isAllowed}} to determine if it should try again with the - given combination of association/session type. - - @ivar allowed_types: A list of association/session types that are - allowed by the server. The order of the pairs in this list - determines preference. If an association/session type comes - earlier in the list, the library is more likely to use that - type. - @type allowed_types: [(str, str)] - """ - - def __init__(self, allowed_types): - self.setAllowedTypes(allowed_types) - - def copy(self): - return self.__class__(list(self.allowed_types)) - - def setAllowedTypes(self, allowed_types): - """Set the allowed association types, checking to make sure - each combination is valid.""" - for (assoc_type, session_type) in allowed_types: - checkSessionType(assoc_type, session_type) - - self.allowed_types = allowed_types - - def addAllowedType(self, assoc_type, session_type=None): - """Add an association type and session type to the allowed - types list. The assocation/session pairs are tried in the - order that they are added.""" - if self.allowed_types is None: - self.allowed_types = [] - - if session_type is None: - available = getSessionTypes(assoc_type) - - if not available: - raise ValueError('No session available for association type %r' - % (assoc_type,)) - - for session_type in getSessionTypes(assoc_type): - self.addAllowedType(assoc_type, session_type) - else: - checkSessionType(assoc_type, session_type) - self.allowed_types.append((assoc_type, session_type)) - - - def isAllowed(self, assoc_type, session_type): - """Is this combination of association type and session type allowed?""" - assoc_good = (assoc_type, session_type) in self.allowed_types - matches = session_type in getSessionTypes(assoc_type) - return assoc_good and matches - - def getAllowedType(self): - """Get a pair of assocation type and session type that are - supported""" - try: - return self.allowed_types[0] - except IndexError: - return (None, None) - -default_negotiator = SessionNegotiator(default_association_order) -encrypted_negotiator = SessionNegotiator(only_encrypted_association_order) - -def getSecretSize(assoc_type): - if assoc_type == 'HMAC-SHA1': - return 20 - elif assoc_type == 'HMAC-SHA256': - return 32 - else: - raise ValueError('Unsupported association type: %r' % (assoc_type,)) - -class Association(object): - """ - This class represents an association between a server and a - consumer. In general, users of this library will never see - instances of this object. The only exception is if you implement - a custom C{L{OpenIDStore<openid.store.interface.OpenIDStore>}}. - - If you do implement such a store, it will need to store the values - of the C{L{handle}}, C{L{secret}}, C{L{issued}}, C{L{lifetime}}, and - C{L{assoc_type}} instance variables. - - @ivar handle: This is the handle the server gave this association. - - @type handle: C{str} - - - @ivar secret: This is the shared secret the server generated for - this association. - - @type secret: C{str} - - - @ivar issued: This is the time this association was issued, in - seconds since 00:00 GMT, January 1, 1970. (ie, a unix - timestamp) - - @type issued: C{int} - - - @ivar lifetime: This is the amount of time this association is - good for, measured in seconds since the association was - issued. - - @type lifetime: C{int} - - - @ivar assoc_type: This is the type of association this instance - represents. The only valid value of this field at this time - is C{'HMAC-SHA1'}, but new types may be defined in the future. - - @type assoc_type: C{str} - - - @sort: __init__, fromExpiresIn, getExpiresIn, __eq__, __ne__, - handle, secret, issued, lifetime, assoc_type - """ - - # The ordering and name of keys as stored by serialize - assoc_keys = [ - 'version', - 'handle', - 'secret', - 'issued', - 'lifetime', - 'assoc_type', - ] - - - _macs = { - 'HMAC-SHA1': cryptutil.hmacSha1, - 'HMAC-SHA256': cryptutil.hmacSha256, - } - - - def fromExpiresIn(cls, expires_in, handle, secret, assoc_type): - """ - This is an alternate constructor used by the OpenID consumer - library to create associations. C{L{OpenIDStore - <openid.store.interface.OpenIDStore>}} implementations - shouldn't use this constructor. - - - @param expires_in: This is the amount of time this association - is good for, measured in seconds since the association was - issued. - - @type expires_in: C{int} - - - @param handle: This is the handle the server gave this - association. - - @type handle: C{str} - - - @param secret: This is the shared secret the server generated - for this association. - - @type secret: C{str} - - - @param assoc_type: This is the type of association this - instance represents. The only valid value of this field - at this time is C{'HMAC-SHA1'}, but new types may be - defined in the future. - - @type assoc_type: C{str} - """ - issued = int(time.time()) - lifetime = expires_in - return cls(handle, secret, issued, lifetime, assoc_type) - - fromExpiresIn = classmethod(fromExpiresIn) - - def __init__(self, handle, secret, issued, lifetime, assoc_type): - """ - This is the standard constructor for creating an association. - - - @param handle: This is the handle the server gave this - association. - - @type handle: C{str} - - - @param secret: This is the shared secret the server generated - for this association. - - @type secret: C{str} - - - @param issued: This is the time this association was issued, - in seconds since 00:00 GMT, January 1, 1970. (ie, a unix - timestamp) - - @type issued: C{int} - - - @param lifetime: This is the amount of time this association - is good for, measured in seconds since the association was - issued. - - @type lifetime: C{int} - - - @param assoc_type: This is the type of association this - instance represents. The only valid value of this field - at this time is C{'HMAC-SHA1'}, but new types may be - defined in the future. - - @type assoc_type: C{str} - """ - if assoc_type not in all_association_types: - fmt = '%r is not a supported association type' - raise ValueError(fmt % (assoc_type,)) - -# secret_size = getSecretSize(assoc_type) -# if len(secret) != secret_size: -# fmt = 'Wrong size secret (%s bytes) for association type %s' -# raise ValueError(fmt % (len(secret), assoc_type)) - - self.handle = handle - self.secret = secret - self.issued = issued - self.lifetime = lifetime - self.assoc_type = assoc_type - - def getExpiresIn(self, now=None): - """ - This returns the number of seconds this association is still - valid for, or C{0} if the association is no longer valid. - - - @return: The number of seconds this association is still valid - for, or C{0} if the association is no longer valid. - - @rtype: C{int} - """ - if now is None: - now = int(time.time()) - - return max(0, self.issued + self.lifetime - now) - - expiresIn = property(getExpiresIn) - - def __eq__(self, other): - """ - This checks to see if two C{L{Association}} instances - represent the same association. - - - @return: C{True} if the two instances represent the same - association, C{False} otherwise. - - @rtype: C{bool} - """ - return type(self) is type(other) and self.__dict__ == other.__dict__ - - def __ne__(self, other): - """ - This checks to see if two C{L{Association}} instances - represent different associations. - - - @return: C{True} if the two instances represent different - associations, C{False} otherwise. - - @rtype: C{bool} - """ - return not (self == other) - - def serialize(self): - """ - Convert an association to KV form. - - @return: String in KV form suitable for deserialization by - deserialize. - - @rtype: str - """ - data = { - 'version':'2', - 'handle':self.handle, - 'secret':oidutil.toBase64(self.secret), - 'issued':str(int(self.issued)), - 'lifetime':str(int(self.lifetime)), - 'assoc_type':self.assoc_type - } - - assert len(data) == len(self.assoc_keys) - pairs = [] - for field_name in self.assoc_keys: - pairs.append((field_name, data[field_name])) - - return kvform.seqToKV(pairs, strict=True) - - def deserialize(cls, assoc_s): - """ - Parse an association as stored by serialize(). - - inverse of serialize - - - @param assoc_s: Association as serialized by serialize() - - @type assoc_s: str - - - @return: instance of this class - """ - pairs = kvform.kvToSeq(assoc_s, strict=True) - keys = [] - values = [] - for k, v in pairs: - keys.append(k) - values.append(v) - - if keys != cls.assoc_keys: - raise ValueError('Unexpected key values: %r', keys) - - version, handle, secret, issued, lifetime, assoc_type = values - if version != '2': - raise ValueError('Unknown version: %r' % version) - issued = int(issued) - lifetime = int(lifetime) - secret = oidutil.fromBase64(secret) - return cls(handle, secret, issued, lifetime, assoc_type) - - deserialize = classmethod(deserialize) - - def sign(self, pairs): - """ - Generate a signature for a sequence of (key, value) pairs - - - @param pairs: The pairs to sign, in order - - @type pairs: sequence of (str, str) - - - @return: The binary signature of this sequence of pairs - - @rtype: str - """ - kv = kvform.seqToKV(pairs) - - try: - mac = self._macs[self.assoc_type] - except KeyError: - raise ValueError( - 'Unknown association type: %r' % (self.assoc_type,)) - - return mac(self.secret, kv) - - - def getMessageSignature(self, message): - """Return the signature of a message. - - If I am not a sign-all association, the message must have a - signed list. - - @return: the signature, base64 encoded - - @rtype: str - - @raises ValueError: If there is no signed list and I am not a sign-all - type of association. - """ - pairs = self._makePairs(message) - return oidutil.toBase64(self.sign(pairs)) - - def signMessage(self, message): - """Add a signature (and a signed list) to a message. - - @return: a new Message object with a signature - @rtype: L{openid.message.Message} - """ - if (message.hasKey(OPENID_NS, 'sig') or - message.hasKey(OPENID_NS, 'signed')): - raise ValueError('Message already has signed list or signature') - - extant_handle = message.getArg(OPENID_NS, 'assoc_handle') - if extant_handle and extant_handle != self.handle: - raise ValueError("Message has a different association handle") - - signed_message = message.copy() - signed_message.setArg(OPENID_NS, 'assoc_handle', self.handle) - message_keys = signed_message.toPostArgs().keys() - signed_list = [k[7:] for k in message_keys - if k.startswith('openid.')] - signed_list.append('signed') - signed_list.sort() - signed_message.setArg(OPENID_NS, 'signed', ','.join(signed_list)) - sig = self.getMessageSignature(signed_message) - signed_message.setArg(OPENID_NS, 'sig', sig) - return signed_message - - def checkMessageSignature(self, message): - """Given a message with a signature, calculate a new signature - and return whether it matches the signature in the message. - - @raises ValueError: if the message has no signature or no signature - can be calculated for it. - """ - message_sig = message.getArg(OPENID_NS, 'sig') - if not message_sig: - raise ValueError("%s has no sig." % (message,)) - calculated_sig = self.getMessageSignature(message) - return calculated_sig == message_sig - - - def _makePairs(self, message): - signed = message.getArg(OPENID_NS, 'signed') - if not signed: - raise ValueError('Message has no signed list: %s' % (message,)) - - signed_list = signed.split(',') - pairs = [] - data = message.toPostArgs() - for field in signed_list: - pairs.append((field, data.get('openid.' + field, ''))) - return pairs - - def __repr__(self): - return "<%s.%s %s %s>" % ( - self.__class__.__module__, - self.__class__.__name__, - self.assoc_type, - self.handle) diff --git a/askbot/deps/openid/consumer/__init__.py b/askbot/deps/openid/consumer/__init__.py deleted file mode 100644 index aab51a29..00000000 --- a/askbot/deps/openid/consumer/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -""" -This package contains the portions of the library used only when -implementing an OpenID consumer. -""" - -__all__ = ['consumer', 'discover'] diff --git a/askbot/deps/openid/consumer/consumer.py b/askbot/deps/openid/consumer/consumer.py deleted file mode 100644 index 64a2278c..00000000 --- a/askbot/deps/openid/consumer/consumer.py +++ /dev/null @@ -1,1900 +0,0 @@ -# -*- test-case-name: openid.test.test_consumer -*- -"""OpenID support for Relying Parties (aka Consumers). - -This module documents the main interface with the OpenID consumer -library. The only part of the library which has to be used and isn't -documented in full here is the store required to create an -C{L{Consumer}} instance. More on the abstract store type and -concrete implementations of it that are provided in the documentation -for the C{L{__init__<Consumer.__init__>}} method of the -C{L{Consumer}} class. - - -OVERVIEW -======== - - The OpenID identity verification process most commonly uses the - following steps, as visible to the user of this library: - - 1. The user enters their OpenID into a field on the consumer's - site, and hits a login button. - - 2. The consumer site discovers the user's OpenID provider using - the Yadis protocol. - - 3. The consumer site sends the browser a redirect to the - OpenID provider. This is the authentication request as - described in the OpenID specification. - - 4. The OpenID provider's site sends the browser a redirect - back to the consumer site. This redirect contains the - provider's response to the authentication request. - - The most important part of the flow to note is the consumer's site - must handle two separate HTTP requests in order to perform the - full identity check. - - -LIBRARY DESIGN -============== - - This consumer library is designed with that flow in mind. The - goal is to make it as easy as possible to perform the above steps - securely. - - At a high level, there are two important parts in the consumer - library. The first important part is this module, which contains - the interface to actually use this library. The second is the - C{L{openid.store.interface}} module, which describes the - interface to use if you need to create a custom method for storing - the state this library needs to maintain between requests. - - In general, the second part is less important for users of the - library to know about, as several implementations are provided - which cover a wide variety of situations in which consumers may - use the library. - - This module contains a class, C{L{Consumer}}, with methods - corresponding to the actions necessary in each of steps 2, 3, and - 4 described in the overview. Use of this library should be as easy - as creating an C{L{Consumer}} instance and calling the methods - appropriate for the action the site wants to take. - - -SESSIONS, STORES, AND STATELESS MODE -==================================== - - The C{L{Consumer}} object keeps track of two types of state: - - 1. State of the user's current authentication attempt. Things like - the identity URL, the list of endpoints discovered for that - URL, and in case where some endpoints are unreachable, the list - of endpoints already tried. This state needs to be held from - Consumer.begin() to Consumer.complete(), but it is only applicable - to a single session with a single user agent, and at the end of - the authentication process (i.e. when an OP replies with either - C{id_res} or C{cancel}) it may be discarded. - - 2. State of relationships with servers, i.e. shared secrets - (associations) with servers and nonces seen on signed messages. - This information should persist from one session to the next and - should not be bound to a particular user-agent. - - - These two types of storage are reflected in the first two arguments of - Consumer's constructor, C{session} and C{store}. C{session} is a - dict-like object and we hope your web framework provides you with one - of these bound to the user agent. C{store} is an instance of - L{openid.store.interface.OpenIDStore}. - - Since the store does hold secrets shared between your application and the - OpenID provider, you should be careful about how you use it in a shared - hosting environment. If the filesystem or database permissions of your - web host allow strangers to read from them, do not store your data there! - If you have no safe place to store your data, construct your consumer - with C{None} for the store, and it will operate only in stateless mode. - Stateless mode may be slower, put more load on the OpenID provider, and - trusts the provider to keep you safe from replay attacks. - - - Several store implementation are provided, and the interface is - fully documented so that custom stores can be used as well. See - the documentation for the C{L{Consumer}} class for more - information on the interface for stores. The implementations that - are provided allow the consumer site to store the necessary data - in several different ways, including several SQL databases and - normal files on disk. - - -IMMEDIATE MODE -============== - - In the flow described above, the user may need to confirm to the - OpenID provider that it's ok to disclose his or her identity. - The provider may draw pages asking for information from the user - before it redirects the browser back to the consumer's site. This - is generally transparent to the consumer site, so it is typically - ignored as an implementation detail. - - There can be times, however, where the consumer site wants to get - a response immediately. When this is the case, the consumer can - put the library in immediate mode. In immediate mode, there is an - extra response possible from the server, which is essentially the - server reporting that it doesn't have enough information to answer - the question yet. - - -USING THIS LIBRARY -================== - - Integrating this library into an application is usually a - relatively straightforward process. The process should basically - follow this plan: - - Add an OpenID login field somewhere on your site. When an OpenID - is entered in that field and the form is submitted, it should make - a request to your site which includes that OpenID URL. - - First, the application should L{instantiate a Consumer<Consumer.__init__>} - with a session for per-user state and store for shared state. - using the store of choice. - - Next, the application should call the 'C{L{begin<Consumer.begin>}}' method on the - C{L{Consumer}} instance. This method takes the OpenID URL. The - C{L{begin<Consumer.begin>}} method returns an C{L{AuthRequest}} - object. - - Next, the application should call the - C{L{redirectURL<AuthRequest.redirectURL>}} method on the - C{L{AuthRequest}} object. The parameter C{return_to} is the URL - that the OpenID server will send the user back to after attempting - to verify his or her identity. The C{realm} parameter is the - URL (or URL pattern) that identifies your web site to the user - when he or she is authorizing it. Send a redirect to the - resulting URL to the user's browser. - - That's the first half of the authentication process. The second - half of the process is done after the user's OpenID Provider sends the - user's browser a redirect back to your site to complete their - login. - - When that happens, the user will contact your site at the URL - given as the C{return_to} URL to the - C{L{redirectURL<AuthRequest.redirectURL>}} call made - above. The request will have several query parameters added to - the URL by the OpenID provider as the information necessary to - finish the request. - - Get a C{L{Consumer}} instance with the same session and store as - before and call its C{L{complete<Consumer.complete>}} method, - passing in all the received query arguments. - - There are multiple possible return types possible from that - method. These indicate whether or not the login was successful, - and include any additional information appropriate for their type. - -@var SUCCESS: constant used as the status for - L{SuccessResponse<openid.consumer.consumer.SuccessResponse>} objects. - -@var FAILURE: constant used as the status for - L{FailureResponse<openid.consumer.consumer.FailureResponse>} objects. - -@var CANCEL: constant used as the status for - L{CancelResponse<openid.consumer.consumer.CancelResponse>} objects. - -@var SETUP_NEEDED: constant used as the status for - L{SetupNeededResponse<openid.consumer.consumer.SetupNeededResponse>} - objects. -""" - -import cgi -import copy -from urlparse import urlparse, urldefrag - -from askbot.deps.openid import fetchers - -from askbot.deps.openid.consumer.discover import discover, OpenIDServiceEndpoint, \ - DiscoveryFailure, OPENID_1_0_TYPE, OPENID_1_1_TYPE, OPENID_2_0_TYPE -from askbot.deps.openid.message import Message, OPENID_NS, OPENID2_NS, OPENID1_NS, \ - IDENTIFIER_SELECT, no_default, BARE_NS -from askbot.deps.openid import cryptutil -from askbot.deps.openid import oidutil -from askbot.deps.openid.association import Association, default_negotiator, \ - SessionNegotiator -from askbot.deps.openid.dh import DiffieHellman -from askbot.deps.openid.store.nonce import mkNonce, split as splitNonce -from askbot.deps.openid.yadis.manager import Discovery -from askbot.deps.openid import urinorm - - -__all__ = ['AuthRequest', 'Consumer', 'SuccessResponse', - 'SetupNeededResponse', 'CancelResponse', 'FailureResponse', - 'SUCCESS', 'FAILURE', 'CANCEL', 'SETUP_NEEDED', - ] - - -def makeKVPost(request_message, server_url): - """Make a Direct Request to an OpenID Provider and return the - result as a Message object. - - @raises openid.fetchers.HTTPFetchingError: if an error is - encountered in making the HTTP post. - - @rtype: L{openid.message.Message} - """ - # XXX: TESTME - resp = fetchers.fetch(server_url, body=request_message.toURLEncoded()) - - # Process response in separate function that can be shared by async code. - return _httpResponseToMessage(resp, server_url) - - -def _httpResponseToMessage(response, server_url): - """Adapt a POST response to a Message. - - @type response: L{openid.fetchers.HTTPResponse} - @param response: Result of a POST to an OpenID endpoint. - - @rtype: L{openid.message.Message} - - @raises openid.fetchers.HTTPFetchingError: if the server returned a - status of other than 200 or 400. - - @raises ServerError: if the server returned an OpenID error. - """ - # Should this function be named Message.fromHTTPResponse instead? - response_message = Message.fromKVForm(response.body) - if response.status == 400: - raise ServerError.fromMessage(response_message) - - elif response.status not in (200, 206): - fmt = 'bad status code from server %s: %s' - error_message = fmt % (server_url, response.status) - raise fetchers.HTTPFetchingError(error_message) - - return response_message - - - -class Consumer(object): - """An OpenID consumer implementation that performs discovery and - does session management. - - @ivar consumer: an instance of an object implementing the OpenID - protocol, but doing no discovery or session management. - - @type consumer: GenericConsumer - - @ivar session: A dictionary-like object representing the user's - session data. This is used for keeping state of the OpenID - transaction when the user is redirected to the server. - - @cvar session_key_prefix: A string that is prepended to session - keys to ensure that they are unique. This variable may be - changed to suit your application. - """ - session_key_prefix = "_openid_consumer_" - - _token = 'last_token' - - _discover = staticmethod(discover) - - def __init__(self, session, store, consumer_class=None): - """Initialize a Consumer instance. - - You should create a new instance of the Consumer object with - every HTTP request that handles OpenID transactions. - - @param session: See L{the session instance variable<openid.consumer.consumer.Consumer.session>} - - @param store: an object that implements the interface in - C{L{openid.store.interface.OpenIDStore}}. Several - implementations are provided, to cover common database - environments. - - @type store: C{L{openid.store.interface.OpenIDStore}} - - @see: L{openid.store.interface} - @see: L{openid.store} - """ - self.session = session - if consumer_class is None: - consumer_class = GenericConsumer - self.consumer = consumer_class(store) - self._token_key = self.session_key_prefix + self._token - - def begin(self, user_url, anonymous=False): - """Start the OpenID authentication process. See steps 1-2 in - the overview at the top of this file. - - @param user_url: Identity URL given by the user. This method - performs a textual transformation of the URL to try and - make sure it is normalized. For example, a user_url of - example.com will be normalized to http://example.com/ - normalizing and resolving any redirects the server might - issue. - - @type user_url: unicode - - @param anonymous: Whether to make an anonymous request of the OpenID - provider. Such a request does not ask for an authorization - assertion for an OpenID identifier, but may be used with - extensions to pass other data. e.g. "I don't care who you are, - but I'd like to know your time zone." - - @type anonymous: bool - - @returns: An object containing the discovered information will - be returned, with a method for building a redirect URL to - the server, as described in step 3 of the overview. This - object may also be used to add extension arguments to the - request, using its - L{addExtensionArg<openid.consumer.consumer.AuthRequest.addExtensionArg>} - method. - - @returntype: L{AuthRequest<openid.consumer.consumer.AuthRequest>} - - @raises openid.consumer.discover.DiscoveryFailure: when I fail to - find an OpenID server for this URL. If the C{yadis} package - is available, L{openid.consumer.discover.DiscoveryFailure} is - an alias for C{yadis.discover.DiscoveryFailure}. - """ - disco = Discovery(self.session, user_url, self.session_key_prefix) - try: - service = disco.getNextService(self._discover) - except fetchers.HTTPFetchingError, why: - raise DiscoveryFailure( - 'Error fetching XRDS document: %s' % (why[0],), None) - - if service is None: - raise DiscoveryFailure( - 'No usable OpenID services found for %s' % (user_url,), None) - else: - return self.beginWithoutDiscovery(service, anonymous) - - def beginWithoutDiscovery(self, service, anonymous=False): - """Start OpenID verification without doing OpenID server - discovery. This method is used internally by Consumer.begin - after discovery is performed, and exists to provide an - interface for library users needing to perform their own - discovery. - - @param service: an OpenID service endpoint descriptor. This - object and factories for it are found in the - L{openid.consumer.discover} module. - - @type service: - L{OpenIDServiceEndpoint<openid.consumer.discover.OpenIDServiceEndpoint>} - - @returns: an OpenID authentication request object. - - @rtype: L{AuthRequest<openid.consumer.consumer.AuthRequest>} - - @See: Openid.consumer.consumer.Consumer.begin - @see: openid.consumer.discover - """ - auth_req = self.consumer.begin(service) - self.session[self._token_key] = auth_req.endpoint - - try: - auth_req.setAnonymous(anonymous) - except ValueError, why: - raise ProtocolError(str(why)) - - return auth_req - - def complete(self, query, current_url): - """Called to interpret the server's response to an OpenID - request. It is called in step 4 of the flow described in the - consumer overview. - - @param query: A dictionary of the query parameters for this - HTTP request. - - @param current_url: The URL used to invoke the application. - Extract the URL from your application's web - request framework and specify it here to have it checked - against the openid.return_to value in the response. If - the return_to URL check fails, the status of the - completion will be FAILURE. - - @returns: a subclass of Response. The type of response is - indicated by the status attribute, which will be one of - SUCCESS, CANCEL, FAILURE, or SETUP_NEEDED. - - @see: L{SuccessResponse<openid.consumer.consumer.SuccessResponse>} - @see: L{CancelResponse<openid.consumer.consumer.CancelResponse>} - @see: L{SetupNeededResponse<openid.consumer.consumer.SetupNeededResponse>} - @see: L{FailureResponse<openid.consumer.consumer.FailureResponse>} - """ - - endpoint = self.session.get(self._token_key) - - message = Message.fromPostArgs(query) - response = self.consumer.complete(message, endpoint, current_url) - - try: - del self.session[self._token_key] - except KeyError: - pass - - if (response.status in ['success', 'cancel'] and - response.identity_url is not None): - - disco = Discovery(self.session, - response.identity_url, - self.session_key_prefix) - # This is OK to do even if we did not do discovery in - # the first place. - disco.cleanup(force=True) - - return response - - def setAssociationPreference(self, association_preferences): - """Set the order in which association types/sessions should be - attempted. For instance, to only allow HMAC-SHA256 - associations created with a DH-SHA256 association session: - - >>> consumer.setAssociationPreference([('HMAC-SHA256', 'DH-SHA256')]) - - Any association type/association type pair that is not in this - list will not be attempted at all. - - @param association_preferences: The list of allowed - (association type, association session type) pairs that - should be allowed for this consumer to use, in order from - most preferred to least preferred. - @type association_preferences: [(str, str)] - - @returns: None - - @see: C{L{openid.association.SessionNegotiator}} - """ - self.consumer.negotiator = SessionNegotiator(association_preferences) - -class DiffieHellmanSHA1ConsumerSession(object): - session_type = 'DH-SHA1' - hash_func = staticmethod(cryptutil.sha1) - secret_size = 20 - allowed_assoc_types = ['HMAC-SHA1'] - - def __init__(self, dh=None): - if dh is None: - dh = DiffieHellman.fromDefaults() - - self.dh = dh - - def getRequest(self): - cpub = cryptutil.longToBase64(self.dh.public) - - args = {'dh_consumer_public': cpub} - - if not self.dh.usingDefaultValues(): - args.update({ - 'dh_modulus': cryptutil.longToBase64(self.dh.modulus), - 'dh_gen': cryptutil.longToBase64(self.dh.generator), - }) - - return args - - def extractSecret(self, response): - dh_server_public64 = response.getArg( - OPENID_NS, 'dh_server_public', no_default) - enc_mac_key64 = response.getArg(OPENID_NS, 'enc_mac_key', no_default) - dh_server_public = cryptutil.base64ToLong(dh_server_public64) - enc_mac_key = oidutil.fromBase64(enc_mac_key64) - return self.dh.xorSecret(dh_server_public, enc_mac_key, self.hash_func) - -class DiffieHellmanSHA256ConsumerSession(DiffieHellmanSHA1ConsumerSession): - session_type = 'DH-SHA256' - hash_func = staticmethod(cryptutil.sha256) - secret_size = 32 - allowed_assoc_types = ['HMAC-SHA256'] - -class PlainTextConsumerSession(object): - session_type = 'no-encryption' - allowed_assoc_types = ['HMAC-SHA1', 'HMAC-SHA256'] - - def getRequest(self): - return {} - - def extractSecret(self, response): - mac_key64 = response.getArg(OPENID_NS, 'mac_key', no_default) - return oidutil.fromBase64(mac_key64) - -class SetupNeededError(Exception): - """Internally-used exception that indicates that an immediate-mode - request cancelled.""" - def __init__(self, user_setup_url=None): - Exception.__init__(self, user_setup_url) - self.user_setup_url = user_setup_url - -class ProtocolError(ValueError): - """Exception that indicates that a message violated the - protocol. It is raised and caught internally to this file.""" - -class TypeURIMismatch(ProtocolError): - """A protocol error arising from type URIs mismatching - """ - - def __init__(self, expected, endpoint): - ProtocolError.__init__(self, expected, endpoint) - self.expected = expected - self.endpoint = endpoint - - def __str__(self): - s = '<%s.%s: Required type %s not found in %s for endpoint %s>' % ( - self.__class__.__module__, self.__class__.__name__, - self.expected, self.endpoint.type_uris, self.endpoint) - return s - - - -class ServerError(Exception): - """Exception that is raised when the server returns a 400 response - code to a direct request.""" - - def __init__(self, error_text, error_code, message): - Exception.__init__(self, error_text) - self.error_text = error_text - self.error_code = error_code - self.message = message - - def fromMessage(cls, message): - """Generate a ServerError instance, extracting the error text - and the error code from the message.""" - error_text = message.getArg( - OPENID_NS, 'error', '<no error message supplied>') - error_code = message.getArg(OPENID_NS, 'error_code') - return cls(error_text, error_code, message) - - fromMessage = classmethod(fromMessage) - -class GenericConsumer(object): - """This is the implementation of the common logic for OpenID - consumers. It is unaware of the application in which it is - running. - - @ivar negotiator: An object that controls the kind of associations - that the consumer makes. It defaults to - C{L{openid.association.default_negotiator}}. Assign a - different negotiator to it if you have specific requirements - for how associations are made. - @type negotiator: C{L{openid.association.SessionNegotiator}} - """ - - # The name of the query parameter that gets added to the return_to - # URL when using OpenID1. You can change this value if you want or - # need a different name, but don't make it start with openid, - # because it's not a standard protocol thing for OpenID1. For - # OpenID2, the library will take care of the nonce using standard - # OpenID query parameter names. - openid1_nonce_query_arg_name = 'janrain_nonce' - - # Another query parameter that gets added to the return_to for - # OpenID 1; if the user's session state is lost, use this claimed - # identifier to do discovery when verifying the response. - openid1_return_to_identifier_name = 'openid1_claimed_id' - - session_types = { - 'DH-SHA1':DiffieHellmanSHA1ConsumerSession, - 'DH-SHA256':DiffieHellmanSHA256ConsumerSession, - 'no-encryption':PlainTextConsumerSession, - } - - _discover = staticmethod(discover) - - def __init__(self, store): - self.store = store - self.negotiator = default_negotiator.copy() - - def begin(self, service_endpoint): - """Create an AuthRequest object for the specified - service_endpoint. This method will create an association if - necessary.""" - if self.store is None: - assoc = None - else: - assoc = self._getAssociation(service_endpoint) - - request = AuthRequest(service_endpoint, assoc) - request.return_to_args[self.openid1_nonce_query_arg_name] = mkNonce() - - if request.message.isOpenID1(): - request.return_to_args[self.openid1_return_to_identifier_name] = \ - request.endpoint.claimed_id - - return request - - def complete(self, message, endpoint, return_to): - """Process the OpenID message, using the specified endpoint - and return_to URL as context. This method will handle any - OpenID message that is sent to the return_to URL. - """ - mode = message.getArg(OPENID_NS, 'mode', '<No mode set>') - - modeMethod = getattr(self, '_complete_' + mode, - self._completeInvalid) - - return modeMethod(message, endpoint, return_to) - - def _complete_cancel(self, message, endpoint, _): - return CancelResponse(endpoint) - - def _complete_error(self, message, endpoint, _): - error = message.getArg(OPENID_NS, 'error') - contact = message.getArg(OPENID_NS, 'contact') - reference = message.getArg(OPENID_NS, 'reference') - - return FailureResponse(endpoint, error, contact=contact, - reference=reference) - - def _complete_setup_needed(self, message, endpoint, _): - if not message.isOpenID2(): - return self._completeInvalid(message, endpoint, _) - - user_setup_url = message.getArg(OPENID2_NS, 'user_setup_url') - return SetupNeededResponse(endpoint, user_setup_url) - - def _complete_id_res(self, message, endpoint, return_to): - try: - self._checkSetupNeeded(message) - except SetupNeededError, why: - return SetupNeededResponse(endpoint, why.user_setup_url) - else: - try: - return self._doIdRes(message, endpoint, return_to) - except (ProtocolError, DiscoveryFailure), why: - return FailureResponse(endpoint, why[0]) - - def _completeInvalid(self, message, endpoint, _): - mode = message.getArg(OPENID_NS, 'mode', '<No mode set>') - return FailureResponse(endpoint, - 'Invalid openid.mode: %r' % (mode,)) - - def _checkReturnTo(self, message, return_to): - """Check an OpenID message and its openid.return_to value - against a return_to URL from an application. Return True on - success, False on failure. - """ - # Check the openid.return_to args against args in the original - # message. - try: - self._verifyReturnToArgs(message.toPostArgs()) - except ProtocolError, why: - oidutil.log("Verifying return_to arguments: %s" % (why[0],)) - return False - - # Check the return_to base URL against the one in the message. - msg_return_to = message.getArg(OPENID_NS, 'return_to') - - # The URL scheme, authority, and path MUST be the same between - # the two URLs. - app_parts = urlparse(urinorm.urinorm(return_to)) - msg_parts = urlparse(urinorm.urinorm(msg_return_to)) - - # (addressing scheme, network location, path) must be equal in - # both URLs. - for part in range(0, 3): - if app_parts[part] != msg_parts[part]: - return False - - return True - - _makeKVPost = staticmethod(makeKVPost) - - def _checkSetupNeeded(self, message): - """Check an id_res message to see if it is a - checkid_immediate cancel response. - - @raises SetupNeededError: if it is a checkid_immediate cancellation - """ - # In OpenID 1, we check to see if this is a cancel from - # immediate mode by the presence of the user_setup_url - # parameter. - if message.isOpenID1(): - user_setup_url = message.getArg(OPENID1_NS, 'user_setup_url') - if user_setup_url is not None: - raise SetupNeededError(user_setup_url) - - def _doIdRes(self, message, endpoint, return_to): - """Handle id_res responses that are not cancellations of - immediate mode requests. - - @param message: the response paramaters. - @param endpoint: the discovered endpoint object. May be None. - - @raises ProtocolError: If the message contents are not - well-formed according to the OpenID specification. This - includes missing fields or not signing fields that should - be signed. - - @raises DiscoveryFailure: If the subject of the id_res message - does not match the supplied endpoint, and discovery on the - identifier in the message fails (this should only happen - when using OpenID 2) - - @returntype: L{Response} - """ - # Checks for presence of appropriate fields (and checks - # signed list fields) - self._idResCheckForFields(message) - - if not self._checkReturnTo(message, return_to): - raise ProtocolError( - "return_to does not match return URL. Expected %r, got %r" - % (return_to, message.getArg(OPENID_NS, 'return_to'))) - - - # Verify discovery information: - endpoint = self._verifyDiscoveryResults(message, endpoint) - oidutil.log("Received id_res response from %s using association %s" % - (endpoint.server_url, - message.getArg(OPENID_NS, 'assoc_handle'))) - - self._idResCheckSignature(message, endpoint.server_url) - - # Will raise a ProtocolError if the nonce is bad - self._idResCheckNonce(message, endpoint) - - signed_list_str = message.getArg(OPENID_NS, 'signed', no_default) - signed_list = signed_list_str.split(',') - signed_fields = ["openid." + s for s in signed_list] - return SuccessResponse(endpoint, message, signed_fields) - - def _idResGetNonceOpenID1(self, message, endpoint): - """Extract the nonce from an OpenID 1 response. Return the - nonce from the BARE_NS since we independently check the - return_to arguments are the same as those in the response - message. - - See the openid1_nonce_query_arg_name class variable - - @returns: The nonce as a string or None - """ - return message.getArg(BARE_NS, self.openid1_nonce_query_arg_name) - - def _idResCheckNonce(self, message, endpoint): - if message.isOpenID1(): - # This indicates that the nonce was generated by the consumer - nonce = self._idResGetNonceOpenID1(message, endpoint) - server_url = '' - else: - nonce = message.getArg(OPENID2_NS, 'response_nonce') - server_url = endpoint.server_url - - if nonce is None: - raise ProtocolError('Nonce missing from response') - - try: - timestamp, salt = splitNonce(nonce) - except ValueError, why: - raise ProtocolError('Malformed nonce: %s' % (why[0],)) - - if (self.store is not None and - not self.store.useNonce(server_url, timestamp, salt)): - raise ProtocolError('Nonce already used or out of range') - - def _idResCheckSignature(self, message, server_url): - assoc_handle = message.getArg(OPENID_NS, 'assoc_handle') - if self.store is None: - assoc = None - else: - assoc = self.store.getAssociation(server_url, assoc_handle) - - if assoc: - if assoc.getExpiresIn() <= 0: - # XXX: It might be a good idea sometimes to re-start the - # authentication with a new association. Doing it - # automatically opens the possibility for - # denial-of-service by a server that just returns expired - # associations (or really short-lived associations) - raise ProtocolError( - 'Association with %s expired' % (server_url,)) - - if not assoc.checkMessageSignature(message): - raise ProtocolError('Bad signature') - - else: - # It's not an association we know about. Stateless mode is our - # only possible path for recovery. - # XXX - async framework will not want to block on this call to - # _checkAuth. - if not self._checkAuth(message, server_url): - raise ProtocolError('Server denied check_authentication') - - def _idResCheckForFields(self, message): - # XXX: this should be handled by the code that processes the - # response (that is, if a field is missing, we should not have - # to explicitly check that it's present, just make sure that - # the fields are actually being used by the rest of the code - # in tests). Although, which fields are signed does need to be - # checked somewhere. - basic_fields = ['return_to', 'assoc_handle', 'sig', 'signed'] - basic_sig_fields = ['return_to', 'identity'] - - require_fields = { - OPENID2_NS: basic_fields + ['op_endpoint'], - OPENID1_NS: basic_fields + ['identity'], - } - - require_sigs = { - OPENID2_NS: basic_sig_fields + ['response_nonce', - 'claimed_id', - 'assoc_handle', - 'op_endpoint',], - OPENID1_NS: basic_sig_fields, - } - - for field in require_fields[message.getOpenIDNamespace()]: - if not message.hasKey(OPENID_NS, field): - raise ProtocolError('Missing required field %r' % (field,)) - - signed_list_str = message.getArg(OPENID_NS, 'signed', no_default) - signed_list = signed_list_str.split(',') - - for field in require_sigs[message.getOpenIDNamespace()]: - # Field is present and not in signed list - if message.hasKey(OPENID_NS, field) and field not in signed_list: - raise ProtocolError('"%s" not signed' % (field,)) - - - def _verifyReturnToArgs(query): - """Verify that the arguments in the return_to URL are present in this - response. - """ - message = Message.fromPostArgs(query) - return_to = message.getArg(OPENID_NS, 'return_to') - - if return_to is None: - raise ProtocolError('Response has no return_to') - - parsed_url = urlparse(return_to) - rt_query = parsed_url[4] - parsed_args = cgi.parse_qsl(rt_query) - - for rt_key, rt_value in parsed_args: - try: - value = query[rt_key] - if rt_value != value: - format = ("parameter %s value %r does not match " - "return_to's value %r") - raise ProtocolError(format % (rt_key, value, rt_value)) - except KeyError: - format = "return_to parameter %s absent from query %r" - raise ProtocolError(format % (rt_key, query)) - - # Make sure all non-OpenID arguments in the response are also - # in the signed return_to. - bare_args = message.getArgs(BARE_NS) - for pair in bare_args.iteritems(): - if pair not in parsed_args: - raise ProtocolError("Parameter %s not in return_to URL" % (pair[0],)) - - _verifyReturnToArgs = staticmethod(_verifyReturnToArgs) - - def _verifyDiscoveryResults(self, resp_msg, endpoint=None): - """ - Extract the information from an OpenID assertion message and - verify it against the original - - @param endpoint: The endpoint that resulted from doing discovery - @param resp_msg: The id_res message object - - @returns: the verified endpoint - """ - if resp_msg.getOpenIDNamespace() == OPENID2_NS: - return self._verifyDiscoveryResultsOpenID2(resp_msg, endpoint) - else: - return self._verifyDiscoveryResultsOpenID1(resp_msg, endpoint) - - - def _verifyDiscoveryResultsOpenID2(self, resp_msg, endpoint): - to_match = OpenIDServiceEndpoint() - to_match.type_uris = [OPENID_2_0_TYPE] - to_match.claimed_id = resp_msg.getArg(OPENID2_NS, 'claimed_id') - to_match.local_id = resp_msg.getArg(OPENID2_NS, 'identity') - - # Raises a KeyError when the op_endpoint is not present - to_match.server_url = resp_msg.getArg( - OPENID2_NS, 'op_endpoint', no_default) - - # claimed_id and identifier must both be present or both - # be absent - if (to_match.claimed_id is None and - to_match.local_id is not None): - raise ProtocolError( - 'openid.identity is present without openid.claimed_id') - - elif (to_match.claimed_id is not None and - to_match.local_id is None): - raise ProtocolError( - 'openid.claimed_id is present without openid.identity') - - # This is a response without identifiers, so there's really no - # checking that we can do, so return an endpoint that's for - # the specified `openid.op_endpoint' - elif to_match.claimed_id is None: - return OpenIDServiceEndpoint.fromOPEndpointURL(to_match.server_url) - - # The claimed ID doesn't match, so we have to do discovery - # again. This covers not using sessions, OP identifier - # endpoints and responses that didn't match the original - # request. - if not endpoint: - oidutil.log('No pre-discovered information supplied.') - endpoint = self._discoverAndVerify(to_match.claimed_id, [to_match]) - else: - # The claimed ID matches, so we use the endpoint that we - # discovered in initiation. This should be the most common - # case. - try: - self._verifyDiscoverySingle(endpoint, to_match) - except ProtocolError, e: - oidutil.log( - "Error attempting to use stored discovery information: " + - str(e)) - oidutil.log("Attempting discovery to verify endpoint") - endpoint = self._discoverAndVerify( - to_match.claimed_id, [to_match]) - - # The endpoint we return should have the claimed ID from the - # message we just verified, fragment and all. - if endpoint.claimed_id != to_match.claimed_id: - endpoint = copy.copy(endpoint) - endpoint.claimed_id = to_match.claimed_id - return endpoint - - def _verifyDiscoveryResultsOpenID1(self, resp_msg, endpoint): - claimed_id = resp_msg.getArg(BARE_NS, self.openid1_return_to_identifier_name) - - if endpoint is None and claimed_id is None: - raise RuntimeError( - 'When using OpenID 1, the claimed ID must be supplied, ' - 'either by passing it through as a return_to parameter ' - 'or by using a session, and supplied to the GenericConsumer ' - 'as the argument to complete()') - elif endpoint is not None and claimed_id is None: - claimed_id = endpoint.claimed_id - - to_match = OpenIDServiceEndpoint() - to_match.type_uris = [OPENID_1_1_TYPE] - to_match.local_id = resp_msg.getArg(OPENID1_NS, 'identity') - # Restore delegate information from the initiation phase - to_match.claimed_id = claimed_id - - if to_match.local_id is None: - raise ProtocolError('Missing required field openid.identity') - - to_match_1_0 = copy.copy(to_match) - to_match_1_0.type_uris = [OPENID_1_0_TYPE] - - if endpoint is not None: - try: - try: - self._verifyDiscoverySingle(endpoint, to_match) - except TypeURIMismatch: - self._verifyDiscoverySingle(endpoint, to_match_1_0) - except ProtocolError, e: - oidutil.log("Error attempting to use stored discovery information: " + - str(e)) - oidutil.log("Attempting discovery to verify endpoint") - else: - return endpoint - - # Endpoint is either bad (failed verification) or None - return self._discoverAndVerify(claimed_id, [to_match, to_match_1_0]) - - def _verifyDiscoverySingle(self, endpoint, to_match): - """Verify that the given endpoint matches the information - extracted from the OpenID assertion, and raise an exception if - there is a mismatch. - - @type endpoint: openid.consumer.discover.OpenIDServiceEndpoint - @type to_match: openid.consumer.discover.OpenIDServiceEndpoint - - @rtype: NoneType - - @raises ProtocolError: when the endpoint does not match the - discovered information. - """ - # Every type URI that's in the to_match endpoint has to be - # present in the discovered endpoint. - for type_uri in to_match.type_uris: - if not endpoint.usesExtension(type_uri): - raise TypeURIMismatch(type_uri, endpoint) - - # Fragments do not influence discovery, so we can't compare a - # claimed identifier with a fragment to discovered information. - defragged_claimed_id, _ = urldefrag(to_match.claimed_id) - if defragged_claimed_id != endpoint.claimed_id: - raise ProtocolError( - 'Claimed ID does not match (different subjects!), ' - 'Expected %s, got %s' % - (defragged_claimed_id, endpoint.claimed_id)) - - if to_match.getLocalID() != endpoint.getLocalID(): - raise ProtocolError('local_id mismatch. Expected %s, got %s' % - (to_match.getLocalID(), endpoint.getLocalID())) - - # If the server URL is None, this must be an OpenID 1 - # response, because op_endpoint is a required parameter in - # OpenID 2. In that case, we don't actually care what the - # discovered server_url is, because signature checking or - # check_auth should take care of that check for us. - if to_match.server_url is None: - assert to_match.preferredNamespace() == OPENID1_NS, ( - """The code calling this must ensure that OpenID 2 - responses have a non-none `openid.op_endpoint' and - that it is set as the `server_url' attribute of the - `to_match' endpoint.""") - - elif to_match.server_url != endpoint.server_url: - raise ProtocolError('OP Endpoint mismatch. Expected %s, got %s' % - (to_match.server_url, endpoint.server_url)) - - def _discoverAndVerify(self, claimed_id, to_match_endpoints): - """Given an endpoint object created from the information in an - OpenID response, perform discovery and verify the discovery - results, returning the matching endpoint that is the result of - doing that discovery. - - @type to_match: openid.consumer.discover.OpenIDServiceEndpoint - @param to_match: The endpoint whose information we're confirming - - @rtype: openid.consumer.discover.OpenIDServiceEndpoint - @returns: The result of performing discovery on the claimed - identifier in `to_match' - - @raises DiscoveryFailure: when discovery fails. - """ - oidutil.log('Performing discovery on %s' % (claimed_id,)) - _, services = self._discover(claimed_id) - if not services: - raise DiscoveryFailure('No OpenID information found at %s' % - (claimed_id,), None) - return self._verifyDiscoveredServices(claimed_id, services, - to_match_endpoints) - - - def _verifyDiscoveredServices(self, claimed_id, services, to_match_endpoints): - """See @L{_discoverAndVerify}""" - - # Search the services resulting from discovery to find one - # that matches the information from the assertion - failure_messages = [] - for endpoint in services: - for to_match_endpoint in to_match_endpoints: - try: - self._verifyDiscoverySingle( - endpoint, to_match_endpoint) - except ProtocolError, why: - failure_messages.append(str(why)) - else: - # It matches, so discover verification has - # succeeded. Return this endpoint. - return endpoint - else: - oidutil.log('Discovery verification failure for %s' % - (claimed_id,)) - for failure_message in failure_messages: - oidutil.log(' * Endpoint mismatch: ' + failure_message) - - raise DiscoveryFailure( - 'No matching endpoint found after discovering %s' - % (claimed_id,), None) - - def _checkAuth(self, message, server_url): - """Make a check_authentication request to verify this message. - - @returns: True if the request is valid. - @rtype: bool - """ - oidutil.log('Using OpenID check_authentication') - request = self._createCheckAuthRequest(message) - if request is None: - return False - try: - response = self._makeKVPost(request, server_url) - except (fetchers.HTTPFetchingError, ServerError), e: - oidutil.log('check_authentication failed: %s' % (e[0],)) - return False - else: - return self._processCheckAuthResponse(response, server_url) - - def _createCheckAuthRequest(self, message): - """Generate a check_authentication request message given an - id_res message. - """ - signed = message.getArg(OPENID_NS, 'signed') - if signed: - for k in signed.split(','): - oidutil.log(k) - val = message.getAliasedArg(k) - - # Signed value is missing - if val is None: - oidutil.log('Missing signed field %r' % (k,)) - return None - - check_auth_message = message.copy() - check_auth_message.setArg(OPENID_NS, 'mode', 'check_authentication') - return check_auth_message - - def _processCheckAuthResponse(self, response, server_url): - """Process the response message from a check_authentication - request, invalidating associations if requested. - """ - is_valid = response.getArg(OPENID_NS, 'is_valid', 'false') - - invalidate_handle = response.getArg(OPENID_NS, 'invalidate_handle') - if invalidate_handle is not None: - oidutil.log( - 'Received "invalidate_handle" from server %s' % (server_url,)) - if self.store is None: - oidutil.log('Unexpectedly got invalidate_handle without ' - 'a store!') - else: - self.store.removeAssociation(server_url, invalidate_handle) - - if is_valid == 'true': - return True - else: - oidutil.log('Server responds that checkAuth call is not valid') - return False - - def _getAssociation(self, endpoint): - """Get an association for the endpoint's server_url. - - First try seeing if we have a good association in the - store. If we do not, then attempt to negotiate an association - with the server. - - If we negotiate a good association, it will get stored. - - @returns: A valid association for the endpoint's server_url or None - @rtype: openid.association.Association or NoneType - """ - assoc = self.store.getAssociation(endpoint.server_url) - - if assoc is None or assoc.expiresIn <= 0: - assoc = self._negotiateAssociation(endpoint) - if assoc is not None: - self.store.storeAssociation(endpoint.server_url, assoc) - - return assoc - - def _negotiateAssociation(self, endpoint): - """Make association requests to the server, attempting to - create a new association. - - @returns: a new association object - - @rtype: L{openid.association.Association} - """ - # Get our preferred session/association type from the negotiatior. - assoc_type, session_type = self.negotiator.getAllowedType() - - try: - assoc = self._requestAssociation( - endpoint, assoc_type, session_type) - except ServerError, why: - supportedTypes = self._extractSupportedAssociationType(why, - endpoint, - assoc_type) - if supportedTypes is not None: - assoc_type, session_type = supportedTypes - # Attempt to create an association from the assoc_type - # and session_type that the server told us it - # supported. - try: - assoc = self._requestAssociation( - endpoint, assoc_type, session_type) - except ServerError, why: - # Do not keep trying, since it rejected the - # association type that it told us to use. - oidutil.log('Server %s refused its suggested association ' - 'type: session_type=%s, assoc_type=%s' - % (endpoint.server_url, session_type, - assoc_type)) - return None - else: - return assoc - else: - return assoc - - def _extractSupportedAssociationType(self, server_error, endpoint, - assoc_type): - """Handle ServerErrors resulting from association requests. - - @returns: If server replied with an C{unsupported-type} error, - return a tuple of supported C{association_type}, C{session_type}. - Otherwise logs the error and returns None. - @rtype: tuple or None - """ - # Any error message whose code is not 'unsupported-type' - # should be considered a total failure. - if server_error.error_code != 'unsupported-type' or \ - server_error.message.isOpenID1(): - oidutil.log( - 'Server error when requesting an association from %r: %s' - % (endpoint.server_url, server_error.error_text)) - return None - - # The server didn't like the association/session type - # that we sent, and it sent us back a message that - # might tell us how to handle it. - oidutil.log( - 'Unsupported association type %s: %s' % (assoc_type, - server_error.error_text,)) - - # Extract the session_type and assoc_type from the - # error message - assoc_type = server_error.message.getArg(OPENID_NS, 'assoc_type') - session_type = server_error.message.getArg(OPENID_NS, 'session_type') - - if assoc_type is None or session_type is None: - oidutil.log('Server responded with unsupported association ' - 'session but did not supply a fallback.') - return None - elif not self.negotiator.isAllowed(assoc_type, session_type): - fmt = ('Server sent unsupported session/association type: ' - 'session_type=%s, assoc_type=%s') - oidutil.log(fmt % (session_type, assoc_type)) - return None - else: - return assoc_type, session_type - - - def _requestAssociation(self, endpoint, assoc_type, session_type): - """Make and process one association request to this endpoint's - OP endpoint URL. - - @returns: An association object or None if the association - processing failed. - - @raises ServerError: when the remote OpenID server returns an error. - """ - assoc_session, args = self._createAssociateRequest( - endpoint, assoc_type, session_type) - - try: - response = self._makeKVPost(args, endpoint.server_url) - except fetchers.HTTPFetchingError, why: - oidutil.log('openid.associate request failed: %s' % (why[0],)) - return None - - try: - assoc = self._extractAssociation(response, assoc_session) - except KeyError, why: - oidutil.log('Missing required parameter in response from %s: %s' - % (endpoint.server_url, why[0])) - return None - except ProtocolError, why: - oidutil.log('Protocol error parsing response from %s: %s' % ( - endpoint.server_url, why[0])) - return None - else: - return assoc - - def _createAssociateRequest(self, endpoint, assoc_type, session_type): - """Create an association request for the given assoc_type and - session_type. - - @param endpoint: The endpoint whose server_url will be - queried. The important bit about the endpoint is whether - it's in compatiblity mode (OpenID 1.1) - - @param assoc_type: The association type that the request - should ask for. - @type assoc_type: str - - @param session_type: The session type that should be used in - the association request. The session_type is used to - create an association session object, and that session - object is asked for any additional fields that it needs to - add to the request. - @type session_type: str - - @returns: a pair of the association session object and the - request message that will be sent to the server. - @rtype: (association session type (depends on session_type), - openid.message.Message) - """ - session_type_class = self.session_types[session_type] - assoc_session = session_type_class() - - args = { - 'mode': 'associate', - 'assoc_type': assoc_type, - } - - if not endpoint.compatibilityMode(): - args['ns'] = OPENID2_NS - - # Leave out the session type if we're in compatibility mode - # *and* it's no-encryption. - if (not endpoint.compatibilityMode() or - assoc_session.session_type != 'no-encryption'): - args['session_type'] = assoc_session.session_type - - args.update(assoc_session.getRequest()) - message = Message.fromOpenIDArgs(args) - return assoc_session, message - - def _getOpenID1SessionType(self, assoc_response): - """Given an association response message, extract the OpenID - 1.X session type. - - This function mostly takes care of the 'no-encryption' default - behavior in OpenID 1. - - If the association type is plain-text, this function will - return 'no-encryption' - - @returns: The association type for this message - @rtype: str - - @raises KeyError: when the session_type field is absent. - """ - # If it's an OpenID 1 message, allow session_type to default - # to None (which signifies "no-encryption") - session_type = assoc_response.getArg(OPENID1_NS, 'session_type') - - # Handle the differences between no-encryption association - # respones in OpenID 1 and 2: - - # no-encryption is not really a valid session type for - # OpenID 1, but we'll accept it anyway, while issuing a - # warning. - if session_type == 'no-encryption': - oidutil.log('WARNING: OpenID server sent "no-encryption"' - 'for OpenID 1.X') - - # Missing or empty session type is the way to flag a - # 'no-encryption' response. Change the session type to - # 'no-encryption' so that it can be handled in the same - # way as OpenID 2 'no-encryption' respones. - elif session_type == '' or session_type is None: - session_type = 'no-encryption' - - return session_type - - def _extractAssociation(self, assoc_response, assoc_session): - """Attempt to extract an association from the response, given - the association response message and the established - association session. - - @param assoc_response: The association response message from - the server - @type assoc_response: openid.message.Message - - @param assoc_session: The association session object that was - used when making the request - @type assoc_session: depends on the session type of the request - - @raises ProtocolError: when data is malformed - @raises KeyError: when a field is missing - - @rtype: openid.association.Association - """ - # Extract the common fields from the response, raising an - # exception if they are not found - assoc_type = assoc_response.getArg( - OPENID_NS, 'assoc_type', no_default) - assoc_handle = assoc_response.getArg( - OPENID_NS, 'assoc_handle', no_default) - - # expires_in is a base-10 string. The Python parsing will - # accept literals that have whitespace around them and will - # accept negative values. Neither of these are really in-spec, - # but we think it's OK to accept them. - expires_in_str = assoc_response.getArg( - OPENID_NS, 'expires_in', no_default) - try: - expires_in = int(expires_in_str) - except ValueError, why: - raise ProtocolError('Invalid expires_in field: %s' % (why[0],)) - - # OpenID 1 has funny association session behaviour. - if assoc_response.isOpenID1(): - session_type = self._getOpenID1SessionType(assoc_response) - else: - session_type = assoc_response.getArg( - OPENID2_NS, 'session_type', no_default) - - # Session type mismatch - if assoc_session.session_type != session_type: - if (assoc_response.isOpenID1() and - session_type == 'no-encryption'): - # In OpenID 1, any association request can result in a - # 'no-encryption' association response. Setting - # assoc_session to a new no-encryption session should - # make the rest of this function work properly for - # that case. - assoc_session = PlainTextConsumerSession() - else: - # Any other mismatch, regardless of protocol version - # results in the failure of the association session - # altogether. - fmt = 'Session type mismatch. Expected %r, got %r' - message = fmt % (assoc_session.session_type, session_type) - raise ProtocolError(message) - - # Make sure assoc_type is valid for session_type - if assoc_type not in assoc_session.allowed_assoc_types: - fmt = 'Unsupported assoc_type for session %s returned: %s' - raise ProtocolError(fmt % (assoc_session.session_type, assoc_type)) - - # Delegate to the association session to extract the secret - # from the response, however is appropriate for that session - # type. - try: - secret = assoc_session.extractSecret(assoc_response) - except ValueError, why: - fmt = 'Malformed response for %s session: %s' - raise ProtocolError(fmt % (assoc_session.session_type, why[0])) - - return Association.fromExpiresIn( - expires_in, assoc_handle, secret, assoc_type) - -class AuthRequest(object): - """An object that holds the state necessary for generating an - OpenID authentication request. This object holds the association - with the server and the discovered information with which the - request will be made. - - It is separate from the consumer because you may wish to add - things to the request before sending it on its way to the - server. It also has serialization options that let you encode the - authentication request as a URL or as a form POST. - """ - - def __init__(self, endpoint, assoc): - """ - Creates a new AuthRequest object. This just stores each - argument in an appropriately named field. - - Users of this library should not create instances of this - class. Instances of this class are created by the library - when needed. - """ - self.assoc = assoc - self.endpoint = endpoint - self.return_to_args = {} - self.message = Message(endpoint.preferredNamespace()) - self._anonymous = False - - def setAnonymous(self, is_anonymous): - """Set whether this request should be made anonymously. If a - request is anonymous, the identifier will not be sent in the - request. This is only useful if you are making another kind of - request with an extension in this request. - - Anonymous requests are not allowed when the request is made - with OpenID 1. - - @raises ValueError: when attempting to set an OpenID1 request - as anonymous - """ - if is_anonymous and self.message.isOpenID1(): - raise ValueError('OpenID 1 requests MUST include the ' - 'identifier in the request') - else: - self._anonymous = is_anonymous - - def addExtension(self, extension_request): - """Add an extension to this checkid request. - - @param extension_request: An object that implements the - extension interface for adding arguments to an OpenID - message. - """ - extension_request.toMessage(self.message) - - def addExtensionArg(self, namespace, key, value): - """Add an extension argument to this OpenID authentication - request. - - Use caution when adding arguments, because they will be - URL-escaped and appended to the redirect URL, which can easily - get quite long. - - @param namespace: The namespace for the extension. For - example, the simple registration extension uses the - namespace C{sreg}. - - @type namespace: str - - @param key: The key within the extension namespace. For - example, the nickname field in the simple registration - extension's key is C{nickname}. - - @type key: str - - @param value: The value to provide to the server for this - argument. - - @type value: str - """ - self.message.setArg(namespace, key, value) - - def getMessage(self, realm, return_to=None, immediate=False): - """Produce a L{openid.message.Message} representing this request. - - @param realm: The URL (or URL pattern) that identifies your - web site to the user when she is authorizing it. - - @type realm: str - - @param return_to: The URL that the OpenID provider will send the - user back to after attempting to verify her identity. - - Not specifying a return_to URL means that the user will not - be returned to the site issuing the request upon its - completion. - - @type return_to: str - - @param immediate: If True, the OpenID provider is to send back - a response immediately, useful for behind-the-scenes - authentication attempts. Otherwise the OpenID provider - may engage the user before providing a response. This is - the default case, as the user may need to provide - credentials or approve the request before a positive - response can be sent. - - @type immediate: bool - - @returntype: L{openid.message.Message} - """ - if return_to: - return_to = oidutil.appendArgs(return_to, self.return_to_args) - elif immediate: - raise ValueError( - '"return_to" is mandatory when using "checkid_immediate"') - elif self.message.isOpenID1(): - raise ValueError('"return_to" is mandatory for OpenID 1 requests') - elif self.return_to_args: - raise ValueError('extra "return_to" arguments were specified, ' - 'but no return_to was specified') - - if immediate: - mode = 'checkid_immediate' - else: - mode = 'checkid_setup' - - message = self.message.copy() - if message.isOpenID1(): - realm_key = 'trust_root' - else: - realm_key = 'realm' - - message.updateArgs(OPENID_NS, - { - realm_key:realm, - 'mode':mode, - 'return_to':return_to, - }) - - if not self._anonymous: - if self.endpoint.isOPIdentifier(): - # This will never happen when we're in compatibility - # mode, as long as isOPIdentifier() returns False - # whenever preferredNamespace() returns OPENID1_NS. - claimed_id = request_identity = IDENTIFIER_SELECT - else: - request_identity = self.endpoint.getLocalID() - claimed_id = self.endpoint.claimed_id - - # This is true for both OpenID 1 and 2 - message.setArg(OPENID_NS, 'identity', request_identity) - - if message.isOpenID2(): - message.setArg(OPENID2_NS, 'claimed_id', claimed_id) - - if self.assoc: - message.setArg(OPENID_NS, 'assoc_handle', self.assoc.handle) - assoc_log_msg = 'with assocication %s' % (self.assoc.handle,) - else: - assoc_log_msg = 'using stateless mode.' - - oidutil.log("Generated %s request to %s %s" % - (mode, self.endpoint.server_url, assoc_log_msg)) - - return message - - def redirectURL(self, realm, return_to=None, immediate=False): - """Returns a URL with an encoded OpenID request. - - The resulting URL is the OpenID provider's endpoint URL with - parameters appended as query arguments. You should redirect - the user agent to this URL. - - OpenID 2.0 endpoints also accept POST requests, see - C{L{shouldSendRedirect}} and C{L{formMarkup}}. - - @param realm: The URL (or URL pattern) that identifies your - web site to the user when she is authorizing it. - - @type realm: str - - @param return_to: The URL that the OpenID provider will send the - user back to after attempting to verify her identity. - - Not specifying a return_to URL means that the user will not - be returned to the site issuing the request upon its - completion. - - @type return_to: str - - @param immediate: If True, the OpenID provider is to send back - a response immediately, useful for behind-the-scenes - authentication attempts. Otherwise the OpenID provider - may engage the user before providing a response. This is - the default case, as the user may need to provide - credentials or approve the request before a positive - response can be sent. - - @type immediate: bool - - @returns: The URL to redirect the user agent to. - - @returntype: str - """ - message = self.getMessage(realm, return_to, immediate) - return message.toURL(self.endpoint.server_url) - - def formMarkup(self, realm, return_to=None, immediate=False, - form_tag_attrs=None): - """Get html for a form to submit this request to the IDP. - - @param form_tag_attrs: Dictionary of attributes to be added to - the form tag. 'accept-charset' and 'enctype' have defaults - that can be overridden. If a value is supplied for - 'action' or 'method', it will be replaced. - @type form_tag_attrs: {unicode: unicode} - """ - message = self.getMessage(realm, return_to, immediate) - return message.toFormMarkup(self.endpoint.server_url, - form_tag_attrs) - - def htmlMarkup(self, realm, return_to=None, immediate=False, - form_tag_attrs=None): - """Get an autosubmitting HTML page that submits this request to the - IDP. This is just a wrapper for formMarkup. - - @see: formMarkup - - @returns: str - """ - return oidutil.autoSubmitHTML(self.formMarkup(realm, - return_to, - immediate, - form_tag_attrs)) - - def shouldSendRedirect(self): - """Should this OpenID authentication request be sent as a HTTP - redirect or as a POST (form submission)? - - @rtype: bool - """ - return self.endpoint.compatibilityMode() - -FAILURE = 'failure' -SUCCESS = 'success' -CANCEL = 'cancel' -SETUP_NEEDED = 'setup_needed' - -class Response(object): - status = None - - def setEndpoint(self, endpoint): - self.endpoint = endpoint - if endpoint is None: - self.identity_url = None - else: - self.identity_url = endpoint.claimed_id - - def getDisplayIdentifier(self): - """Return the display identifier for this response. - - The display identifier is related to the Claimed Identifier, but the - two are not always identical. The display identifier is something the - user should recognize as what they entered, whereas the response's - claimed identifier (in the L{identity_url} attribute) may have extra - information for better persistence. - - URLs will be stripped of their fragments for display. XRIs will - display the human-readable identifier (i-name) instead of the - persistent identifier (i-number). - - Use the display identifier in your user interface. Use - L{identity_url} for querying your database or authorization server. - """ - if self.endpoint is not None: - return self.endpoint.getDisplayIdentifier() - return None - -class SuccessResponse(Response): - """A response with a status of SUCCESS. Indicates that this request is a - successful acknowledgement from the OpenID server that the - supplied URL is, indeed controlled by the requesting agent. - - @ivar identity_url: The identity URL that has been authenticated; the Claimed Identifier. - See also L{getDisplayIdentifier}. - - @ivar endpoint: The endpoint that authenticated the identifier. You - may access other discovered information related to this endpoint, - such as the CanonicalID of an XRI, through this object. - @type endpoint: L{OpenIDServiceEndpoint<openid.consumer.discover.OpenIDServiceEndpoint>} - - @ivar signed_fields: The arguments in the server's response that - were signed and verified. - - @cvar status: SUCCESS - """ - - status = SUCCESS - - def __init__(self, endpoint, message, signed_fields=None): - # Don't use setEndpoint, because endpoint should never be None - # for a successfull transaction. - self.endpoint = endpoint - self.identity_url = endpoint.claimed_id - - self.message = message - - if signed_fields is None: - signed_fields = [] - self.signed_fields = signed_fields - - def isOpenID1(self): - """Was this authentication response an OpenID 1 authentication - response? - """ - return self.message.isOpenID1() - - def isSigned(self, ns_uri, ns_key): - """Return whether a particular key is signed, regardless of - its namespace alias - """ - return self.message.getKey(ns_uri, ns_key) in self.signed_fields - - def getSigned(self, ns_uri, ns_key, default=None): - """Return the specified signed field if available, - otherwise return default - """ - if self.isSigned(ns_uri, ns_key): - return self.message.getArg(ns_uri, ns_key, default) - else: - return default - - def getSignedNS(self, ns_uri): - """Get signed arguments from the response message. Return a - dict of all arguments in the specified namespace. If any of - the arguments are not signed, return None. - """ - msg_args = self.message.getArgs(ns_uri) - - for key in msg_args.iterkeys(): - if not self.isSigned(ns_uri, key): - oidutil.log("SuccessResponse.getSignedNS: (%s, %s) not signed." - % (ns_uri, key)) - return None - - return msg_args - - def extensionResponse(self, namespace_uri, require_signed): - """Return response arguments in the specified namespace. - - @param namespace_uri: The namespace URI of the arguments to be - returned. - - @param require_signed: True if the arguments should be among - those signed in the response, False if you don't care. - - If require_signed is True and the arguments are not signed, - return None. - """ - if require_signed: - return self.getSignedNS(namespace_uri) - else: - return self.message.getArgs(namespace_uri) - - def getReturnTo(self): - """Get the openid.return_to argument from this response. - - This is useful for verifying that this request was initiated - by this consumer. - - @returns: The return_to URL supplied to the server on the - initial request, or C{None} if the response did not contain - an C{openid.return_to} argument. - - @returntype: str - """ - return self.getSigned(OPENID_NS, 'return_to') - - def __eq__(self, other): - return ( - (self.endpoint == other.endpoint) and - (self.identity_url == other.identity_url) and - (self.message == other.message) and - (self.signed_fields == other.signed_fields) and - (self.status == other.status)) - - def __ne__(self, other): - return not (self == other) - - def __repr__(self): - return '<%s.%s id=%r signed=%r>' % ( - self.__class__.__module__, - self.__class__.__name__, - self.identity_url, self.signed_fields) - - -class FailureResponse(Response): - """A response with a status of FAILURE. Indicates that the OpenID - protocol has failed. This could be locally or remotely triggered. - - @ivar identity_url: The identity URL for which authenitcation was - attempted, if it can be determined. Otherwise, None. - - @ivar message: A message indicating why the request failed, if one - is supplied. otherwise, None. - - @cvar status: FAILURE - """ - - status = FAILURE - - def __init__(self, endpoint, message=None, contact=None, - reference=None): - self.setEndpoint(endpoint) - self.message = message - self.contact = contact - self.reference = reference - - def __repr__(self): - return "<%s.%s id=%r message=%r>" % ( - self.__class__.__module__, self.__class__.__name__, - self.identity_url, self.message) - - -class CancelResponse(Response): - """A response with a status of CANCEL. Indicates that the user - cancelled the OpenID authentication request. - - @ivar identity_url: The identity URL for which authenitcation was - attempted, if it can be determined. Otherwise, None. - - @cvar status: CANCEL - """ - - status = CANCEL - - def __init__(self, endpoint): - self.setEndpoint(endpoint) - -class SetupNeededResponse(Response): - """A response with a status of SETUP_NEEDED. Indicates that the - request was in immediate mode, and the server is unable to - authenticate the user without further interaction. - - @ivar identity_url: The identity URL for which authenitcation was - attempted. - - @ivar setup_url: A URL that can be used to send the user to the - server to set up for authentication. The user should be - redirected in to the setup_url, either in the current window - or in a new browser window. C{None} in OpenID 2.0. - - @cvar status: SETUP_NEEDED - """ - - status = SETUP_NEEDED - - def __init__(self, endpoint, setup_url=None): - self.setEndpoint(endpoint) - self.setup_url = setup_url diff --git a/askbot/deps/openid/consumer/discover.py b/askbot/deps/openid/consumer/discover.py deleted file mode 100644 index d7a0c2a3..00000000 --- a/askbot/deps/openid/consumer/discover.py +++ /dev/null @@ -1,470 +0,0 @@ -# -*- test-case-name: openid.test.test_discover -*- -"""Functions to discover OpenID endpoints from identifiers. -""" - -__all__ = [ - 'DiscoveryFailure', - 'OPENID_1_0_NS', - 'OPENID_1_0_TYPE', - 'OPENID_1_1_TYPE', - 'OPENID_2_0_TYPE', - 'OPENID_IDP_2_0_TYPE', - 'OpenIDServiceEndpoint', - 'discover', - ] - -import urlparse - -from askbot.deps.openid import oidutil, fetchers, urinorm - -from askbot.deps.openid import yadis -from askbot.deps.openid.yadis.etxrd import nsTag, XRDSError, XRD_NS_2_0 -from askbot.deps.openid.yadis.services import applyFilter as extractServices -from askbot.deps.openid.yadis.discover import discover as yadisDiscover -from askbot.deps.openid.yadis.discover import DiscoveryFailure -from askbot.deps.openid.yadis import xrires, filters -from askbot.deps.openid.yadis import xri - -from askbot.deps.openid.consumer import html_parse - -OPENID_1_0_NS = 'http://openid.net/xmlns/1.0' -OPENID_IDP_2_0_TYPE = 'http://specs.openid.net/auth/2.0/server' -OPENID_2_0_TYPE = 'http://specs.openid.net/auth/2.0/signon' -OPENID_1_1_TYPE = 'http://openid.net/signon/1.1' -OPENID_1_0_TYPE = 'http://openid.net/signon/1.0' - -from askbot.deps.openid.message import OPENID1_NS as OPENID_1_0_MESSAGE_NS -from askbot.deps.openid.message import OPENID2_NS as OPENID_2_0_MESSAGE_NS - -class OpenIDServiceEndpoint(object): - """Object representing an OpenID service endpoint. - - @ivar identity_url: the verified identifier. - @ivar canonicalID: For XRI, the persistent identifier. - """ - - # OpenID service type URIs, listed in order of preference. The - # ordering of this list affects yadis and XRI service discovery. - openid_type_uris = [ - OPENID_IDP_2_0_TYPE, - - OPENID_2_0_TYPE, - OPENID_1_1_TYPE, - OPENID_1_0_TYPE, - ] - - def __init__(self): - self.claimed_id = None - self.server_url = None - self.type_uris = [] - self.local_id = None - self.canonicalID = None - self.used_yadis = False # whether this came from an XRDS - self.display_identifier = None - - def usesExtension(self, extension_uri): - return extension_uri in self.type_uris - - def preferredNamespace(self): - if (OPENID_IDP_2_0_TYPE in self.type_uris or - OPENID_2_0_TYPE in self.type_uris): - return OPENID_2_0_MESSAGE_NS - else: - return OPENID_1_0_MESSAGE_NS - - def supportsType(self, type_uri): - """Does this endpoint support this type? - - I consider C{/server} endpoints to implicitly support C{/signon}. - """ - return ( - (type_uri in self.type_uris) or - (type_uri == OPENID_2_0_TYPE and self.isOPIdentifier()) - ) - - def getDisplayIdentifier(self): - """Return the display_identifier if set, else return the claimed_id. - """ - if self.display_identifier is not None: - return self.display_identifier - if self.claimed_id is None: - return None - else: - return urlparse.urldefrag(self.claimed_id)[0] - - def compatibilityMode(self): - return self.preferredNamespace() != OPENID_2_0_MESSAGE_NS - - def isOPIdentifier(self): - return OPENID_IDP_2_0_TYPE in self.type_uris - - def parseService(self, yadis_url, uri, type_uris, service_element): - """Set the state of this object based on the contents of the - service element.""" - self.type_uris = type_uris - self.server_url = uri - self.used_yadis = True - - if not self.isOPIdentifier(): - # XXX: This has crappy implications for Service elements - # that contain both 'server' and 'signon' Types. But - # that's a pathological configuration anyway, so I don't - # think I care. - self.local_id = findOPLocalIdentifier(service_element, - self.type_uris) - self.claimed_id = yadis_url - - def getLocalID(self): - """Return the identifier that should be sent as the - openid.identity parameter to the server.""" - # I looked at this conditional and thought "ah-hah! there's the bug!" - # but Python actually makes that one big expression somehow, i.e. - # "x is x is x" is not the same thing as "(x is x) is x". - # That's pretty weird, dude. -- kmt, 1/07 - if (self.local_id is self.canonicalID is None): - return self.claimed_id - else: - return self.local_id or self.canonicalID - - def fromBasicServiceEndpoint(cls, endpoint): - """Create a new instance of this class from the endpoint - object passed in. - - @return: None or OpenIDServiceEndpoint for this endpoint object""" - type_uris = endpoint.matchTypes(cls.openid_type_uris) - - # If any Type URIs match and there is an endpoint URI - # specified, then this is an OpenID endpoint - if type_uris and endpoint.uri is not None: - openid_endpoint = cls() - openid_endpoint.parseService( - endpoint.yadis_url, - endpoint.uri, - endpoint.type_uris, - endpoint.service_element) - else: - openid_endpoint = None - - return openid_endpoint - - fromBasicServiceEndpoint = classmethod(fromBasicServiceEndpoint) - - def fromHTML(cls, uri, html): - """Parse the given document as HTML looking for an OpenID <link - rel=...> - - @rtype: [OpenIDServiceEndpoint] - """ - discovery_types = [ - (OPENID_2_0_TYPE, 'openid2.provider', 'openid2.local_id'), - (OPENID_1_1_TYPE, 'openid.server', 'openid.delegate'), - ] - - link_attrs = html_parse.parseLinkAttrs(html) - services = [] - for type_uri, op_endpoint_rel, local_id_rel in discovery_types: - op_endpoint_url = html_parse.findFirstHref( - link_attrs, op_endpoint_rel) - if op_endpoint_url is None: - continue - - service = cls() - service.claimed_id = uri - service.local_id = html_parse.findFirstHref( - link_attrs, local_id_rel) - service.server_url = op_endpoint_url - service.type_uris = [type_uri] - - services.append(service) - - return services - - fromHTML = classmethod(fromHTML) - - - def fromXRDS(cls, uri, xrds): - """Parse the given document as XRDS looking for OpenID services. - - @rtype: [OpenIDServiceEndpoint] - - @raises XRDSError: When the XRDS does not parse. - - @since: 2.1.0 - """ - return extractServices(uri, xrds, cls) - - fromXRDS = classmethod(fromXRDS) - - - def fromDiscoveryResult(cls, discoveryResult): - """Create endpoints from a DiscoveryResult. - - @type discoveryResult: L{DiscoveryResult} - - @rtype: list of L{OpenIDServiceEndpoint} - - @raises XRDSError: When the XRDS does not parse. - - @since: 2.1.0 - """ - if discoveryResult.isXRDS(): - method = cls.fromXRDS - else: - method = cls.fromHTML - return method(discoveryResult.normalized_uri, - discoveryResult.response_text) - - fromDiscoveryResult = classmethod(fromDiscoveryResult) - - - def fromOPEndpointURL(cls, op_endpoint_url): - """Construct an OP-Identifier OpenIDServiceEndpoint object for - a given OP Endpoint URL - - @param op_endpoint_url: The URL of the endpoint - @rtype: OpenIDServiceEndpoint - """ - service = cls() - service.server_url = op_endpoint_url - service.type_uris = [OPENID_IDP_2_0_TYPE] - return service - - fromOPEndpointURL = classmethod(fromOPEndpointURL) - - - def __str__(self): - return ("<%s.%s " - "server_url=%r " - "claimed_id=%r " - "local_id=%r " - "canonicalID=%r " - "used_yadis=%s " - ">" - % (self.__class__.__module__, self.__class__.__name__, - self.server_url, - self.claimed_id, - self.local_id, - self.canonicalID, - self.used_yadis)) - - - -def findOPLocalIdentifier(service_element, type_uris): - """Find the OP-Local Identifier for this xrd:Service element. - - This considers openid:Delegate to be a synonym for xrd:LocalID if - both OpenID 1.X and OpenID 2.0 types are present. If only OpenID - 1.X is present, it returns the value of openid:Delegate. If only - OpenID 2.0 is present, it returns the value of xrd:LocalID. If - there is more than one LocalID tag and the values are different, - it raises a DiscoveryFailure. This is also triggered when the - xrd:LocalID and openid:Delegate tags are different. - - @param service_element: The xrd:Service element - @type service_element: ElementTree.Node - - @param type_uris: The xrd:Type values present in this service - element. This function could extract them, but higher level - code needs to do that anyway. - @type type_uris: [str] - - @raises DiscoveryFailure: when discovery fails. - - @returns: The OP-Local Identifier for this service element, if one - is present, or None otherwise. - @rtype: str or unicode or NoneType - """ - # XXX: Test this function on its own! - - # Build the list of tags that could contain the OP-Local Identifier - local_id_tags = [] - if (OPENID_1_1_TYPE in type_uris or - OPENID_1_0_TYPE in type_uris): - local_id_tags.append(nsTag(OPENID_1_0_NS, 'Delegate')) - - if OPENID_2_0_TYPE in type_uris: - local_id_tags.append(nsTag(XRD_NS_2_0, 'LocalID')) - - # Walk through all the matching tags and make sure that they all - # have the same value - local_id = None - for local_id_tag in local_id_tags: - for local_id_element in service_element.findall(local_id_tag): - if local_id is None: - local_id = local_id_element.text - elif local_id != local_id_element.text: - format = 'More than one %r tag found in one service element' - message = format % (local_id_tag,) - raise DiscoveryFailure(message, None) - - return local_id - -def normalizeURL(url): - """Normalize a URL, converting normalization failures to - DiscoveryFailure""" - try: - normalized = urinorm.urinorm(url) - except ValueError, why: - raise DiscoveryFailure('Normalizing identifier: %s' % (why[0],), None) - else: - return urlparse.urldefrag(normalized)[0] - -def normalizeXRI(xri): - """Normalize an XRI, stripping its scheme if present""" - if xri.startswith("xri://"): - xri = xri[6:] - return xri - -def arrangeByType(service_list, preferred_types): - """Rearrange service_list in a new list so services are ordered by - types listed in preferred_types. Return the new list.""" - - def enumerate(elts): - """Return an iterable that pairs the index of an element with - that element. - - For Python 2.2 compatibility""" - return zip(range(len(elts)), elts) - - def bestMatchingService(service): - """Return the index of the first matching type, or something - higher if no type matches. - - This provides an ordering in which service elements that - contain a type that comes earlier in the preferred types list - come before service elements that come later. If a service - element has more than one type, the most preferred one wins. - """ - for i, t in enumerate(preferred_types): - if preferred_types[i] in service.type_uris: - return i - - return len(preferred_types) - - # Build a list with the service elements in tuples whose - # comparison will prefer the one with the best matching service - prio_services = [(bestMatchingService(s), orig_index, s) - for (orig_index, s) in enumerate(service_list)] - prio_services.sort() - - # Now that the services are sorted by priority, remove the sort - # keys from the list. - for i in range(len(prio_services)): - prio_services[i] = prio_services[i][2] - - return prio_services - -def getOPOrUserServices(openid_services): - """Extract OP Identifier services. If none found, return the - rest, sorted with most preferred first according to - OpenIDServiceEndpoint.openid_type_uris. - - openid_services is a list of OpenIDServiceEndpoint objects. - - Returns a list of OpenIDServiceEndpoint objects.""" - - op_services = arrangeByType(openid_services, [OPENID_IDP_2_0_TYPE]) - - openid_services = arrangeByType(openid_services, - OpenIDServiceEndpoint.openid_type_uris) - - return op_services or openid_services - -def discoverYadis(uri): - """Discover OpenID services for a URI. Tries Yadis and falls back - on old-style <link rel='...'> discovery if Yadis fails. - - @param uri: normalized identity URL - @type uri: str - - @return: (claimed_id, services) - @rtype: (str, list(OpenIDServiceEndpoint)) - - @raises DiscoveryFailure: when discovery fails. - """ - # Might raise a yadis.discover.DiscoveryFailure if no document - # came back for that URI at all. I don't think falling back - # to OpenID 1.0 discovery on the same URL will help, so don't - # bother to catch it. - response = yadisDiscover(uri) - - yadis_url = response.normalized_uri - body = response.response_text - try: - openid_services = OpenIDServiceEndpoint.fromXRDS(yadis_url, body) - except XRDSError: - # Does not parse as a Yadis XRDS file - openid_services = [] - - if not openid_services: - # Either not an XRDS or there are no OpenID services. - - if response.isXRDS(): - # if we got the Yadis content-type or followed the Yadis - # header, re-fetch the document without following the Yadis - # header, with no Accept header. - return discoverNoYadis(uri) - - # Try to parse the response as HTML. - # <link rel="..."> - openid_services = OpenIDServiceEndpoint.fromHTML(yadis_url, body) - - return (yadis_url, getOPOrUserServices(openid_services)) - -def discoverXRI(iname): - endpoints = [] - iname = normalizeXRI(iname) - try: - canonicalID, services = xrires.ProxyResolver().query( - iname, OpenIDServiceEndpoint.openid_type_uris) - - if canonicalID is None: - raise XRDSError('No CanonicalID found for XRI %r' % (iname,)) - - flt = filters.mkFilter(OpenIDServiceEndpoint) - for service_element in services: - endpoints.extend(flt.getServiceEndpoints(iname, service_element)) - except XRDSError: - oidutil.log('xrds error on ' + iname) - - for endpoint in endpoints: - # Is there a way to pass this through the filter to the endpoint - # constructor instead of tacking it on after? - endpoint.canonicalID = canonicalID - endpoint.claimed_id = canonicalID - endpoint.display_identifier = iname - - # FIXME: returned xri should probably be in some normal form - return iname, getOPOrUserServices(endpoints) - - -def discoverNoYadis(uri): - http_resp = fetchers.fetch(uri) - if http_resp.status not in (200, 206): - raise DiscoveryFailure( - 'HTTP Response status from identity URL host is not 200. ' - 'Got status %r' % (http_resp.status,), http_resp) - - claimed_id = http_resp.final_url - openid_services = OpenIDServiceEndpoint.fromHTML( - claimed_id, http_resp.body) - return claimed_id, openid_services - -def discoverURI(uri): - parsed = urlparse.urlparse(uri) - if parsed[0] and parsed[1]: - if parsed[0] not in ['http', 'https']: - raise DiscoveryFailure('URI scheme is not HTTP or HTTPS', None) - else: - uri = 'http://' + uri - - uri = normalizeURL(uri) - claimed_id, openid_services = discoverYadis(uri) - claimed_id = normalizeURL(claimed_id) - return claimed_id, openid_services - -def discover(identifier): - if xri.identifierScheme(identifier) == "XRI": - return discoverXRI(identifier) - else: - return discoverURI(identifier) diff --git a/askbot/deps/openid/consumer/html_parse.py b/askbot/deps/openid/consumer/html_parse.py deleted file mode 100644 index 880dfda6..00000000 --- a/askbot/deps/openid/consumer/html_parse.py +++ /dev/null @@ -1,249 +0,0 @@ -""" -This module implements a VERY limited parser that finds <link> tags in -the head of HTML or XHTML documents and parses out their attributes -according to the OpenID spec. It is a liberal parser, but it requires -these things from the data in order to work: - - - There must be an open <html> tag - - - There must be an open <head> tag inside of the <html> tag - - - Only <link>s that are found inside of the <head> tag are parsed - (this is by design) - - - The parser follows the OpenID specification in resolving the - attributes of the link tags. This means that the attributes DO NOT - get resolved as they would by an XML or HTML parser. In particular, - only certain entities get replaced, and href attributes do not get - resolved relative to a base URL. - -From http://openid.net/specs.bml#linkrel: - - - The openid.server URL MUST be an absolute URL. OpenID consumers - MUST NOT attempt to resolve relative URLs. - - - The openid.server URL MUST NOT include entities other than &, - <, >, and ". - -The parser ignores SGML comments and <![CDATA[blocks]]>. Both kinds of -quoting are allowed for attributes. - -The parser deals with invalid markup in these ways: - - - Tag names are not case-sensitive - - - The <html> tag is accepted even when it is not at the top level - - - The <head> tag is accepted even when it is not a direct child of - the <html> tag, but a <html> tag must be an ancestor of the <head> - tag - - - <link> tags are accepted even when they are not direct children of - the <head> tag, but a <head> tag must be an ancestor of the <link> - tag - - - If there is no closing tag for an open <html> or <head> tag, the - remainder of the document is viewed as being inside of the tag. If - there is no closing tag for a <link> tag, the link tag is treated - as a short tag. Exceptions to this rule are that <html> closes - <html> and <body> or <head> closes <head> - - - Attributes of the <link> tag are not required to be quoted. - - - In the case of duplicated attribute names, the attribute coming - last in the tag will be the value returned. - - - Any text that does not parse as an attribute within a link tag will - be ignored. (e.g. <link pumpkin rel='openid.server' /> will ignore - pumpkin) - - - If there are more than one <html> or <head> tag, the parser only - looks inside of the first one. - - - The contents of <script> tags are ignored entirely, except unclosed - <script> tags. Unclosed <script> tags are ignored. - - - Any other invalid markup is ignored, including unclosed SGML - comments and unclosed <![CDATA[blocks. -""" - -__all__ = ['parseLinkAttrs'] - -import re - -flags = ( re.DOTALL # Match newlines with '.' - | re.IGNORECASE - | re.VERBOSE # Allow comments and whitespace in patterns - | re.UNICODE # Make \b respect Unicode word boundaries - ) - -# Stuff to remove before we start looking for tags -removed_re = re.compile(r''' - # Comments - <!--.*?--> - - # CDATA blocks -| <!\[CDATA\[.*?\]\]> - - # script blocks -| <script\b - - # make sure script is not an XML namespace - (?!:) - - [^>]*>.*?</script> - -''', flags) - -tag_expr = r''' -# Starts with the tag name at a word boundary, where the tag name is -# not a namespace -<%(tag_name)s\b(?!:) - -# All of the stuff up to a ">", hopefully attributes. -(?P<attrs>[^>]*?) - -(?: # Match a short tag - /> - -| # Match a full tag - > - - (?P<contents>.*?) - - # Closed by - (?: # One of the specified close tags - </?%(closers)s\s*> - - # End of the string - | \Z - - ) - -) -''' - -def tagMatcher(tag_name, *close_tags): - if close_tags: - options = '|'.join((tag_name,) + close_tags) - closers = '(?:%s)' % (options,) - else: - closers = tag_name - - expr = tag_expr % locals() - return re.compile(expr, flags) - -# Must contain at least an open html and an open head tag -html_find = tagMatcher('html') -head_find = tagMatcher('head', 'body') -link_find = re.compile(r'<link\b(?!:)', flags) - -attr_find = re.compile(r''' -# Must start with a sequence of word-characters, followed by an equals sign -(?P<attr_name>\w+)= - -# Then either a quoted or unquoted attribute -(?: - - # Match everything that\'s between matching quote marks - (?P<qopen>["\'])(?P<q_val>.*?)(?P=qopen) -| - - # If the value is not quoted, match up to whitespace - (?P<unq_val>(?:[^\s<>/]|/(?!>))+) -) - -| - -(?P<end_link>[<>]) -''', flags) - -# Entity replacement: -replacements = { - 'amp':'&', - 'lt':'<', - 'gt':'>', - 'quot':'"', - } - -ent_replace = re.compile(r'&(%s);' % '|'.join(replacements.keys())) -def replaceEnt(mo): - "Replace the entities that are specified by OpenID" - return replacements.get(mo.group(1), mo.group()) - -def parseLinkAttrs(html): - """Find all link tags in a string representing a HTML document and - return a list of their attributes. - - @param html: the text to parse - @type html: str or unicode - - @return: A list of dictionaries of attributes, one for each link tag - @rtype: [[(type(html), type(html))]] - """ - stripped = removed_re.sub('', html) - html_mo = html_find.search(stripped) - if html_mo is None or html_mo.start('contents') == -1: - return [] - - start, end = html_mo.span('contents') - head_mo = head_find.search(stripped, start, end) - if head_mo is None or head_mo.start('contents') == -1: - return [] - - start, end = head_mo.span('contents') - link_mos = link_find.finditer(stripped, head_mo.start(), head_mo.end()) - - matches = [] - for link_mo in link_mos: - start = link_mo.start() + 5 - link_attrs = {} - for attr_mo in attr_find.finditer(stripped, start): - if attr_mo.lastgroup == 'end_link': - break - - # Either q_val or unq_val must be present, but not both - # unq_val is a True (non-empty) value if it is present - attr_name, q_val, unq_val = attr_mo.group( - 'attr_name', 'q_val', 'unq_val') - attr_val = ent_replace.sub(replaceEnt, unq_val or q_val) - - link_attrs[attr_name] = attr_val - - matches.append(link_attrs) - - return matches - -def relMatches(rel_attr, target_rel): - """Does this target_rel appear in the rel_str?""" - # XXX: TESTME - rels = rel_attr.strip().split() - for rel in rels: - rel = rel.lower() - if rel == target_rel: - return 1 - - return 0 - -def linkHasRel(link_attrs, target_rel): - """Does this link have target_rel as a relationship?""" - # XXX: TESTME - rel_attr = link_attrs.get('rel') - return rel_attr and relMatches(rel_attr, target_rel) - -def findLinksRel(link_attrs_list, target_rel): - """Filter the list of link attributes on whether it has target_rel - as a relationship.""" - # XXX: TESTME - matchesTarget = lambda attrs: linkHasRel(attrs, target_rel) - return filter(matchesTarget, link_attrs_list) - -def findFirstHref(link_attrs_list, target_rel): - """Return the value of the href attribute for the first link tag - in the list that has target_rel as a relationship.""" - # XXX: TESTME - matches = findLinksRel(link_attrs_list, target_rel) - if not matches: - return None - first = matches[0] - return first.get('href') diff --git a/askbot/deps/openid/cryptutil.py b/askbot/deps/openid/cryptutil.py deleted file mode 100644 index 8d4bef8a..00000000 --- a/askbot/deps/openid/cryptutil.py +++ /dev/null @@ -1,220 +0,0 @@ -"""Module containing a cryptographic-quality source of randomness and -other cryptographically useful functionality - -Python 2.4 needs no external support for this module, nor does Python -2.3 on a system with /dev/urandom. - -Other configurations will need a quality source of random bytes and -access to a function that will convert binary strings to long -integers. This module will work with the Python Cryptography Toolkit -(pycrypto) if it is present. pycrypto can be found with a search -engine, but is currently found at: - -http://www.amk.ca/python/code/crypto -""" - -__all__ = [ - 'base64ToLong', - 'binaryToLong', - 'hmacSha1', - 'hmacSha256', - 'longToBase64', - 'longToBinary', - 'randomString', - 'randrange', - 'sha1', - 'sha256', - ] - -import hmac -import os -import random - -from askbot.deps.openid.oidutil import toBase64, fromBase64 - -try: - import hashlib -except ImportError: - import sha as sha1_module - - try: - from Crypto.Hash import SHA256 as sha256_module - except ImportError: - sha256_module = None - -else: - class HashContainer(object): - def __init__(self, hash_constructor): - self.new = hash_constructor - self.digest_size = hash_constructor().digest_size - - sha1_module = HashContainer(hashlib.sha1) - sha256_module = HashContainer(hashlib.sha256) - -def hmacSha1(key, text): - return hmac.new(key, text, sha1_module).digest() - -def sha1(s): - return sha1_module.new(s).digest() - -if sha256_module is not None: - def hmacSha256(key, text): - return hmac.new(key, text, sha256_module).digest() - - def sha256(s): - return sha256_module.new(s).digest() - - SHA256_AVAILABLE = True - -else: - _no_sha256 = NotImplementedError( - 'Use Python 2.5, install pycrypto or install hashlib to use SHA256') - - def hmacSha256(unused_key, unused_text): - raise _no_sha256 - - def sha256(s): - raise _no_sha256 - - SHA256_AVAILABLE = False - -try: - from Crypto.Util.number import long_to_bytes, bytes_to_long -except ImportError: - import pickle - try: - # Check Python compatiblity by raising an exception on import - # if the needed functionality is not present. Present in - # Python >= 2.3 - pickle.encode_long - pickle.decode_long - except AttributeError: - raise ImportError( - 'No functionality for serializing long integers found') - - # Present in Python >= 2.4 - try: - reversed - except NameError: - def reversed(seq): - return map(seq.__getitem__, xrange(len(seq) - 1, -1, -1)) - - def longToBinary(l): - if l == 0: - return '\x00' - - return ''.join(reversed(pickle.encode_long(l))) - - def binaryToLong(s): - return pickle.decode_long(''.join(reversed(s))) -else: - # We have pycrypto - - def longToBinary(l): - if l < 0: - raise ValueError('This function only supports positive integers') - - bytes = long_to_bytes(l) - if ord(bytes[0]) > 127: - return '\x00' + bytes - else: - return bytes - - def binaryToLong(bytes): - if not bytes: - raise ValueError('Empty string passed to strToLong') - - if ord(bytes[0]) > 127: - raise ValueError('This function only supports positive integers') - - return bytes_to_long(bytes) - -# A cryptographically safe source of random bytes -try: - getBytes = os.urandom -except AttributeError: - try: - from Crypto.Util.randpool import RandomPool - except ImportError: - # Fall back on /dev/urandom, if present. It would be nice to - # have Windows equivalent here, but for now, require pycrypto - # on Windows. - try: - _urandom = file('/dev/urandom', 'rb') - except IOError: - raise ImportError('No adequate source of randomness found!') - else: - def getBytes(n): - bytes = [] - while n: - chunk = _urandom.read(n) - n -= len(chunk) - bytes.append(chunk) - assert n >= 0 - return ''.join(bytes) - else: - _pool = RandomPool() - def getBytes(n, pool=_pool): - if pool.entropy < n: - pool.randomize() - return pool.get_bytes(n) - -# A randrange function that works for longs -try: - randrange = random.SystemRandom().randrange -except AttributeError: - # In Python 2.2's random.Random, randrange does not support - # numbers larger than sys.maxint for randrange. For simplicity, - # use this implementation for any Python that does not have - # random.SystemRandom - from math import log, ceil - - _duplicate_cache = {} - def randrange(start, stop=None, step=1): - if stop is None: - stop = start - start = 0 - - r = (stop - start) // step - try: - (duplicate, nbytes) = _duplicate_cache[r] - except KeyError: - rbytes = longToBinary(r) - if rbytes[0] == '\x00': - nbytes = len(rbytes) - 1 - else: - nbytes = len(rbytes) - - mxrand = (256 ** nbytes) - - # If we get a number less than this, then it is in the - # duplicated range. - duplicate = mxrand % r - - if len(_duplicate_cache) > 10: - _duplicate_cache.clear() - - _duplicate_cache[r] = (duplicate, nbytes) - - while 1: - bytes = '\x00' + getBytes(nbytes) - n = binaryToLong(bytes) - # Keep looping if this value is in the low duplicated range - if n >= duplicate: - break - - return start + (n % r) * step - -def longToBase64(l): - return toBase64(longToBinary(l)) - -def base64ToLong(s): - return binaryToLong(fromBase64(s)) - -def randomString(length, chrs=None): - """Produce a string of length random bytes, chosen from chrs.""" - if chrs is None: - return getBytes(length) - else: - n = len(chrs) - return ''.join([chrs[randrange(n)] for _ in xrange(length)]) diff --git a/askbot/deps/openid/dh.py b/askbot/deps/openid/dh.py deleted file mode 100644 index 7dbb758d..00000000 --- a/askbot/deps/openid/dh.py +++ /dev/null @@ -1,42 +0,0 @@ -from askbot.deps.openid import cryptutil -from askbot.deps.openid import oidutil - -def strxor(x, y): - if len(x) != len(y): - raise ValueError('Inputs to strxor must have the same length') - - xor = lambda (a, b): chr(ord(a) ^ ord(b)) - return "".join(map(xor, zip(x, y))) - -class DiffieHellman(object): - DEFAULT_MOD = 155172898181473697471232257763715539915724801966915404479707795314057629378541917580651227423698188993727816152646631438561595825688188889951272158842675419950341258706556549803580104870537681476726513255747040765857479291291572334510643245094715007229621094194349783925984760375594985848253359305585439638443L - - DEFAULT_GEN = 2 - - def fromDefaults(cls): - return cls(cls.DEFAULT_MOD, cls.DEFAULT_GEN) - - fromDefaults = classmethod(fromDefaults) - - def __init__(self, modulus, generator): - self.modulus = long(modulus) - self.generator = long(generator) - - self._setPrivate(cryptutil.randrange(1, modulus - 1)) - - def _setPrivate(self, private): - """This is here to make testing easier""" - self.private = private - self.public = pow(self.generator, self.private, self.modulus) - - def usingDefaultValues(self): - return (self.modulus == self.DEFAULT_MOD and - self.generator == self.DEFAULT_GEN) - - def getSharedSecret(self, composite): - return pow(composite, self.private, self.modulus) - - def xorSecret(self, composite, secret, hash_func): - dh_shared = self.getSharedSecret(composite) - hashed_dh_shared = hash_func(cryptutil.longToBinary(dh_shared)) - return strxor(secret, hashed_dh_shared) diff --git a/askbot/deps/openid/extension.py b/askbot/deps/openid/extension.py deleted file mode 100644 index d6ec0044..00000000 --- a/askbot/deps/openid/extension.py +++ /dev/null @@ -1,46 +0,0 @@ -from askbot.deps.openid import message as message_module - -class Extension(object): - """An interface for OpenID extensions. - - @ivar ns_uri: The namespace to which to add the arguments for this - extension - """ - ns_uri = None - ns_alias = None - - def getExtensionArgs(self): - """Get the string arguments that should be added to an OpenID - message for this extension. - - @returns: A dictionary of completely non-namespaced arguments - to be added. For example, if the extension's alias is - 'uncle', and this method returns {'meat':'Hot Rats'}, the - final message will contain {'openid.uncle.meat':'Hot Rats'} - """ - raise NotImplementedError - - def toMessage(self, message=None): - """Add the arguments from this extension to the provided - message, or create a new message containing only those - arguments. - - @returns: The message with the extension arguments added - """ - if message is None: - warnings.warn('Passing None to Extension.toMessage is deprecated. ' - 'Creating a message assuming you want OpenID 2.', - DeprecationWarning, stacklevel=2) - message = message_module.Message(message_module.OPENID2_NS) - - implicit = message.isOpenID1() - - try: - message.namespaces.addAlias(self.ns_uri, self.ns_alias, - implicit=implicit) - except KeyError: - if message.namespaces.getAlias(self.ns_uri) != self.ns_alias: - raise - - message.updateArgs(self.ns_uri, self.getExtensionArgs()) - return message diff --git a/askbot/deps/openid/extensions/__init__.py b/askbot/deps/openid/extensions/__init__.py deleted file mode 100644 index e27aa75f..00000000 --- a/askbot/deps/openid/extensions/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""OpenID Extension modules.""" - -__all__ = ['ax', 'pape', 'sreg'] - -from askbot.deps.openid.extensions.draft import pape5 as pape diff --git a/askbot/deps/openid/extensions/ax.py b/askbot/deps/openid/extensions/ax.py deleted file mode 100644 index 9ba89b93..00000000 --- a/askbot/deps/openid/extensions/ax.py +++ /dev/null @@ -1,774 +0,0 @@ -# -*- test-case-name: openid.test.test_ax -*- -"""Implements the OpenID Attribute Exchange specification, version 1.0. - -@since: 2.1.0 -""" - -__all__ = [ - 'AttributeRequest', - 'FetchRequest', - 'FetchResponse', - 'StoreRequest', - 'StoreResponse', - ] - -from askbot.deps.openid import extension -from askbot.deps.openid.server.trustroot import TrustRoot -from askbot.deps.openid.message import NamespaceMap, OPENID_NS - -# Use this as the 'count' value for an attribute in a FetchRequest to -# ask for as many values as the OP can provide. -UNLIMITED_VALUES = "unlimited" - -# Minimum supported alias length in characters. Here for -# completeness. -MINIMUM_SUPPORTED_ALIAS_LENGTH = 32 - -def checkAlias(alias): - """ - Check an alias for invalid characters; raise AXError if any are - found. Return None if the alias is valid. - """ - if ',' in alias: - raise AXError("Alias %r must not contain comma" % (alias,)) - if '.' in alias: - raise AXError("Alias %r must not contain period" % (alias,)) - - -class AXError(ValueError): - """Results from data that does not meet the attribute exchange 1.0 - specification""" - - -class NotAXMessage(AXError): - """Raised when there is no Attribute Exchange mode in the message.""" - - def __repr__(self): - return self.__class__.__name__ - - def __str__(self): - return self.__class__.__name__ - - -class AXMessage(extension.Extension): - """Abstract class containing common code for attribute exchange messages - - @cvar ns_alias: The preferred namespace alias for attribute - exchange messages - - @cvar mode: The type of this attribute exchange message. This must - be overridden in subclasses. - """ - - # This class is abstract, so it's OK that it doesn't override the - # abstract method in Extension: - # - #pylint:disable-msg=W0223 - - ns_alias = 'ax' - mode = None - ns_uri = 'http://openid.net/srv/ax/1.0' - - def _checkMode(self, ax_args): - """Raise an exception if the mode in the attribute exchange - arguments does not match what is expected for this class. - - @raises NotAXMessage: When there is no mode value in ax_args at all. - - @raises AXError: When mode does not match. - """ - mode = ax_args.get('mode') - if mode != self.mode: - if not mode: - raise NotAXMessage() - else: - raise AXError( - 'Expected mode %r; got %r' % (self.mode, mode)) - - def _newArgs(self): - """Return a set of attribute exchange arguments containing the - basic information that must be in every attribute exchange - message. - """ - return {'mode':self.mode} - - -class AttrInfo(object): - """Represents a single attribute in an attribute exchange - request. This should be added to an AXRequest object in order to - request the attribute. - - @ivar required: Whether the attribute will be marked as required - when presented to the subject of the attribute exchange - request. - @type required: bool - - @ivar count: How many values of this type to request from the - subject. Defaults to one. - @type count: int - - @ivar type_uri: The identifier that determines what the attribute - represents and how it is serialized. For example, one type URI - representing dates could represent a Unix timestamp in base 10 - and another could represent a human-readable string. - @type type_uri: str - - @ivar alias: The name that should be given to this alias in the - request. If it is not supplied, a generic name will be - assigned. For example, if you want to call a Unix timestamp - value 'tstamp', set its alias to that value. If two attributes - in the same message request to use the same alias, the request - will fail to be generated. - @type alias: str or NoneType - """ - - # It's OK that this class doesn't have public methods (it's just a - # holder for a bunch of attributes): - # - #pylint:disable-msg=R0903 - - def __init__(self, type_uri, count=1, required=False, alias=None): - self.required = required - self.count = count - self.type_uri = type_uri - self.alias = alias - - if self.alias is not None: - checkAlias(self.alias) - - def wantsUnlimitedValues(self): - """ - When processing a request for this attribute, the OP should - call this method to determine whether all available attribute - values were requested. If self.count == UNLIMITED_VALUES, - this returns True. Otherwise this returns False, in which - case self.count is an integer. - """ - return self.count == UNLIMITED_VALUES - -def toTypeURIs(namespace_map, alias_list_s): - """Given a namespace mapping and a string containing a - comma-separated list of namespace aliases, return a list of type - URIs that correspond to those aliases. - - @param namespace_map: The mapping from namespace URI to alias - @type namespace_map: openid.message.NamespaceMap - - @param alias_list_s: The string containing the comma-separated - list of aliases. May also be None for convenience. - @type alias_list_s: str or NoneType - - @returns: The list of namespace URIs that corresponds to the - supplied list of aliases. If the string was zero-length or - None, an empty list will be returned. - - @raise KeyError: If an alias is present in the list of aliases but - is not present in the namespace map. - """ - uris = [] - - if alias_list_s: - for alias in alias_list_s.split(','): - type_uri = namespace_map.getNamespaceURI(alias) - if type_uri is None: - raise KeyError( - 'No type is defined for attribute name %r' % (alias,)) - else: - uris.append(type_uri) - - return uris - - -class FetchRequest(AXMessage): - """An attribute exchange 'fetch_request' message. This message is - sent by a relying party when it wishes to obtain attributes about - the subject of an OpenID authentication request. - - @ivar requested_attributes: The attributes that have been - requested thus far, indexed by the type URI. - @type requested_attributes: {str:AttrInfo} - - @ivar update_url: A URL that will accept responses for this - attribute exchange request, even in the absence of the user - who made this request. - """ - mode = 'fetch_request' - - def __init__(self, update_url=None): - AXMessage.__init__(self) - self.requested_attributes = {} - self.update_url = update_url - - def add(self, attribute): - """Add an attribute to this attribute exchange request. - - @param attribute: The attribute that is being requested - @type attribute: C{L{AttrInfo}} - - @returns: None - - @raise KeyError: when the requested attribute is already - present in this fetch request. - """ - if attribute.type_uri in self.requested_attributes: - raise KeyError('The attribute %r has already been requested' - % (attribute.type_uri,)) - - self.requested_attributes[attribute.type_uri] = attribute - - def getExtensionArgs(self): - """Get the serialized form of this attribute fetch request. - - @returns: The fetch request message parameters - @rtype: {unicode:unicode} - """ - aliases = NamespaceMap() - - required = [] - if_available = [] - - ax_args = self._newArgs() - - for type_uri, attribute in self.requested_attributes.iteritems(): - if attribute.alias is None: - alias = aliases.add(type_uri) - else: - # This will raise an exception when the second - # attribute with the same alias is added. I think it - # would be better to complain at the time that the - # attribute is added to this object so that the code - # that is adding it is identified in the stack trace, - # but it's more work to do so, and it won't be 100% - # accurate anyway, since the attributes are - # mutable. So for now, just live with the fact that - # we'll learn about the error later. - # - # The other possible approach is to hide the error and - # generate a new alias on the fly. I think that would - # probably be bad. - alias = aliases.addAlias(type_uri, attribute.alias) - - if attribute.required: - required.append(alias) - else: - if_available.append(alias) - - if attribute.count != 1: - ax_args['count.' + alias] = str(attribute.count) - - ax_args['type.' + alias] = type_uri - - if required: - ax_args['required'] = ','.join(required) - - if if_available: - ax_args['if_available'] = ','.join(if_available) - - return ax_args - - def getRequiredAttrs(self): - """Get the type URIs for all attributes that have been marked - as required. - - @returns: A list of the type URIs for attributes that have - been marked as required. - @rtype: [str] - """ - required = [] - for type_uri, attribute in self.requested_attributes.iteritems(): - if attribute.required: - required.append(type_uri) - - return required - - def fromOpenIDRequest(cls, openid_request): - """Extract a FetchRequest from an OpenID message - - @param openid_request: The OpenID authentication request - containing the attribute fetch request - @type openid_request: C{L{openid.server.server.CheckIDRequest}} - - @rtype: C{L{FetchRequest}} or C{None} - @returns: The FetchRequest extracted from the message or None, if - the message contained no AX extension. - - @raises KeyError: if the AuthRequest is not consistent in its use - of namespace aliases. - - @raises AXError: When parseExtensionArgs would raise same. - - @see: L{parseExtensionArgs} - """ - message = openid_request.message - ax_args = message.getArgs(cls.ns_uri) - self = cls() - try: - self.parseExtensionArgs(ax_args) - except NotAXMessage, err: - return None - - if self.update_url: - # Update URL must match the openid.realm of the underlying - # OpenID 2 message. - realm = message.getArg(OPENID_NS, 'realm', - message.getArg(OPENID_NS, 'return_to')) - - if not realm: - raise AXError(("Cannot validate update_url %r " + - "against absent realm") % (self.update_url,)) - - tr = TrustRoot.parse(realm) - if not tr.validateURL(self.update_url): - raise AXError("Update URL %r failed validation against realm %r" % - (self.update_url, realm,)) - - return self - - fromOpenIDRequest = classmethod(fromOpenIDRequest) - - def parseExtensionArgs(self, ax_args): - """Given attribute exchange arguments, populate this FetchRequest. - - @param ax_args: Attribute Exchange arguments from the request. - As returned from L{Message.getArgs<openid.message.Message.getArgs>}. - @type ax_args: dict - - @raises KeyError: if the message is not consistent in its use - of namespace aliases. - - @raises NotAXMessage: If ax_args does not include an Attribute Exchange - mode. - - @raises AXError: If the data to be parsed does not follow the - attribute exchange specification. At least when - 'if_available' or 'required' is not specified for a - particular attribute type. - """ - # Raises an exception if the mode is not the expected value - self._checkMode(ax_args) - - aliases = NamespaceMap() - - for key, value in ax_args.iteritems(): - if key.startswith('type.'): - alias = key[5:] - type_uri = value - aliases.addAlias(type_uri, alias) - - count_key = 'count.' + alias - count_s = ax_args.get(count_key) - if count_s: - try: - count = int(count_s) - if count <= 0: - raise AXError("Count %r must be greater than zero, got %r" % (count_key, count_s,)) - except ValueError: - if count_s != UNLIMITED_VALUES: - raise AXError("Invalid count value for %r: %r" % (count_key, count_s,)) - count = count_s - else: - count = 1 - - self.add(AttrInfo(type_uri, alias=alias, count=count)) - - required = toTypeURIs(aliases, ax_args.get('required')) - - for type_uri in required: - self.requested_attributes[type_uri].required = True - - if_available = toTypeURIs(aliases, ax_args.get('if_available')) - - all_type_uris = required + if_available - - for type_uri in aliases.iterNamespaceURIs(): - if type_uri not in all_type_uris: - raise AXError( - 'Type URI %r was in the request but not ' - 'present in "required" or "if_available"' % (type_uri,)) - - self.update_url = ax_args.get('update_url') - - def iterAttrs(self): - """Iterate over the AttrInfo objects that are - contained in this fetch_request. - """ - return self.requested_attributes.itervalues() - - def __iter__(self): - """Iterate over the attribute type URIs in this fetch_request - """ - return iter(self.requested_attributes) - - def has_key(self, type_uri): - """Is the given type URI present in this fetch_request? - """ - return type_uri in self.requested_attributes - - __contains__ = has_key - - -class AXKeyValueMessage(AXMessage): - """An abstract class that implements a message that has attribute - keys and values. It contains the common code between - fetch_response and store_request. - """ - - # This class is abstract, so it's OK that it doesn't override the - # abstract method in Extension: - # - #pylint:disable-msg=W0223 - - def __init__(self): - AXMessage.__init__(self) - self.data = {} - - def addValue(self, type_uri, value): - """Add a single value for the given attribute type to the - message. If there are already values specified for this type, - this value will be sent in addition to the values already - specified. - - @param type_uri: The URI for the attribute - - @param value: The value to add to the response to the relying - party for this attribute - @type value: unicode - - @returns: None - """ - try: - values = self.data[type_uri] - except KeyError: - values = self.data[type_uri] = [] - - values.append(value) - - def setValues(self, type_uri, values): - """Set the values for the given attribute type. This replaces - any values that have already been set for this attribute. - - @param type_uri: The URI for the attribute - - @param values: A list of values to send for this attribute. - @type values: [unicode] - """ - - self.data[type_uri] = values - - def _getExtensionKVArgs(self, aliases=None): - """Get the extension arguments for the key/value pairs - contained in this message. - - @param aliases: An alias mapping. Set to None if you don't - care about the aliases for this request. - """ - if aliases is None: - aliases = NamespaceMap() - - ax_args = {} - - for type_uri, values in self.data.iteritems(): - alias = aliases.add(type_uri) - - ax_args['type.' + alias] = type_uri - ax_args['count.' + alias] = str(len(values)) - - for i, value in enumerate(values): - key = 'value.%s.%d' % (alias, i + 1) - ax_args[key] = value - - return ax_args - - def parseExtensionArgs(self, ax_args): - """Parse attribute exchange key/value arguments into this - object. - - @param ax_args: The attribute exchange fetch_response - arguments, with namespacing removed. - @type ax_args: {unicode:unicode} - - @returns: None - - @raises ValueError: If the message has bad values for - particular fields - - @raises KeyError: If the namespace mapping is bad or required - arguments are missing - """ - self._checkMode(ax_args) - - aliases = NamespaceMap() - - for key, value in ax_args.iteritems(): - if key.startswith('type.'): - type_uri = value - alias = key[5:] - checkAlias(alias) - aliases.addAlias(type_uri, alias) - - for type_uri, alias in aliases.iteritems(): - try: - count_s = ax_args['count.' + alias] - except KeyError: - value = ax_args['value.' + alias] - - if value == u'': - values = [] - else: - values = [value] - else: - count = int(count_s) - values = [] - for i in range(1, count + 1): - value_key = 'value.%s.%d' % (alias, i) - value = ax_args[value_key] - values.append(value) - - self.data[type_uri] = values - - def getSingle(self, type_uri, default=None): - """Get a single value for an attribute. If no value was sent - for this attribute, use the supplied default. If there is more - than one value for this attribute, this method will fail. - - @type type_uri: str - @param type_uri: The URI for the attribute - - @param default: The value to return if the attribute was not - sent in the fetch_response. - - @returns: The value of the attribute in the fetch_response - message, or the default supplied - @rtype: unicode or NoneType - - @raises ValueError: If there is more than one value for this - parameter in the fetch_response message. - @raises KeyError: If the attribute was not sent in this response - """ - values = self.data.get(type_uri) - if not values: - return default - elif len(values) == 1: - return values[0] - else: - raise AXError( - 'More than one value present for %r' % (type_uri,)) - - def get(self, type_uri): - """Get the list of values for this attribute in the - fetch_response. - - XXX: what to do if the values are not present? default - parameter? this is funny because it's always supposed to - return a list, so the default may break that, though it's - provided by the user's code, so it might be okay. If no - default is supplied, should the return be None or []? - - @param type_uri: The URI of the attribute - - @returns: The list of values for this attribute in the - response. May be an empty list. - @rtype: [unicode] - - @raises KeyError: If the attribute was not sent in the response - """ - return self.data[type_uri] - - def count(self, type_uri): - """Get the number of responses for a particular attribute in - this fetch_response message. - - @param type_uri: The URI of the attribute - - @returns: The number of values sent for this attribute - - @raises KeyError: If the attribute was not sent in the - response. KeyError will not be raised if the number of - values was zero. - """ - return len(self.get(type_uri)) - - -class FetchResponse(AXKeyValueMessage): - """A fetch_response attribute exchange message - """ - mode = 'fetch_response' - - def __init__(self, request=None, update_url=None): - """ - @param request: When supplied, I will use namespace aliases - that match those in this request. I will also check to - make sure I do not respond with attributes that were not - requested. - - @type request: L{FetchRequest} - - @param update_url: By default, C{update_url} is taken from the - request. But if you do not supply the request, you may set - the C{update_url} here. - - @type update_url: str - """ - AXKeyValueMessage.__init__(self) - self.update_url = update_url - self.request = request - - def getExtensionArgs(self): - """Serialize this object into arguments in the attribute - exchange namespace - - @returns: The dictionary of unqualified attribute exchange - arguments that represent this fetch_response. - @rtype: {unicode;unicode} - """ - - aliases = NamespaceMap() - - zero_value_types = [] - - if self.request is not None: - # Validate the data in the context of the request (the - # same attributes should be present in each, and the - # counts in the response must be no more than the counts - # in the request) - - for type_uri in self.data: - if type_uri not in self.request: - raise KeyError( - 'Response attribute not present in request: %r' - % (type_uri,)) - - for attr_info in self.request.iterAttrs(): - # Copy the aliases from the request so that reading - # the response in light of the request is easier - if attr_info.alias is None: - aliases.add(attr_info.type_uri) - else: - aliases.addAlias(attr_info.type_uri, attr_info.alias) - - try: - values = self.data[attr_info.type_uri] - except KeyError: - values = [] - zero_value_types.append(attr_info) - - if (attr_info.count != UNLIMITED_VALUES) and \ - (attr_info.count < len(values)): - raise AXError( - 'More than the number of requested values were ' - 'specified for %r' % (attr_info.type_uri,)) - - kv_args = self._getExtensionKVArgs(aliases) - - # Add the KV args into the response with the args that are - # unique to the fetch_response - ax_args = self._newArgs() - - # For each requested attribute, put its type/alias and count - # into the response even if no data were returned. - for attr_info in zero_value_types: - alias = aliases.getAlias(attr_info.type_uri) - kv_args['type.' + alias] = attr_info.type_uri - kv_args['count.' + alias] = '0' - - update_url = ((self.request and self.request.update_url) - or self.update_url) - - if update_url: - ax_args['update_url'] = update_url - - ax_args.update(kv_args) - - return ax_args - - def parseExtensionArgs(self, ax_args): - """@see: {Extension.parseExtensionArgs<openid.extension.Extension.parseExtensionArgs>}""" - super(FetchResponse, self).parseExtensionArgs(ax_args) - self.update_url = ax_args.get('update_url') - - def fromSuccessResponse(cls, success_response, signed=True): - """Construct a FetchResponse object from an OpenID library - SuccessResponse object. - - @param success_response: A successful id_res response object - @type success_response: openid.consumer.consumer.SuccessResponse - - @param signed: Whether non-signed args should be - processsed. If True (the default), only signed arguments - will be processsed. - @type signed: bool - - @returns: A FetchResponse containing the data from the OpenID - message, or None if the SuccessResponse did not contain AX - extension data. - - @raises AXError: when the AX data cannot be parsed. - """ - self = cls() - ax_args = success_response.extensionResponse(self.ns_uri, signed) - - try: - self.parseExtensionArgs(ax_args) - except NotAXMessage, err: - return None - else: - return self - - fromSuccessResponse = classmethod(fromSuccessResponse) - - -class StoreRequest(AXKeyValueMessage): - """A store request attribute exchange message representation - """ - mode = 'store_request' - - def __init__(self, aliases=None): - """ - @param aliases: The namespace aliases to use when making this - store request. Leave as None to use defaults. - """ - super(StoreRequest, self).__init__() - self.aliases = aliases - - def getExtensionArgs(self): - """ - @see: L{Extension.getExtensionArgs<openid.extension.Extension.getExtensionArgs>} - """ - ax_args = self._newArgs() - kv_args = self._getExtensionKVArgs(self.aliases) - ax_args.update(kv_args) - return ax_args - - -class StoreResponse(AXMessage): - """An indication that the store request was processed along with - this OpenID transaction. - """ - - SUCCESS_MODE = 'store_response_success' - FAILURE_MODE = 'store_response_failure' - - def __init__(self, succeeded=True, error_message=None): - AXMessage.__init__(self) - - if succeeded and error_message is not None: - raise AXError('An error message may only be included in a ' - 'failing fetch response') - if succeeded: - self.mode = self.SUCCESS_MODE - else: - self.mode = self.FAILURE_MODE - - self.error_message = error_message - - def succeeded(self): - """Was this response a success response?""" - return self.mode == self.SUCCESS_MODE - - def getExtensionArgs(self): - """@see: {Extension.getExtensionArgs<openid.extension.Extension.getExtensionArgs>}""" - ax_args = self._newArgs() - if not self.succeeded() and self.error_message: - ax_args['error'] = self.error_message - - return ax_args diff --git a/askbot/deps/openid/extensions/draft/__init__.py b/askbot/deps/openid/extensions/draft/__init__.py deleted file mode 100644 index e69de29b..00000000 --- a/askbot/deps/openid/extensions/draft/__init__.py +++ /dev/null diff --git a/askbot/deps/openid/extensions/draft/pape2.py b/askbot/deps/openid/extensions/draft/pape2.py deleted file mode 100644 index 13e4acd2..00000000 --- a/askbot/deps/openid/extensions/draft/pape2.py +++ /dev/null @@ -1,277 +0,0 @@ -"""An implementation of the OpenID Provider Authentication Policy -Extension 1.0 - -@see: http://openid.net/developers/specs/ - -@since: 2.1.0 -""" - -__all__ = [ - 'Request', - 'Response', - 'ns_uri', - 'AUTH_PHISHING_RESISTANT', - 'AUTH_MULTI_FACTOR', - 'AUTH_MULTI_FACTOR_PHYSICAL', - ] - -from askbot.deps.openid.extension import Extension -import re - -ns_uri = "http://specs.openid.net/extensions/pape/1.0" - -AUTH_MULTI_FACTOR_PHYSICAL = \ - 'http://schemas.openid.net/pape/policies/2007/06/multi-factor-physical' -AUTH_MULTI_FACTOR = \ - 'http://schemas.openid.net/pape/policies/2007/06/multi-factor' -AUTH_PHISHING_RESISTANT = \ - 'http://schemas.openid.net/pape/policies/2007/06/phishing-resistant' - -TIME_VALIDATOR = re.compile('^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ$') - -class Request(Extension): - """A Provider Authentication Policy request, sent from a relying - party to a provider - - @ivar preferred_auth_policies: The authentication policies that - the relying party prefers - @type preferred_auth_policies: [str] - - @ivar max_auth_age: The maximum time, in seconds, that the relying - party wants to allow to have elapsed before the user must - re-authenticate - @type max_auth_age: int or NoneType - """ - - ns_alias = 'pape' - - def __init__(self, preferred_auth_policies=None, max_auth_age=None): - super(Request, self).__init__() - if not preferred_auth_policies: - preferred_auth_policies = [] - - self.preferred_auth_policies = preferred_auth_policies - self.max_auth_age = max_auth_age - - def __nonzero__(self): - return bool(self.preferred_auth_policies or - self.max_auth_age is not None) - - def addPolicyURI(self, policy_uri): - """Add an acceptable authentication policy URI to this request - - This method is intended to be used by the relying party to add - acceptable authentication types to the request. - - @param policy_uri: The identifier for the preferred type of - authentication. - @see: http://openid.net/specs/openid-provider-authentication-policy-extension-1_0-01.html#auth_policies - """ - if policy_uri not in self.preferred_auth_policies: - self.preferred_auth_policies.append(policy_uri) - - def getExtensionArgs(self): - """@see: C{L{Extension.getExtensionArgs}} - """ - ns_args = { - 'preferred_auth_policies':' '.join(self.preferred_auth_policies) - } - - if self.max_auth_age is not None: - ns_args['max_auth_age'] = str(self.max_auth_age) - - return ns_args - - def fromOpenIDRequest(cls, request): - """Instantiate a Request object from the arguments in a - C{checkid_*} OpenID message - """ - self = cls() - args = request.message.getArgs(self.ns_uri) - - if args == {}: - return None - - self.parseExtensionArgs(args) - return self - - fromOpenIDRequest = classmethod(fromOpenIDRequest) - - def parseExtensionArgs(self, args): - """Set the state of this request to be that expressed in these - PAPE arguments - - @param args: The PAPE arguments without a namespace - - @rtype: None - - @raises ValueError: When the max_auth_age is not parseable as - an integer - """ - - # preferred_auth_policies is a space-separated list of policy URIs - self.preferred_auth_policies = [] - - policies_str = args.get('preferred_auth_policies') - if policies_str: - for uri in policies_str.split(' '): - if uri not in self.preferred_auth_policies: - self.preferred_auth_policies.append(uri) - - # max_auth_age is base-10 integer number of seconds - max_auth_age_str = args.get('max_auth_age') - self.max_auth_age = None - - if max_auth_age_str: - try: - self.max_auth_age = int(max_auth_age_str) - except ValueError: - pass - - def preferredTypes(self, supported_types): - """Given a list of authentication policy URIs that a provider - supports, this method returns the subsequence of those types - that are preferred by the relying party. - - @param supported_types: A sequence of authentication policy - type URIs that are supported by a provider - - @returns: The sub-sequence of the supported types that are - preferred by the relying party. This list will be ordered - in the order that the types appear in the supported_types - sequence, and may be empty if the provider does not prefer - any of the supported authentication types. - - @returntype: [str] - """ - return filter(self.preferred_auth_policies.__contains__, - supported_types) - -Request.ns_uri = ns_uri - - -class Response(Extension): - """A Provider Authentication Policy response, sent from a provider - to a relying party - """ - - ns_alias = 'pape' - - def __init__(self, auth_policies=None, auth_time=None, - nist_auth_level=None): - super(Response, self).__init__() - if auth_policies: - self.auth_policies = auth_policies - else: - self.auth_policies = [] - - self.auth_time = auth_time - self.nist_auth_level = nist_auth_level - - def addPolicyURI(self, policy_uri): - """Add a authentication policy to this response - - This method is intended to be used by the provider to add a - policy that the provider conformed to when authenticating the user. - - @param policy_uri: The identifier for the preferred type of - authentication. - @see: http://openid.net/specs/openid-provider-authentication-policy-extension-1_0-01.html#auth_policies - """ - if policy_uri not in self.auth_policies: - self.auth_policies.append(policy_uri) - - def fromSuccessResponse(cls, success_response): - """Create a C{L{Response}} object from a successful OpenID - library response - (C{L{openid.consumer.consumer.SuccessResponse}}) response - message - - @param success_response: A SuccessResponse from consumer.complete() - @type success_response: C{L{openid.consumer.consumer.SuccessResponse}} - - @rtype: Response or None - @returns: A provider authentication policy response from the - data that was supplied with the C{id_res} response or None - if the provider sent no signed PAPE response arguments. - """ - self = cls() - - # PAPE requires that the args be signed. - args = success_response.getSignedNS(self.ns_uri) - - # Only try to construct a PAPE response if the arguments were - # signed in the OpenID response. If not, return None. - if args is not None: - self.parseExtensionArgs(args) - return self - else: - return None - - def parseExtensionArgs(self, args, strict=False): - """Parse the provider authentication policy arguments into the - internal state of this object - - @param args: unqualified provider authentication policy - arguments - - @param strict: Whether to raise an exception when bad data is - encountered - - @returns: None. The data is parsed into the internal fields of - this object. - """ - policies_str = args.get('auth_policies') - if policies_str and policies_str != 'none': - self.auth_policies = policies_str.split(' ') - - nist_level_str = args.get('nist_auth_level') - if nist_level_str: - try: - nist_level = int(nist_level_str) - except ValueError: - if strict: - raise ValueError('nist_auth_level must be an integer between ' - 'zero and four, inclusive') - else: - self.nist_auth_level = None - else: - if 0 <= nist_level < 5: - self.nist_auth_level = nist_level - - auth_time = args.get('auth_time') - if auth_time: - if TIME_VALIDATOR.match(auth_time): - self.auth_time = auth_time - elif strict: - raise ValueError("auth_time must be in RFC3339 format") - - fromSuccessResponse = classmethod(fromSuccessResponse) - - def getExtensionArgs(self): - """@see: C{L{Extension.getExtensionArgs}} - """ - if len(self.auth_policies) == 0: - ns_args = { - 'auth_policies':'none', - } - else: - ns_args = { - 'auth_policies':' '.join(self.auth_policies), - } - - if self.nist_auth_level is not None: - if self.nist_auth_level not in range(0, 5): - raise ValueError('nist_auth_level must be an integer between ' - 'zero and four, inclusive') - ns_args['nist_auth_level'] = str(self.nist_auth_level) - - if self.auth_time is not None: - if not TIME_VALIDATOR.match(self.auth_time): - raise ValueError('auth_time must be in RFC3339 format') - - ns_args['auth_time'] = self.auth_time - - return ns_args - -Response.ns_uri = ns_uri diff --git a/askbot/deps/openid/extensions/draft/pape5.py b/askbot/deps/openid/extensions/draft/pape5.py deleted file mode 100644 index 4ec6f3ab..00000000 --- a/askbot/deps/openid/extensions/draft/pape5.py +++ /dev/null @@ -1,473 +0,0 @@ -"""An implementation of the OpenID Provider Authentication Policy -Extension 1.0, Draft 5 - -@see: http://openid.net/developers/specs/ - -@since: 2.1.0 -""" - -__all__ = [ - 'Request', - 'Response', - 'ns_uri', - 'AUTH_PHISHING_RESISTANT', - 'AUTH_MULTI_FACTOR', - 'AUTH_MULTI_FACTOR_PHYSICAL', - 'LEVELS_NIST', - 'LEVELS_JISA', - ] - -from askbot.deps.openid.extension import Extension -import warnings -import re - -ns_uri = "http://specs.openid.net/extensions/pape/1.0" - -AUTH_MULTI_FACTOR_PHYSICAL = \ - 'http://schemas.openid.net/pape/policies/2007/06/multi-factor-physical' -AUTH_MULTI_FACTOR = \ - 'http://schemas.openid.net/pape/policies/2007/06/multi-factor' -AUTH_PHISHING_RESISTANT = \ - 'http://schemas.openid.net/pape/policies/2007/06/phishing-resistant' -AUTH_NONE = \ - 'http://schemas.openid.net/pape/policies/2007/06/none' - -TIME_VALIDATOR = re.compile('^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ$') - -LEVELS_NIST = 'http://csrc.nist.gov/publications/nistpubs/800-63/SP800-63V1_0_2.pdf' -LEVELS_JISA = 'http://www.jisa.or.jp/spec/auth_level.html' - -class PAPEExtension(Extension): - _default_auth_level_aliases = { - 'nist': LEVELS_NIST, - 'jisa': LEVELS_JISA, - } - - def __init__(self): - self.auth_level_aliases = self._default_auth_level_aliases.copy() - - def _addAuthLevelAlias(self, auth_level_uri, alias=None): - """Add an auth level URI alias to this request. - - @param auth_level_uri: The auth level URI to send in the - request. - - @param alias: The namespace alias to use for this auth level - in this message. May be None if the alias is not - important. - """ - if alias is None: - try: - alias = self._getAlias(auth_level_uri) - except KeyError: - alias = self._generateAlias() - else: - existing_uri = self.auth_level_aliases.get(alias) - if existing_uri is not None and existing_uri != auth_level_uri: - raise KeyError('Attempting to redefine alias %r from %r to %r', - alias, existing_uri, auth_level_uri) - - self.auth_level_aliases[alias] = auth_level_uri - - def _generateAlias(self): - """Return an unused auth level alias""" - for i in xrange(1000): - alias = 'cust%d' % (i,) - if alias not in self.auth_level_aliases: - return alias - - raise RuntimeError('Could not find an unused alias (tried 1000!)') - - def _getAlias(self, auth_level_uri): - """Return the alias for the specified auth level URI. - - @raises KeyError: if no alias is defined - """ - for (alias, existing_uri) in self.auth_level_aliases.iteritems(): - if auth_level_uri == existing_uri: - return alias - - raise KeyError(auth_level_uri) - -class Request(PAPEExtension): - """A Provider Authentication Policy request, sent from a relying - party to a provider - - @ivar preferred_auth_policies: The authentication policies that - the relying party prefers - @type preferred_auth_policies: [str] - - @ivar max_auth_age: The maximum time, in seconds, that the relying - party wants to allow to have elapsed before the user must - re-authenticate - @type max_auth_age: int or NoneType - - @ivar preferred_auth_level_types: Ordered list of authentication - level namespace URIs - - @type preferred_auth_level_types: [str] - """ - - ns_alias = 'pape' - - def __init__(self, preferred_auth_policies=None, max_auth_age=None, - preferred_auth_level_types=None): - super(Request, self).__init__() - if preferred_auth_policies is None: - preferred_auth_policies = [] - - self.preferred_auth_policies = preferred_auth_policies - self.max_auth_age = max_auth_age - self.preferred_auth_level_types = [] - - if preferred_auth_level_types is not None: - for auth_level in preferred_auth_level_types: - self.addAuthLevel(auth_level) - - def __nonzero__(self): - return bool(self.preferred_auth_policies or - self.max_auth_age is not None or - self.preferred_auth_level_types) - - def addPolicyURI(self, policy_uri): - """Add an acceptable authentication policy URI to this request - - This method is intended to be used by the relying party to add - acceptable authentication types to the request. - - @param policy_uri: The identifier for the preferred type of - authentication. - @see: http://openid.net/specs/openid-provider-authentication-policy-extension-1_0-05.html#auth_policies - """ - if policy_uri not in self.preferred_auth_policies: - self.preferred_auth_policies.append(policy_uri) - - def addAuthLevel(self, auth_level_uri, alias=None): - self._addAuthLevelAlias(auth_level_uri, alias) - if auth_level_uri not in self.preferred_auth_level_types: - self.preferred_auth_level_types.append(auth_level_uri) - - def getExtensionArgs(self): - """@see: C{L{Extension.getExtensionArgs}} - """ - ns_args = { - 'preferred_auth_policies':' '.join(self.preferred_auth_policies), - } - - if self.max_auth_age is not None: - ns_args['max_auth_age'] = str(self.max_auth_age) - - if self.preferred_auth_level_types: - preferred_types = [] - - for auth_level_uri in self.preferred_auth_level_types: - alias = self._getAlias(auth_level_uri) - ns_args['auth_level.ns.%s' % (alias,)] = auth_level_uri - preferred_types.append(alias) - - ns_args['preferred_auth_level_types'] = ' '.join(preferred_types) - - return ns_args - - def fromOpenIDRequest(cls, request): - """Instantiate a Request object from the arguments in a - C{checkid_*} OpenID message - """ - self = cls() - args = request.message.getArgs(self.ns_uri) - is_openid1 = request.message.isOpenID1() - - if args == {}: - return None - - self.parseExtensionArgs(args, is_openid1) - return self - - fromOpenIDRequest = classmethod(fromOpenIDRequest) - - def parseExtensionArgs(self, args, is_openid1, strict=False): - """Set the state of this request to be that expressed in these - PAPE arguments - - @param args: The PAPE arguments without a namespace - - @param strict: Whether to raise an exception if the input is - out of spec or otherwise malformed. If strict is false, - malformed input will be ignored. - - @param is_openid1: Whether the input should be treated as part - of an OpenID1 request - - @rtype: None - - @raises ValueError: When the max_auth_age is not parseable as - an integer - """ - - # preferred_auth_policies is a space-separated list of policy URIs - self.preferred_auth_policies = [] - - policies_str = args.get('preferred_auth_policies') - if policies_str: - for uri in policies_str.split(' '): - if uri not in self.preferred_auth_policies: - self.preferred_auth_policies.append(uri) - - # max_auth_age is base-10 integer number of seconds - max_auth_age_str = args.get('max_auth_age') - self.max_auth_age = None - - if max_auth_age_str: - try: - self.max_auth_age = int(max_auth_age_str) - except ValueError: - if strict: - raise - - # Parse auth level information - preferred_auth_level_types = args.get('preferred_auth_level_types') - if preferred_auth_level_types: - aliases = preferred_auth_level_types.strip().split() - - for alias in aliases: - key = 'auth_level.ns.%s' % (alias,) - try: - uri = args[key] - except KeyError: - if is_openid1: - uri = self._default_auth_level_aliases.get(alias) - else: - uri = None - - if uri is None: - if strict: - raise ValueError('preferred auth level %r is not ' - 'defined in this message' % (alias,)) - else: - self.addAuthLevel(uri, alias) - - def preferredTypes(self, supported_types): - """Given a list of authentication policy URIs that a provider - supports, this method returns the subsequence of those types - that are preferred by the relying party. - - @param supported_types: A sequence of authentication policy - type URIs that are supported by a provider - - @returns: The sub-sequence of the supported types that are - preferred by the relying party. This list will be ordered - in the order that the types appear in the supported_types - sequence, and may be empty if the provider does not prefer - any of the supported authentication types. - - @returntype: [str] - """ - return filter(self.preferred_auth_policies.__contains__, - supported_types) - -Request.ns_uri = ns_uri - - -class Response(PAPEExtension): - """A Provider Authentication Policy response, sent from a provider - to a relying party - - @ivar auth_policies: List of authentication policies conformed to - by this OpenID assertion, represented as policy URIs - """ - - ns_alias = 'pape' - - def __init__(self, auth_policies=None, auth_time=None, - auth_levels=None): - super(Response, self).__init__() - if auth_policies: - self.auth_policies = auth_policies - else: - self.auth_policies = [] - - self.auth_time = auth_time - self.auth_levels = {} - - if auth_levels is None: - auth_levels = {} - - for uri, level in auth_levels.iteritems(): - self.setAuthLevel(uri, level) - - def setAuthLevel(self, level_uri, level, alias=None): - """Set the value for the given auth level type. - - @param level: string representation of an authentication level - valid for level_uri - - @param alias: An optional namespace alias for the given auth - level URI. May be omitted if the alias is not - significant. The library will use a reasonable default for - widely-used auth level types. - """ - self._addAuthLevelAlias(level_uri, alias) - self.auth_levels[level_uri] = level - - def getAuthLevel(self, level_uri): - """Return the auth level for the specified auth level - identifier - - @returns: A string that should map to the auth levels defined - for the auth level type - - @raises KeyError: If the auth level type is not present in - this message - """ - return self.auth_levels[level_uri] - - def _getNISTAuthLevel(self): - try: - return int(self.getAuthLevel(LEVELS_NIST)) - except KeyError: - return None - - nist_auth_level = property( - _getNISTAuthLevel, - doc="Backward-compatibility accessor for the NIST auth level") - - def addPolicyURI(self, policy_uri): - """Add a authentication policy to this response - - This method is intended to be used by the provider to add a - policy that the provider conformed to when authenticating the user. - - @param policy_uri: The identifier for the preferred type of - authentication. - @see: http://openid.net/specs/openid-provider-authentication-policy-extension-1_0-01.html#auth_policies - """ - if policy_uri == AUTH_NONE: - raise RuntimeError( - 'To send no policies, do not set any on the response.') - - if policy_uri not in self.auth_policies: - self.auth_policies.append(policy_uri) - - def fromSuccessResponse(cls, success_response): - """Create a C{L{Response}} object from a successful OpenID - library response - (C{L{openid.consumer.consumer.SuccessResponse}}) response - message - - @param success_response: A SuccessResponse from consumer.complete() - @type success_response: C{L{openid.consumer.consumer.SuccessResponse}} - - @rtype: Response or None - @returns: A provider authentication policy response from the - data that was supplied with the C{id_res} response or None - if the provider sent no signed PAPE response arguments. - """ - self = cls() - - # PAPE requires that the args be signed. - args = success_response.getSignedNS(self.ns_uri) - is_openid1 = success_response.isOpenID1() - - # Only try to construct a PAPE response if the arguments were - # signed in the OpenID response. If not, return None. - if args is not None: - self.parseExtensionArgs(args, is_openid1) - return self - else: - return None - - def parseExtensionArgs(self, args, is_openid1, strict=False): - """Parse the provider authentication policy arguments into the - internal state of this object - - @param args: unqualified provider authentication policy - arguments - - @param strict: Whether to raise an exception when bad data is - encountered - - @returns: None. The data is parsed into the internal fields of - this object. - """ - policies_str = args.get('auth_policies') - if policies_str: - auth_policies = policies_str.split(' ') - elif strict: - raise ValueError('Missing auth_policies') - else: - auth_policies = [] - - if (len(auth_policies) > 1 and strict and AUTH_NONE in auth_policies): - raise ValueError('Got some auth policies, as well as the special ' - '"none" URI: %r' % (auth_policies,)) - - if 'none' in auth_policies: - msg = '"none" used as a policy URI (see PAPE draft < 5)' - if strict: - raise ValueError(msg) - else: - warnings.warn(msg, stacklevel=2) - - auth_policies = [u for u in auth_policies - if u not in ['none', AUTH_NONE]] - - self.auth_policies = auth_policies - - for (key, val) in args.iteritems(): - if key.startswith('auth_level.'): - alias = key[11:] - - # skip the already-processed namespace declarations - if alias.startswith('ns.'): - continue - - try: - uri = args['auth_level.ns.%s' % (alias,)] - except KeyError: - if is_openid1: - uri = self._default_auth_level_aliases.get(alias) - else: - uri = None - - if uri is None: - if strict: - raise ValueError( - 'Undefined auth level alias: %r' % (alias,)) - else: - self.setAuthLevel(uri, val, alias) - - auth_time = args.get('auth_time') - if auth_time: - if TIME_VALIDATOR.match(auth_time): - self.auth_time = auth_time - elif strict: - raise ValueError("auth_time must be in RFC3339 format") - - fromSuccessResponse = classmethod(fromSuccessResponse) - - def getExtensionArgs(self): - """@see: C{L{Extension.getExtensionArgs}} - """ - if len(self.auth_policies) == 0: - ns_args = { - 'auth_policies': AUTH_NONE, - } - else: - ns_args = { - 'auth_policies':' '.join(self.auth_policies), - } - - for level_type, level in self.auth_levels.iteritems(): - alias = self._getAlias(level_type) - ns_args['auth_level.ns.%s' % (alias,)] = level_type - ns_args['auth_level.%s' % (alias,)] = str(level) - - if self.auth_time is not None: - if not TIME_VALIDATOR.match(self.auth_time): - raise ValueError('auth_time must be in RFC3339 format') - - ns_args['auth_time'] = self.auth_time - - return ns_args - -Response.ns_uri = ns_uri diff --git a/askbot/deps/openid/extensions/sreg.py b/askbot/deps/openid/extensions/sreg.py deleted file mode 100644 index 7e89af47..00000000 --- a/askbot/deps/openid/extensions/sreg.py +++ /dev/null @@ -1,518 +0,0 @@ -"""Simple registration request and response parsing and object representation - -This module contains objects representing simple registration requests -and responses that can be used with both OpenID relying parties and -OpenID providers. - - 1. The relying party creates a request object and adds it to the - C{L{AuthRequest<openid.consumer.consumer.AuthRequest>}} object - before making the C{checkid_} request to the OpenID provider:: - - auth_request.addExtension(SRegRequest(required=['email'])) - - 2. The OpenID provider extracts the simple registration request from - the OpenID request using C{L{SRegRequest.fromOpenIDRequest}}, - gets the user's approval and data, creates a C{L{SRegResponse}} - object and adds it to the C{id_res} response:: - - sreg_req = SRegRequest.fromOpenIDRequest(checkid_request) - # [ get the user's approval and data, informing the user that - # the fields in sreg_response were requested ] - sreg_resp = SRegResponse.extractResponse(sreg_req, user_data) - sreg_resp.toMessage(openid_response.fields) - - 3. The relying party uses C{L{SRegResponse.fromSuccessResponse}} to - extract the data from the OpenID response:: - - sreg_resp = SRegResponse.fromSuccessResponse(success_response) - -@since: 2.0 - -@var sreg_data_fields: The names of the data fields that are listed in - the sreg spec, and a description of them in English - -@var sreg_uri: The preferred URI to use for the simple registration - namespace and XRD Type value -""" - -from askbot.deps.openid.message import registerNamespaceAlias, \ - NamespaceAliasRegistrationError -from askbot.deps.openid.extension import Extension -from askbot.deps.openid import oidutil - -try: - basestring #pylint:disable-msg=W0104 -except NameError: - # For Python 2.2 - basestring = (str, unicode) #pylint:disable-msg=W0622 - -__all__ = [ - 'SRegRequest', - 'SRegResponse', - 'data_fields', - 'ns_uri', - 'ns_uri_1_0', - 'ns_uri_1_1', - 'supportsSReg', - ] - -# The data fields that are listed in the sreg spec -data_fields = { - 'fullname':'Full Name', - 'nickname':'Nickname', - 'dob':'Date of Birth', - 'email':'E-mail Address', - 'gender':'Gender', - 'postcode':'Postal Code', - 'country':'Country', - 'language':'Language', - 'timezone':'Time Zone', - } - -def checkFieldName(field_name): - """Check to see that the given value is a valid simple - registration data field name. - - @raise ValueError: if the field name is not a valid simple - registration data field name - """ - if field_name not in data_fields: - raise ValueError('%r is not a defined simple registration field' % - (field_name,)) - -# URI used in the wild for Yadis documents advertising simple -# registration support -ns_uri_1_0 = 'http://openid.net/sreg/1.0' - -# URI in the draft specification for simple registration 1.1 -# <http://openid.net/specs/openid-simple-registration-extension-1_1-01.html> -ns_uri_1_1 = 'http://openid.net/extensions/sreg/1.1' - -# This attribute will always hold the preferred URI to use when adding -# sreg support to an XRDS file or in an OpenID namespace declaration. -ns_uri = ns_uri_1_1 - -try: - registerNamespaceAlias(ns_uri_1_1, 'sreg') -except NamespaceAliasRegistrationError, e: - oidutil.log('registerNamespaceAlias(%r, %r) failed: %s' % (ns_uri_1_1, - 'sreg', str(e),)) - -def supportsSReg(endpoint): - """Does the given endpoint advertise support for simple - registration? - - @param endpoint: The endpoint object as returned by OpenID discovery - @type endpoint: openid.consumer.discover.OpenIDEndpoint - - @returns: Whether an sreg type was advertised by the endpoint - @rtype: bool - """ - return (endpoint.usesExtension(ns_uri_1_1) or - endpoint.usesExtension(ns_uri_1_0)) - -class SRegNamespaceError(ValueError): - """The simple registration namespace was not found and could not - be created using the expected name (there's another extension - using the name 'sreg') - - This is not I{illegal}, for OpenID 2, although it probably - indicates a problem, since it's not expected that other extensions - will re-use the alias that is in use for OpenID 1. - - If this is an OpenID 1 request, then there is no recourse. This - should not happen unless some code has modified the namespaces for - the message that is being processed. - """ - -def getSRegNS(message): - """Extract the simple registration namespace URI from the given - OpenID message. Handles OpenID 1 and 2, as well as both sreg - namespace URIs found in the wild, as well as missing namespace - definitions (for OpenID 1) - - @param message: The OpenID message from which to parse simple - registration fields. This may be a request or response message. - @type message: C{L{openid.message.Message}} - - @returns: the sreg namespace URI for the supplied message. The - message may be modified to define a simple registration - namespace. - @rtype: C{str} - - @raise ValueError: when using OpenID 1 if the message defines - the 'sreg' alias to be something other than a simple - registration type. - """ - # See if there exists an alias for one of the two defined simple - # registration types. - for sreg_ns_uri in [ns_uri_1_1, ns_uri_1_0]: - alias = message.namespaces.getAlias(sreg_ns_uri) - if alias is not None: - break - else: - # There is no alias for either of the types, so try to add - # one. We default to using the modern value (1.1) - sreg_ns_uri = ns_uri_1_1 - try: - message.namespaces.addAlias(ns_uri_1_1, 'sreg') - except KeyError, why: - # An alias for the string 'sreg' already exists, but it's - # defined for something other than simple registration - raise SRegNamespaceError(why[0]) - - # we know that sreg_ns_uri defined, because it's defined in the - # else clause of the loop as well, so disable the warning - return sreg_ns_uri #pylint:disable-msg=W0631 - -class SRegRequest(Extension): - """An object to hold the state of a simple registration request. - - @ivar required: A list of the required fields in this simple - registration request - @type required: [str] - - @ivar optional: A list of the optional fields in this simple - registration request - @type optional: [str] - - @ivar policy_url: The policy URL that was provided with the request - @type policy_url: str or NoneType - - @group Consumer: requestField, requestFields, getExtensionArgs, addToOpenIDRequest - @group Server: fromOpenIDRequest, parseExtensionArgs - """ - - ns_alias = 'sreg' - - def __init__(self, required=None, optional=None, policy_url=None, - sreg_ns_uri=ns_uri): - """Initialize an empty simple registration request""" - Extension.__init__(self) - self.required = [] - self.optional = [] - self.policy_url = policy_url - self.ns_uri = sreg_ns_uri - - if required: - self.requestFields(required, required=True, strict=True) - - if optional: - self.requestFields(optional, required=False, strict=True) - - # Assign getSRegNS to a static method so that it can be - # overridden for testing. - _getSRegNS = staticmethod(getSRegNS) - - def fromOpenIDRequest(cls, request): - """Create a simple registration request that contains the - fields that were requested in the OpenID request with the - given arguments - - @param request: The OpenID request - @type request: openid.server.CheckIDRequest - - @returns: The newly created simple registration request - @rtype: C{L{SRegRequest}} - """ - self = cls() - - # Since we're going to mess with namespace URI mapping, don't - # mutate the object that was passed in. - message = request.message.copy() - - self.ns_uri = self._getSRegNS(message) - args = message.getArgs(self.ns_uri) - self.parseExtensionArgs(args) - - return self - - fromOpenIDRequest = classmethod(fromOpenIDRequest) - - def parseExtensionArgs(self, args, strict=False): - """Parse the unqualified simple registration request - parameters and add them to this object. - - This method is essentially the inverse of - C{L{getExtensionArgs}}. This method restores the serialized simple - registration request fields. - - If you are extracting arguments from a standard OpenID - checkid_* request, you probably want to use C{L{fromOpenIDRequest}}, - which will extract the sreg namespace and arguments from the - OpenID request. This method is intended for cases where the - OpenID server needs more control over how the arguments are - parsed than that method provides. - - >>> args = message.getArgs(ns_uri) - >>> request.parseExtensionArgs(args) - - @param args: The unqualified simple registration arguments - @type args: {str:str} - - @param strict: Whether requests with fields that are not - defined in the simple registration specification should be - tolerated (and ignored) - @type strict: bool - - @returns: None; updates this object - """ - for list_name in ['required', 'optional']: - required = (list_name == 'required') - items = args.get(list_name) - if items: - for field_name in items.split(','): - try: - self.requestField(field_name, required, strict) - except ValueError: - if strict: - raise - - self.policy_url = args.get('policy_url') - - def allRequestedFields(self): - """A list of all of the simple registration fields that were - requested, whether they were required or optional. - - @rtype: [str] - """ - return self.required + self.optional - - def wereFieldsRequested(self): - """Have any simple registration fields been requested? - - @rtype: bool - """ - return bool(self.allRequestedFields()) - - def __contains__(self, field_name): - """Was this field in the request?""" - return (field_name in self.required or - field_name in self.optional) - - def requestField(self, field_name, required=False, strict=False): - """Request the specified field from the OpenID user - - @param field_name: the unqualified simple registration field name - @type field_name: str - - @param required: whether the given field should be presented - to the user as being a required to successfully complete - the request - - @param strict: whether to raise an exception when a field is - added to a request more than once - - @raise ValueError: when the field requested is not a simple - registration field or strict is set and the field was - requested more than once - """ - checkFieldName(field_name) - - if strict: - if field_name in self.required or field_name in self.optional: - raise ValueError('That field has already been requested') - else: - if field_name in self.required: - return - - if field_name in self.optional: - if required: - self.optional.remove(field_name) - else: - return - - if required: - self.required.append(field_name) - else: - self.optional.append(field_name) - - def requestFields(self, field_names, required=False, strict=False): - """Add the given list of fields to the request - - @param field_names: The simple registration data fields to request - @type field_names: [str] - - @param required: Whether these values should be presented to - the user as required - - @param strict: whether to raise an exception when a field is - added to a request more than once - - @raise ValueError: when a field requested is not a simple - registration field or strict is set and a field was - requested more than once - """ - if isinstance(field_names, basestring): - raise TypeError('Fields should be passed as a list of ' - 'strings (not %r)' % (type(field_names),)) - - for field_name in field_names: - self.requestField(field_name, required, strict=strict) - - def getExtensionArgs(self): - """Get a dictionary of unqualified simple registration - arguments representing this request. - - This method is essentially the inverse of - C{L{parseExtensionArgs}}. This method serializes the simple - registration request fields. - - @rtype: {str:str} - """ - args = {} - - if self.required: - args['required'] = ','.join(self.required) - - if self.optional: - args['optional'] = ','.join(self.optional) - - if self.policy_url: - args['policy_url'] = self.policy_url - - return args - -class SRegResponse(Extension): - """Represents the data returned in a simple registration response - inside of an OpenID C{id_res} response. This object will be - created by the OpenID server, added to the C{id_res} response - object, and then extracted from the C{id_res} message by the - Consumer. - - @ivar data: The simple registration data, keyed by the unqualified - simple registration name of the field (i.e. nickname is keyed - by C{'nickname'}) - - @ivar ns_uri: The URI under which the simple registration data was - stored in the response message. - - @group Server: extractResponse - @group Consumer: fromSuccessResponse - @group Read-only dictionary interface: keys, iterkeys, items, iteritems, - __iter__, get, __getitem__, keys, has_key - """ - - ns_alias = 'sreg' - - def __init__(self, data=None, sreg_ns_uri=ns_uri): - Extension.__init__(self) - if data is None: - self.data = {} - else: - self.data = data - - self.ns_uri = sreg_ns_uri - - def extractResponse(cls, request, data): - """Take a C{L{SRegRequest}} and a dictionary of simple - registration values and create a C{L{SRegResponse}} - object containing that data. - - @param request: The simple registration request object - @type request: SRegRequest - - @param data: The simple registration data for this - response, as a dictionary from unqualified simple - registration field name to string (unicode) value. For - instance, the nickname should be stored under the key - 'nickname'. - @type data: {str:str} - - @returns: a simple registration response object - @rtype: SRegResponse - """ - self = cls() - self.ns_uri = request.ns_uri - for field in request.allRequestedFields(): - value = data.get(field) - if value is not None: - self.data[field] = value - return self - - extractResponse = classmethod(extractResponse) - - # Assign getSRegArgs to a static method so that it can be - # overridden for testing - _getSRegNS = staticmethod(getSRegNS) - - def fromSuccessResponse(cls, success_response, signed_only=True): - """Create a C{L{SRegResponse}} object from a successful OpenID - library response - (C{L{openid.consumer.consumer.SuccessResponse}}) response - message - - @param success_response: A SuccessResponse from consumer.complete() - @type success_response: C{L{openid.consumer.consumer.SuccessResponse}} - - @param signed_only: Whether to process only data that was - signed in the id_res message from the server. - @type signed_only: bool - - @rtype: SRegResponse - @returns: A simple registration response containing the data - that was supplied with the C{id_res} response. - """ - self = cls() - self.ns_uri = self._getSRegNS(success_response.message) - if signed_only: - args = success_response.getSignedNS(self.ns_uri) - else: - args = success_response.message.getArgs(self.ns_uri) - - if not args: - return None - - for field_name in data_fields: - if field_name in args: - self.data[field_name] = args[field_name] - - return self - - fromSuccessResponse = classmethod(fromSuccessResponse) - - def getExtensionArgs(self): - """Get the fields to put in the simple registration namespace - when adding them to an id_res message. - - @see: openid.extension - """ - return self.data - - # Read-only dictionary interface - def get(self, field_name, default=None): - """Like dict.get, except that it checks that the field name is - defined by the simple registration specification""" - checkFieldName(field_name) - return self.data.get(field_name, default) - - def items(self): - """All of the data values in this simple registration response - """ - return self.data.items() - - def iteritems(self): - return self.data.iteritems() - - def keys(self): - return self.data.keys() - - def iterkeys(self): - return self.data.iterkeys() - - def has_key(self, key): - return key in self - - def __contains__(self, field_name): - checkFieldName(field_name) - return field_name in self.data - - def __iter__(self): - return iter(self.data) - - def __getitem__(self, field_name): - checkFieldName(field_name) - return self.data[field_name] - - def __nonzero__(self): - return bool(self.data) diff --git a/askbot/deps/openid/fetchers.py b/askbot/deps/openid/fetchers.py deleted file mode 100644 index 11ab47fb..00000000 --- a/askbot/deps/openid/fetchers.py +++ /dev/null @@ -1,427 +0,0 @@ -# -*- test-case-name: openid.test.test_fetchers -*- -""" -This module contains the HTTP fetcher interface and several implementations. -""" - -__all__ = ['fetch', 'getDefaultFetcher', 'setDefaultFetcher', 'HTTPResponse', - 'HTTPFetcher', 'createHTTPFetcher', 'HTTPFetchingError', - 'HTTPError'] - -import urllib2 -import time -import cStringIO -import sys - -from askbot.deps import openid -import askbot.deps.openid.urinorm - -# Try to import httplib2 for caching support -# http://bitworking.org/projects/httplib2/ -try: - import httplib2 -except ImportError: - # httplib2 not available - httplib2 = None - -# try to import pycurl, which will let us use CurlHTTPFetcher -try: - import pycurl -except ImportError: - pycurl = None - -USER_AGENT = "python-openid/%s (%s)" % (openid.__version__, sys.platform) -MAX_RESPONSE_KB = 1024 - -def fetch(url, body=None, headers=None): - """Invoke the fetch method on the default fetcher. Most users - should need only this method. - - @raises Exception: any exceptions that may be raised by the default fetcher - """ - fetcher = getDefaultFetcher() - return fetcher.fetch(url, body, headers) - -def createHTTPFetcher(): - """Create a default HTTP fetcher instance - - prefers Curl to urllib2.""" - if pycurl is None: - fetcher = Urllib2Fetcher() - else: - fetcher = CurlHTTPFetcher() - - return fetcher - -# Contains the currently set HTTP fetcher. If it is set to None, the -# library will call createHTTPFetcher() to set it. Do not access this -# variable outside of this module. -_default_fetcher = None - -def getDefaultFetcher(): - """Return the default fetcher instance - if no fetcher has been set, it will create a default fetcher. - - @return: the default fetcher - @rtype: HTTPFetcher - """ - global _default_fetcher - - if _default_fetcher is None: - setDefaultFetcher(createHTTPFetcher()) - - return _default_fetcher - -def setDefaultFetcher(fetcher, wrap_exceptions=True): - """Set the default fetcher - - @param fetcher: The fetcher to use as the default HTTP fetcher - @type fetcher: HTTPFetcher - - @param wrap_exceptions: Whether to wrap exceptions thrown by the - fetcher wil HTTPFetchingError so that they may be caught - easier. By default, exceptions will be wrapped. In general, - unwrapped fetchers are useful for debugging of fetching errors - or if your fetcher raises well-known exceptions that you would - like to catch. - @type wrap_exceptions: bool - """ - global _default_fetcher - if fetcher is None or not wrap_exceptions: - _default_fetcher = fetcher - else: - _default_fetcher = ExceptionWrappingFetcher(fetcher) - -def usingCurl(): - """Whether the currently set HTTP fetcher is a Curl HTTP fetcher.""" - return isinstance(getDefaultFetcher(), CurlHTTPFetcher) - -class HTTPResponse(object): - """XXX document attributes""" - headers = None - status = None - body = None - final_url = None - - def __init__(self, final_url=None, status=None, headers=None, body=None): - self.final_url = final_url - self.status = status - self.headers = headers - self.body = body - - def __repr__(self): - return "<%s status %s for %s>" % (self.__class__.__name__, - self.status, - self.final_url) - -class HTTPFetcher(object): - """ - This class is the interface for openid HTTP fetchers. This - interface is only important if you need to write a new fetcher for - some reason. - """ - - def fetch(self, url, body=None, headers=None): - """ - This performs an HTTP POST or GET, following redirects along - the way. If a body is specified, then the request will be a - POST. Otherwise, it will be a GET. - - - @param headers: HTTP headers to include with the request - @type headers: {str:str} - - @return: An object representing the server's HTTP response. If - there are network or protocol errors, an exception will be - raised. HTTP error responses, like 404 or 500, do not - cause exceptions. - - @rtype: L{HTTPResponse} - - @raise Exception: Different implementations will raise - different errors based on the underlying HTTP library. - """ - raise NotImplementedError - -def _allowedURL(url): - return url.startswith('http://') or url.startswith('https://') - -class HTTPFetchingError(Exception): - """Exception that is wrapped around all exceptions that are raised - by the underlying fetcher when using the ExceptionWrappingFetcher - - @ivar why: The exception that caused this exception - """ - def __init__(self, why=None): - Exception.__init__(self, why) - self.why = why - -class ExceptionWrappingFetcher(HTTPFetcher): - """Fetcher that wraps another fetcher, causing all exceptions - - @cvar uncaught_exceptions: Exceptions that should be exposed to the - user if they are raised by the fetch call - """ - - uncaught_exceptions = (SystemExit, KeyboardInterrupt, MemoryError) - - def __init__(self, fetcher): - self.fetcher = fetcher - - def fetch(self, *args, **kwargs): - try: - return self.fetcher.fetch(*args, **kwargs) - except self.uncaught_exceptions: - raise - except: - exc_cls, exc_inst = sys.exc_info()[:2] - if exc_inst is None: - # string exceptions - exc_inst = exc_cls - - raise HTTPFetchingError(why=exc_inst) - -class Urllib2Fetcher(HTTPFetcher): - """An C{L{HTTPFetcher}} that uses urllib2. - """ - - # Parameterized for the benefit of testing frameworks, see - # http://trac.openidenabled.com/trac/ticket/85 - urlopen = staticmethod(urllib2.urlopen) - - def fetch(self, url, body=None, headers=None): - if not _allowedURL(url): - raise ValueError('Bad URL scheme: %r' % (url,)) - - if headers is None: - headers = {} - - headers.setdefault( - 'User-Agent', - "%s Python-urllib/%s" % (USER_AGENT, urllib2.__version__,)) - - req = urllib2.Request(url, data=body, headers=headers) - try: - f = self.urlopen(req) - try: - return self._makeResponse(f) - finally: - f.close() - except urllib2.HTTPError, why: - try: - return self._makeResponse(why) - finally: - why.close() - - def _makeResponse(self, urllib2_response): - resp = HTTPResponse() - resp.body = urllib2_response.read(MAX_RESPONSE_KB * 1024) - resp.final_url = urllib2_response.geturl() - resp.headers = dict(urllib2_response.info().items()) - - if hasattr(urllib2_response, 'code'): - resp.status = urllib2_response.code - else: - resp.status = 200 - - return resp - -class HTTPError(HTTPFetchingError): - """ - This exception is raised by the C{L{CurlHTTPFetcher}} when it - encounters an exceptional situation fetching a URL. - """ - pass - -# XXX: define what we mean by paranoid, and make sure it is. -class CurlHTTPFetcher(HTTPFetcher): - """ - An C{L{HTTPFetcher}} that uses pycurl for fetching. - See U{http://pycurl.sourceforge.net/}. - """ - ALLOWED_TIME = 20 # seconds - - def __init__(self): - HTTPFetcher.__init__(self) - if pycurl is None: - raise RuntimeError('Cannot find pycurl library') - - def _parseHeaders(self, header_file): - header_file.seek(0) - - # Remove the status line from the beginning of the input - unused_http_status_line = header_file.readline().lower () - if unused_http_status_line.startswith('http/1.1 100 '): - unused_http_status_line = header_file.readline() - unused_http_status_line = header_file.readline() - - lines = [line.strip() for line in header_file] - - # and the blank line from the end - empty_line = lines.pop() - if empty_line: - raise HTTPError("No blank line at end of headers: %r" % (line,)) - - headers = {} - for line in lines: - try: - name, value = line.split(':', 1) - except ValueError: - raise HTTPError( - "Malformed HTTP header line in response: %r" % (line,)) - - value = value.strip() - - # HTTP headers are case-insensitive - name = name.lower() - headers[name] = value - - return headers - - def _checkURL(self, url): - # XXX: document that this can be overridden to match desired policy - # XXX: make sure url is well-formed and routeable - return _allowedURL(url) - - def fetch(self, url, body=None, headers=None): - stop = int(time.time()) + self.ALLOWED_TIME - off = self.ALLOWED_TIME - - if headers is None: - headers = {} - - headers.setdefault('User-Agent', - "%s %s" % (USER_AGENT, pycurl.version,)) - - header_list = [] - if headers is not None: - for header_name, header_value in headers.iteritems(): - header_list.append('%s: %s' % (header_name, header_value)) - - c = pycurl.Curl() - try: - c.setopt(pycurl.NOSIGNAL, 1) - - if header_list: - c.setopt(pycurl.HTTPHEADER, header_list) - - # Presence of a body indicates that we should do a POST - if body is not None: - c.setopt(pycurl.POST, 1) - c.setopt(pycurl.POSTFIELDS, body) - - while off > 0: - if not self._checkURL(url): - raise HTTPError("Fetching URL not allowed: %r" % (url,)) - - data = cStringIO.StringIO() - def write_data(chunk): - if data.tell() > 1024*MAX_RESPONSE_KB: - return 0 - else: - return data.write(chunk) - - response_header_data = cStringIO.StringIO() - c.setopt(pycurl.WRITEFUNCTION, write_data) - c.setopt(pycurl.HEADERFUNCTION, response_header_data.write) - c.setopt(pycurl.TIMEOUT, off) - c.setopt(pycurl.URL, askbot.deps.openid.urinorm.urinorm(url)) - - c.perform() - - response_headers = self._parseHeaders(response_header_data) - code = c.getinfo(pycurl.RESPONSE_CODE) - if code in [301, 302, 303, 307]: - url = response_headers.get('location') - if url is None: - raise HTTPError( - 'Redirect (%s) returned without a location' % code) - - # Redirects are always GETs - c.setopt(pycurl.POST, 0) - - # There is no way to reset POSTFIELDS to empty and - # reuse the connection, but we only use it once. - else: - resp = HTTPResponse() - resp.headers = response_headers - resp.status = code - resp.final_url = url - resp.body = data.getvalue() - return resp - - off = stop - int(time.time()) - - raise HTTPError("Timed out fetching: %r" % (url,)) - finally: - c.close() - -class HTTPLib2Fetcher(HTTPFetcher): - """A fetcher that uses C{httplib2} for performing HTTP - requests. This implementation supports HTTP caching. - - @see: http://bitworking.org/projects/httplib2/ - """ - - def __init__(self, cache=None): - """@param cache: An object suitable for use as an C{httplib2} - cache. If a string is passed, it is assumed to be a - directory name. - """ - if httplib2 is None: - raise RuntimeError('Cannot find httplib2 library. ' - 'See http://bitworking.org/projects/httplib2/') - - super(HTTPLib2Fetcher, self).__init__() - - # An instance of the httplib2 object that performs HTTP requests - self.httplib2 = httplib2.Http(cache) - - # We want httplib2 to raise exceptions for errors, just like - # the other fetchers. - self.httplib2.force_exception_to_status_code = False - - def fetch(self, url, body=None, headers=None): - """Perform an HTTP request - - @raises Exception: Any exception that can be raised by httplib2 - - @see: C{L{HTTPFetcher.fetch}} - """ - if body: - method = 'POST' - else: - method = 'GET' - - if headers is None: - headers = {} - - # httplib2 doesn't check to make sure that the URL's scheme is - # 'http' so we do it here. - if not (url.startswith('http://') or url.startswith('https://')): - raise ValueError('URL is not a HTTP URL: %r' % (url,)) - - httplib2_response, content = self.httplib2.request( - url, method, body=body, headers=headers) - - # Translate the httplib2 response to our HTTP response abstraction - - # When a 400 is returned, there is no "content-location" - # header set. This seems like a bug to me. I can't think of a - # case where we really care about the final URL when it is an - # error response, but being careful about it can't hurt. - try: - final_url = httplib2_response['content-location'] - except KeyError: - # We're assuming that no redirects occurred - assert not httplib2_response.previous - - # And this should never happen for a successful response - assert httplib2_response.status != 200 - final_url = url - - return HTTPResponse( - body=content, - final_url=final_url, - headers=dict(httplib2_response.items()), - status=httplib2_response.status, - ) diff --git a/askbot/deps/openid/kvform.py b/askbot/deps/openid/kvform.py deleted file mode 100644 index 81d429ea..00000000 --- a/askbot/deps/openid/kvform.py +++ /dev/null @@ -1,123 +0,0 @@ -__all__ = ['seqToKV', 'kvToSeq', 'dictToKV', 'kvToDict'] - -from askbot.deps.openid import oidutil - -import types - -class KVFormError(ValueError): - pass - -def seqToKV(seq, strict=False): - """Represent a sequence of pairs of strings as newline-terminated - key:value pairs. The pairs are generated in the order given. - - @param seq: The pairs - @type seq: [(str, (unicode|str))] - - @return: A string representation of the sequence - @rtype: str - """ - def err(msg): - formatted = 'seqToKV warning: %s: %r' % (msg, seq) - if strict: - raise KVFormError(formatted) - else: - oidutil.log(formatted) - - lines = [] - for k, v in seq: - if isinstance(k, types.StringType): - k = k.decode('UTF8') - elif not isinstance(k, types.UnicodeType): - err('Converting key to string: %r' % k) - k = str(k) - - if '\n' in k: - raise KVFormError( - 'Invalid input for seqToKV: key contains newline: %r' % (k,)) - - if ':' in k: - raise KVFormError( - 'Invalid input for seqToKV: key contains colon: %r' % (k,)) - - if k.strip() != k: - err('Key has whitespace at beginning or end: %r' % (k,)) - - if isinstance(v, types.StringType): - v = v.decode('UTF8') - elif not isinstance(v, types.UnicodeType): - err('Converting value to string: %r' % (v,)) - v = str(v) - - if '\n' in v: - raise KVFormError( - 'Invalid input for seqToKV: value contains newline: %r' % (v,)) - - if v.strip() != v: - err('Value has whitespace at beginning or end: %r' % (v,)) - - lines.append(k + ':' + v + '\n') - - return ''.join(lines).encode('UTF8') - -def kvToSeq(data, strict=False): - """ - - After one parse, seqToKV and kvToSeq are inverses, with no warnings:: - - seq = kvToSeq(s) - seqToKV(kvToSeq(seq)) == seq - """ - def err(msg): - formatted = 'kvToSeq warning: %s: %r' % (msg, data) - if strict: - raise KVFormError(formatted) - else: - oidutil.log(formatted) - - lines = data.split('\n') - if lines[-1]: - err('Does not end in a newline') - else: - del lines[-1] - - pairs = [] - line_num = 0 - for line in lines: - line_num += 1 - - # Ignore blank lines - if not line.strip(): - continue - - pair = line.split(':', 1) - if len(pair) == 2: - k, v = pair - k_s = k.strip() - if k_s != k: - fmt = ('In line %d, ignoring leading or trailing ' - 'whitespace in key %r') - err(fmt % (line_num, k)) - - if not k_s: - err('In line %d, got empty key' % (line_num,)) - - v_s = v.strip() - if v_s != v: - fmt = ('In line %d, ignoring leading or trailing ' - 'whitespace in value %r') - err(fmt % (line_num, v)) - - pairs.append((k_s.decode('UTF8'), v_s.decode('UTF8'))) - else: - err('Line %d does not contain a colon' % line_num) - - return pairs - -def dictToKV(d): - seq = d.items() - seq.sort() - return seqToKV(seq) - -def kvToDict(s): - return dict(kvToSeq(s)) diff --git a/askbot/deps/openid/message.py b/askbot/deps/openid/message.py deleted file mode 100644 index 1ba72f06..00000000 --- a/askbot/deps/openid/message.py +++ /dev/null @@ -1,631 +0,0 @@ -"""Extension argument processing code -""" -__all__ = ['Message', 'NamespaceMap', 'no_default', 'registerNamespaceAlias', - 'OPENID_NS', 'BARE_NS', 'OPENID1_NS', 'OPENID2_NS', 'SREG_URI', - 'IDENTIFIER_SELECT'] - -import copy -import warnings -import urllib - -from askbot.deps.openid import oidutil -from askbot.deps.openid import kvform -try: - ElementTree = oidutil.importElementTree() -except ImportError: - # No elementtree found, so give up, but don't fail to import, - # since we have fallbacks. - ElementTree = None - -# This doesn't REALLY belong here, but where is better? -IDENTIFIER_SELECT = 'http://specs.openid.net/auth/2.0/identifier_select' - -# URI for Simple Registration extension, the only commonly deployed -# OpenID 1.x extension, and so a special case -SREG_URI = 'http://openid.net/sreg/1.0' - -# The OpenID 1.X namespace URI -OPENID1_NS = 'http://openid.net/signon/1.0' -THE_OTHER_OPENID1_NS = 'http://openid.net/signon/1.1' - -OPENID1_NAMESPACES = OPENID1_NS, THE_OTHER_OPENID1_NS - -# The OpenID 2.0 namespace URI -OPENID2_NS = 'http://specs.openid.net/auth/2.0' - -# The namespace consisting of pairs with keys that are prefixed with -# "openid." but not in another namespace. -NULL_NAMESPACE = oidutil.Symbol('Null namespace') - -# The null namespace, when it is an allowed OpenID namespace -OPENID_NS = oidutil.Symbol('OpenID namespace') - -# The top-level namespace, excluding all pairs with keys that start -# with "openid." -BARE_NS = oidutil.Symbol('Bare namespace') - -# Limit, in bytes, of identity provider and return_to URLs, including -# response payload. See OpenID 1.1 specification, Appendix D. -OPENID1_URL_LIMIT = 2047 - -# All OpenID protocol fields. Used to check namespace aliases. -OPENID_PROTOCOL_FIELDS = [ - 'ns', 'mode', 'error', 'return_to', 'contact', 'reference', - 'signed', 'assoc_type', 'session_type', 'dh_modulus', 'dh_gen', - 'dh_consumer_public', 'claimed_id', 'identity', 'realm', - 'invalidate_handle', 'op_endpoint', 'response_nonce', 'sig', - 'assoc_handle', 'trust_root', 'openid', - ] - -class UndefinedOpenIDNamespace(ValueError): - """Raised if the generic OpenID namespace is accessed when there - is no OpenID namespace set for this message.""" - -class InvalidOpenIDNamespace(ValueError): - """Raised if openid.ns is not a recognized value. - - For recognized values, see L{Message.allowed_openid_namespaces} - """ - def __str__(self): - s = "Invalid OpenID Namespace" - if self.args: - s += " %r" % (self.args[0],) - return s - - -# Sentinel used for Message implementation to indicate that getArg -# should raise an exception instead of returning a default. -no_default = object() - -# Global namespace / alias registration map. See -# registerNamespaceAlias. -registered_aliases = {} - -class NamespaceAliasRegistrationError(Exception): - """ - Raised when an alias or namespace URI has already been registered. - """ - pass - -def registerNamespaceAlias(namespace_uri, alias): - """ - Registers a (namespace URI, alias) mapping in a global namespace - alias map. Raises NamespaceAliasRegistrationError if either the - namespace URI or alias has already been registered with a - different value. This function is required if you want to use a - namespace with an OpenID 1 message. - """ - global registered_aliases - - if registered_aliases.get(alias) == namespace_uri: - return - - if namespace_uri in registered_aliases.values(): - raise NamespaceAliasRegistrationError, \ - 'Namespace uri %r already registered' % (namespace_uri,) - - if alias in registered_aliases: - raise NamespaceAliasRegistrationError, \ - 'Alias %r already registered' % (alias,) - - registered_aliases[alias] = namespace_uri - -class Message(object): - """ - In the implementation of this object, None represents the global - namespace as well as a namespace with no key. - - @cvar namespaces: A dictionary specifying specific - namespace-URI to alias mappings that should be used when - generating namespace aliases. - - @ivar ns_args: two-level dictionary of the values in this message, - grouped by namespace URI. The first level is the namespace - URI. - """ - - allowed_openid_namespaces = [OPENID1_NS, THE_OTHER_OPENID1_NS, OPENID2_NS] - - def __init__(self, openid_namespace=None): - """Create an empty Message. - - @raises InvalidOpenIDNamespace: if openid_namespace is not in - L{Message.allowed_openid_namespaces} - """ - self.args = {} - self.namespaces = NamespaceMap() - if openid_namespace is None: - self._openid_ns_uri = None - else: - implicit = openid_namespace in OPENID1_NAMESPACES - self.setOpenIDNamespace(openid_namespace, implicit) - - def fromPostArgs(cls, args): - """Construct a Message containing a set of POST arguments. - - """ - self = cls() - - # Partition into "openid." args and bare args - openid_args = {} - for key, value in args.items(): - if isinstance(value, list): - raise TypeError("query dict must have one value for each key, " - "not lists of values. Query is %r" % (args,)) - - - try: - prefix, rest = key.split('.', 1) - except ValueError: - prefix = None - - if prefix != 'openid': - self.args[(BARE_NS, key)] = value - else: - openid_args[rest] = value - - self._fromOpenIDArgs(openid_args) - - return self - - fromPostArgs = classmethod(fromPostArgs) - - def fromOpenIDArgs(cls, openid_args): - """Construct a Message from a parsed KVForm message. - - @raises InvalidOpenIDNamespace: if openid.ns is not in - L{Message.allowed_openid_namespaces} - """ - self = cls() - self._fromOpenIDArgs(openid_args) - return self - - fromOpenIDArgs = classmethod(fromOpenIDArgs) - - def _fromOpenIDArgs(self, openid_args): - ns_args = [] - - # Resolve namespaces - for rest, value in openid_args.iteritems(): - try: - ns_alias, ns_key = rest.split('.', 1) - except ValueError: - ns_alias = NULL_NAMESPACE - ns_key = rest - - if ns_alias == 'ns': - self.namespaces.addAlias(value, ns_key) - elif ns_alias == NULL_NAMESPACE and ns_key == 'ns': - # null namespace - self.setOpenIDNamespace(value, False) - else: - ns_args.append((ns_alias, ns_key, value)) - - # Implicitly set an OpenID namespace definition (OpenID 1) - if not self.getOpenIDNamespace(): - self.setOpenIDNamespace(OPENID1_NS, True) - - # Actually put the pairs into the appropriate namespaces - for (ns_alias, ns_key, value) in ns_args: - ns_uri = self.namespaces.getNamespaceURI(ns_alias) - if ns_uri is None: - # we found a namespaced arg without a namespace URI defined - ns_uri = self._getDefaultNamespace(ns_alias) - if ns_uri is None: - ns_uri = self.getOpenIDNamespace() - ns_key = '%s.%s' % (ns_alias, ns_key) - else: - self.namespaces.addAlias(ns_uri, ns_alias, implicit=True) - - self.setArg(ns_uri, ns_key, value) - - def _getDefaultNamespace(self, mystery_alias): - """OpenID 1 compatibility: look for a default namespace URI to - use for this alias.""" - global registered_aliases - # Only try to map an alias to a default if it's an - # OpenID 1.x message. - if self.isOpenID1(): - return registered_aliases.get(mystery_alias) - else: - return None - - def setOpenIDNamespace(self, openid_ns_uri, implicit): - """Set the OpenID namespace URI used in this message. - - @raises InvalidOpenIDNamespace: if the namespace is not in - L{Message.allowed_openid_namespaces} - """ - if openid_ns_uri not in self.allowed_openid_namespaces: - raise InvalidOpenIDNamespace(openid_ns_uri) - - self.namespaces.addAlias(openid_ns_uri, NULL_NAMESPACE, implicit) - self._openid_ns_uri = openid_ns_uri - - def getOpenIDNamespace(self): - return self._openid_ns_uri - - def isOpenID1(self): - return self.getOpenIDNamespace() in OPENID1_NAMESPACES - - def isOpenID2(self): - return self.getOpenIDNamespace() == OPENID2_NS - - def fromKVForm(cls, kvform_string): - """Create a Message from a KVForm string""" - return cls.fromOpenIDArgs(kvform.kvToDict(kvform_string)) - - fromKVForm = classmethod(fromKVForm) - - def copy(self): - return copy.deepcopy(self) - - def toPostArgs(self): - """Return all arguments with openid. in front of namespaced arguments. - """ - args = {} - - # Add namespace definitions to the output - for ns_uri, alias in self.namespaces.iteritems(): - if self.namespaces.isImplicit(ns_uri): - continue - if alias == NULL_NAMESPACE: - ns_key = 'openid.ns' - else: - ns_key = 'openid.ns.' + alias - args[ns_key] = ns_uri - - for (ns_uri, ns_key), value in self.args.iteritems(): - key = self.getKey(ns_uri, ns_key) - args[key] = value.encode('UTF-8') - - return args - - def toArgs(self): - """Return all namespaced arguments, failing if any - non-namespaced arguments exist.""" - # FIXME - undocumented exception - post_args = self.toPostArgs() - kvargs = {} - for k, v in post_args.iteritems(): - if not k.startswith('openid.'): - raise ValueError( - 'This message can only be encoded as a POST, because it ' - 'contains arguments that are not prefixed with "openid."') - else: - kvargs[k[7:]] = v - - return kvargs - - def toFormMarkup(self, action_url, form_tag_attrs=None, - submit_text="Continue"): - """Generate HTML form markup that contains the values in this - message, to be HTTP POSTed as x-www-form-urlencoded UTF-8. - - @param action_url: The URL to which the form will be POSTed - @type action_url: str - - @param form_tag_attrs: Dictionary of attributes to be added to - the form tag. 'accept-charset' and 'enctype' have defaults - that can be overridden. If a value is supplied for - 'action' or 'method', it will be replaced. - @type form_tag_attrs: {unicode: unicode} - - @param submit_text: The text that will appear on the submit - button for this form. - @type submit_text: unicode - - @returns: A string containing (X)HTML markup for a form that - encodes the values in this Message object. - @rtype: str or unicode - """ - if ElementTree is None: - raise RuntimeError('This function requires ElementTree.') - - assert action_url is not None - - form = ElementTree.Element('form') - - if form_tag_attrs: - for name, attr in form_tag_attrs.iteritems(): - form.attrib[name] = attr - - form.attrib['action'] = action_url - form.attrib['method'] = 'post' - form.attrib['accept-charset'] = 'UTF-8' - form.attrib['enctype'] = 'application/x-www-form-urlencoded' - - for name, value in self.toPostArgs().iteritems(): - attrs = {'type': 'hidden', - 'name': name, - 'value': value} - form.append(ElementTree.Element('input', attrs)) - - submit = ElementTree.Element( - 'input', {'type':'submit', 'value':submit_text}) - form.append(submit) - - return ElementTree.tostring(form) - - def toURL(self, base_url): - """Generate a GET URL with the parameters in this message - attached as query parameters.""" - return oidutil.appendArgs(base_url, self.toPostArgs()) - - def toKVForm(self): - """Generate a KVForm string that contains the parameters in - this message. This will fail if the message contains arguments - outside of the 'openid.' prefix. - """ - return kvform.dictToKV(self.toArgs()) - - def toURLEncoded(self): - """Generate an x-www-urlencoded string""" - args = self.toPostArgs().items() - args.sort() - return urllib.urlencode(args) - - def _fixNS(self, namespace): - """Convert an input value into the internally used values of - this object - - @param namespace: The string or constant to convert - @type namespace: str or unicode or BARE_NS or OPENID_NS - """ - if namespace == OPENID_NS: - if self._openid_ns_uri is None: - raise UndefinedOpenIDNamespace('OpenID namespace not set') - else: - namespace = self._openid_ns_uri - - if namespace != BARE_NS and type(namespace) not in [str, unicode]: - raise TypeError( - "Namespace must be BARE_NS, OPENID_NS or a string. got %r" - % (namespace,)) - - if namespace != BARE_NS and ':' not in namespace: - fmt = 'OpenID 2.0 namespace identifiers SHOULD be URIs. Got %r' - warnings.warn(fmt % (namespace,), DeprecationWarning) - - if namespace == 'sreg': - fmt = 'Using %r instead of "sreg" as namespace' - warnings.warn(fmt % (SREG_URI,), DeprecationWarning,) - return SREG_URI - - return namespace - - def hasKey(self, namespace, ns_key): - namespace = self._fixNS(namespace) - return (namespace, ns_key) in self.args - - def getKey(self, namespace, ns_key): - """Get the key for a particular namespaced argument""" - namespace = self._fixNS(namespace) - if namespace == BARE_NS: - return ns_key - - ns_alias = self.namespaces.getAlias(namespace) - - # No alias is defined, so no key can exist - if ns_alias is None: - return None - - if ns_alias == NULL_NAMESPACE: - tail = ns_key - else: - tail = '%s.%s' % (ns_alias, ns_key) - - return 'openid.' + tail - - def getArg(self, namespace, key, default=None): - """Get a value for a namespaced key. - - @param namespace: The namespace in the message for this key - @type namespace: str - - @param key: The key to get within this namespace - @type key: str - - @param default: The value to use if this key is absent from - this message. Using the special value - openid.message.no_default will result in this method - raising a KeyError instead of returning the default. - - @rtype: str or the type of default - @raises KeyError: if default is no_default - @raises UndefinedOpenIDNamespace: if the message has not yet - had an OpenID namespace set - """ - namespace = self._fixNS(namespace) - args_key = (namespace, key) - try: - return self.args[args_key] - except KeyError: - if default is no_default: - raise KeyError((namespace, key)) - else: - return default - - def getArgs(self, namespace): - """Get the arguments that are defined for this namespace URI - - @returns: mapping from namespaced keys to values - @returntype: dict - """ - namespace = self._fixNS(namespace) - return dict([ - (ns_key, value) - for ((pair_ns, ns_key), value) - in self.args.iteritems() - if pair_ns == namespace - ]) - - def updateArgs(self, namespace, updates): - """Set multiple key/value pairs in one call - - @param updates: The values to set - @type updates: {unicode:unicode} - """ - namespace = self._fixNS(namespace) - for k, v in updates.iteritems(): - self.setArg(namespace, k, v) - - def setArg(self, namespace, key, value): - """Set a single argument in this namespace""" - assert key is not None - assert value is not None - namespace = self._fixNS(namespace) - self.args[(namespace, key)] = value - if not (namespace is BARE_NS): - self.namespaces.add(namespace) - - def delArg(self, namespace, key): - namespace = self._fixNS(namespace) - del self.args[(namespace, key)] - - def __repr__(self): - return "<%s.%s %r>" % (self.__class__.__module__, - self.__class__.__name__, - self.args) - - def __eq__(self, other): - return self.args == other.args - - - def __ne__(self, other): - return not (self == other) - - - def getAliasedArg(self, aliased_key, default=None): - if aliased_key == 'ns': - return self.getOpenIDNamespace() - - if aliased_key.startswith('ns.'): - uri = self.namespaces.getNamespaceURI(aliased_key[3:]) - if uri is None: - if default == no_default: - raise KeyError - else: - return default - else: - return uri - - try: - alias, key = aliased_key.split('.', 1) - except ValueError: - # need more than x values to unpack - ns = None - else: - ns = self.namespaces.getNamespaceURI(alias) - - if ns is None: - key = aliased_key - ns = self.getOpenIDNamespace() - - return self.getArg(ns, key, default) - -class NamespaceMap(object): - """Maintains a bijective map between namespace uris and aliases. - """ - def __init__(self): - self.alias_to_namespace = {} - self.namespace_to_alias = {} - self.implicit_namespaces = [] - - def getAlias(self, namespace_uri): - return self.namespace_to_alias.get(namespace_uri) - - def getNamespaceURI(self, alias): - return self.alias_to_namespace.get(alias) - - def iterNamespaceURIs(self): - """Return an iterator over the namespace URIs""" - return iter(self.namespace_to_alias) - - def iterAliases(self): - """Return an iterator over the aliases""" - return iter(self.alias_to_namespace) - - def iteritems(self): - """Iterate over the mapping - - @returns: iterator of (namespace_uri, alias) - """ - return self.namespace_to_alias.iteritems() - - def addAlias(self, namespace_uri, desired_alias, implicit=False): - """Add an alias from this namespace URI to the desired alias - """ - # Check that desired_alias is not an openid protocol field as - # per the spec. - assert desired_alias not in OPENID_PROTOCOL_FIELDS, \ - "%r is not an allowed namespace alias" % (desired_alias,) - - # Check that desired_alias does not contain a period as per - # the spec. - if type(desired_alias) in [str, unicode]: - assert '.' not in desired_alias, \ - "%r must not contain a dot" % (desired_alias,) - - # Check that there is not a namespace already defined for - # the desired alias - current_namespace_uri = self.alias_to_namespace.get(desired_alias) - if (current_namespace_uri is not None - and current_namespace_uri != namespace_uri): - - fmt = ('Cannot map %r to alias %r. ' - '%r is already mapped to alias %r') - - msg = fmt % ( - namespace_uri, - desired_alias, - current_namespace_uri, - desired_alias) - raise KeyError(msg) - - # Check that there is not already a (different) alias for - # this namespace URI - alias = self.namespace_to_alias.get(namespace_uri) - if alias is not None and alias != desired_alias: - fmt = ('Cannot map %r to alias %r. ' - 'It is already mapped to alias %r') - raise KeyError(fmt % (namespace_uri, desired_alias, alias)) - - assert (desired_alias == NULL_NAMESPACE or - type(desired_alias) in [str, unicode]), repr(desired_alias) - assert namespace_uri not in self.implicit_namespaces - self.alias_to_namespace[desired_alias] = namespace_uri - self.namespace_to_alias[namespace_uri] = desired_alias - if implicit: - self.implicit_namespaces.append(namespace_uri) - return desired_alias - - def add(self, namespace_uri): - """Add this namespace URI to the mapping, without caring what - alias it ends up with""" - # See if this namespace is already mapped to an alias - alias = self.namespace_to_alias.get(namespace_uri) - if alias is not None: - return alias - - # Fall back to generating a numerical alias - i = 0 - while True: - alias = 'ext' + str(i) - try: - self.addAlias(namespace_uri, alias) - except KeyError: - i += 1 - else: - return alias - - assert False, "Not reached" - - def isDefined(self, namespace_uri): - return namespace_uri in self.namespace_to_alias - - def __contains__(self, namespace_uri): - return self.isDefined(namespace_uri) - - def isImplicit(self, namespace_uri): - return namespace_uri in self.implicit_namespaces diff --git a/askbot/deps/openid/oidutil.py b/askbot/deps/openid/oidutil.py deleted file mode 100644 index 593fdcf9..00000000 --- a/askbot/deps/openid/oidutil.py +++ /dev/null @@ -1,190 +0,0 @@ -"""This module contains general utility code that is used throughout -the library. - -For users of this library, the C{L{log}} function is probably the most -interesting. -""" - -__all__ = ['log', 'appendArgs', 'toBase64', 'fromBase64', 'autoSubmitHTML'] - -import binascii -import sys -import urlparse - -from urllib import urlencode - -elementtree_modules = [ - 'lxml.etree', - 'xml.etree.cElementTree', - 'xml.etree.ElementTree', - 'cElementTree', - 'elementtree.ElementTree', - ] - -def autoSubmitHTML(form, title='OpenID transaction in progress'): - return """ -<html> -<head> - <title>%s</title> -</head> -<body onload="document.forms[0].submit();"> -%s -<script> -var elements = document.forms[0].elements; -for (var i = 0; i < elements.length; i++) { - elements[i].style.display = "none"; -} -</script> -</body> -</html> -""" % (title, form) - -def importElementTree(module_names=None): - """Find a working ElementTree implementation, trying the standard - places that such a thing might show up. - - >>> ElementTree = importElementTree() - - @param module_names: The names of modules to try to use as - ElementTree. Defaults to C{L{elementtree_modules}} - - @returns: An ElementTree module - """ - if module_names is None: - module_names = elementtree_modules - - for mod_name in module_names: - try: - ElementTree = __import__(mod_name, None, None, ['unused']) - except ImportError: - pass - else: - # Make sure it can actually parse XML - try: - ElementTree.XML('<unused/>') - except (SystemExit, MemoryError, AssertionError): - raise - except: - why = sys.exc_info()[1] - log('Not using ElementTree library %r because it failed to ' - 'parse a trivial document: %s' % (mod_name, why)) - else: - return ElementTree - else: - raise ImportError('No ElementTree library found. ' - 'You may need to install one. ' - 'Tried importing %r' % (module_names,) - ) - -def log(message, level=0): - """Handle a log message from the OpenID library. - - This implementation writes the string it to C{sys.stderr}, - followed by a newline. - - Currently, the library does not use the second parameter to this - function, but that may change in the future. - - To install your own logging hook:: - - from askbot.deps.openid import oidutil - - def myLoggingFunction(message, level): - ... - - oidutil.log = myLoggingFunction - - @param message: A string containing a debugging message from the - OpenID library - @type message: str - - @param level: The severity of the log message. This parameter is - currently unused, but in the future, the library may indicate - more important information with a higher level value. - @type level: int or None - - @returns: Nothing. - """ - - sys.stderr.write(message) - sys.stderr.write('\n') - -def appendArgs(url, args): - """Append query arguments to a HTTP(s) URL. If the URL already has - query arguemtns, these arguments will be added, and the existing - arguments will be preserved. Duplicate arguments will not be - detected or collapsed (both will appear in the output). - - @param url: The url to which the arguments will be appended - @type url: str - - @param args: The query arguments to add to the URL. If a - dictionary is passed, the items will be sorted before - appending them to the URL. If a sequence of pairs is passed, - the order of the sequence will be preserved. - @type args: A dictionary from string to string, or a sequence of - pairs of strings. - - @returns: The URL with the parameters added - @rtype: str - """ - if hasattr(args, 'items'): - args = args.items() - args.sort() - else: - args = list(args) - - if len(args) == 0: - return url - - if '?' in url: - sep = '&' - else: - sep = '?' - - # Map unicode to UTF-8 if present. Do not make any assumptions - # about the encodings of plain bytes (str). - i = 0 - for k, v in args: - if type(k) is not str: - k = k.encode('UTF-8') - - if type(v) is not str: - v = v.encode('UTF-8') - - args[i] = (k, v) - i += 1 - - return '%s%s%s' % (url, sep, urlencode(args)) - -def toBase64(s): - """Represent string s as base64, omitting newlines""" - return binascii.b2a_base64(s)[:-1] - -def fromBase64(s): - try: - return binascii.a2b_base64(s) - except binascii.Error, why: - # Convert to a common exception type - raise ValueError(why[0]) - -class Symbol(object): - """This class implements an object that compares equal to others - of the same type that have the same name. These are distict from - str or unicode objects. - """ - - def __init__(self, name): - self.name = name - - def __eq__(self, other): - return type(self) is type(other) and self.name == other.name - - def __ne__(self, other): - return not (self == other) - - def __hash__(self): - return hash((self.__class__, self.name)) - - def __repr__(self): - return '<Symbol %s>' % (self.name,) diff --git a/askbot/deps/openid/server/__init__.py b/askbot/deps/openid/server/__init__.py deleted file mode 100644 index c8fde257..00000000 --- a/askbot/deps/openid/server/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -""" -This package contains the portions of the library used only when -implementing an OpenID server. See L{openid.server.server}. -""" - -__all__ = ['server', 'trustroot'] diff --git a/askbot/deps/openid/server/server.py b/askbot/deps/openid/server/server.py deleted file mode 100644 index 6d94249b..00000000 --- a/askbot/deps/openid/server/server.py +++ /dev/null @@ -1,1849 +0,0 @@ -# -*- test-case-name: openid.test.test_server -*- -"""OpenID server protocol and logic. - -Overview -======== - - An OpenID server must perform three tasks: - - 1. Examine the incoming request to determine its nature and validity. - - 2. Make a decision about how to respond to this request. - - 3. Format the response according to the protocol. - - The first and last of these tasks may performed by - the L{decodeRequest<Server.decodeRequest>} and - L{encodeResponse<Server.encodeResponse>} methods of the - L{Server} object. Who gets to do the intermediate task -- deciding - how to respond to the request -- will depend on what type of request it - is. - - If it's a request to authenticate a user (a X{C{checkid_setup}} or - X{C{checkid_immediate}} request), you need to decide if you will assert - that this user may claim the identity in question. Exactly how you do - that is a matter of application policy, but it generally involves making - sure the user has an account with your system and is logged in, checking - to see if that identity is hers to claim, and verifying with the user that - she does consent to releasing that information to the party making the - request. - - Examine the properties of the L{CheckIDRequest} object, optionally - check L{CheckIDRequest.returnToVerified}, and and when you've come - to a decision, form a response by calling L{CheckIDRequest.answer}. - - Other types of requests relate to establishing associations between client - and server and verifying the authenticity of previous communications. - L{Server} contains all the logic and data necessary to respond to - such requests; just pass the request to L{Server.handleRequest}. - - -OpenID Extensions -================= - - Do you want to provide other information for your users - in addition to authentication? Version 2.0 of the OpenID - protocol allows consumers to add extensions to their requests. - For example, with sites using the U{Simple Registration - Extension<http://openid.net/specs/openid-simple-registration-extension-1_0.html>}, - a user can agree to have their nickname and e-mail address sent to a - site when they sign up. - - Since extensions do not change the way OpenID authentication works, - code to handle extension requests may be completely separate from the - L{OpenIDRequest} class here. But you'll likely want data sent back by - your extension to be signed. L{OpenIDResponse} provides methods with - which you can add data to it which can be signed with the other data in - the OpenID signature. - - For example:: - - # when request is a checkid_* request - response = request.answer(True) - # this will a signed 'openid.sreg.timezone' parameter to the response - # as well as a namespace declaration for the openid.sreg namespace - response.fields.setArg('http://openid.net/sreg/1.0', 'timezone', 'America/Los_Angeles') - - There are helper modules for a number of extensions, including - L{Attribute Exchange<openid.extensions.ax>}, - L{PAPE<openid.extensions.pape>}, and - L{Simple Registration<openid.extensions.sreg>} in the L{openid.extensions} - package. - -Stores -====== - - The OpenID server needs to maintain state between requests in order - to function. Its mechanism for doing this is called a store. The - store interface is defined in C{L{openid.store.interface.OpenIDStore}}. - Additionally, several concrete store implementations are provided, so that - most sites won't need to implement a custom store. For a store backed - by flat files on disk, see C{L{openid.store.filestore.FileOpenIDStore}}. - For stores based on MySQL or SQLite, see the C{L{openid.store.sqlstore}} - module. - - -Upgrading -========= - -From 1.0 to 1.1 ---------------- - - The keys by which a server looks up associations in its store have changed - in version 1.2 of this library. If your store has entries created from - version 1.0 code, you should empty it. - -From 1.1 to 2.0 ---------------- - - One of the additions to the OpenID protocol was a specified nonce - format for one-way nonces. As a result, the nonce table in the store - has changed. You'll need to run contrib/upgrade-store-1.1-to-2.0 to - upgrade your store, or you'll encounter errors about the wrong number - of columns in the oid_nonces table. - - If you've written your own custom store or code that interacts - directly with it, you'll need to review the change notes in - L{openid.store.interface}. - -@group Requests: OpenIDRequest, AssociateRequest, CheckIDRequest, - CheckAuthRequest - -@group Responses: OpenIDResponse - -@group HTTP Codes: HTTP_OK, HTTP_REDIRECT, HTTP_ERROR - -@group Response Encodings: ENCODE_KVFORM, ENCODE_HTML_FORM, ENCODE_URL -""" - -import time, warnings -from copy import deepcopy - -from askbot.deps.openid import cryptutil -from askbot.deps.openid import oidutil -from askbot.deps.openid import kvform -from askbot.deps.openid.dh import DiffieHellman -from askbot.deps.openid.store.nonce import mkNonce -from askbot.deps.openid.server.trustroot import TrustRoot, verifyReturnTo -from askbot.deps.openid.association import Association, default_negotiator, getSecretSize -from askbot.deps.openid.message import Message, InvalidOpenIDNamespace, \ - OPENID_NS, OPENID2_NS, IDENTIFIER_SELECT, OPENID1_URL_LIMIT -from askbot.deps.openid.urinorm import urinorm - -HTTP_OK = 200 -HTTP_REDIRECT = 302 -HTTP_ERROR = 400 - -BROWSER_REQUEST_MODES = ['checkid_setup', 'checkid_immediate'] - -ENCODE_KVFORM = ('kvform',) -ENCODE_URL = ('URL/redirect',) -ENCODE_HTML_FORM = ('HTML form',) - -UNUSED = None - -class OpenIDRequest(object): - """I represent an incoming OpenID request. - - @cvar mode: the C{X{openid.mode}} of this request. - @type mode: str - """ - mode = None - - -class CheckAuthRequest(OpenIDRequest): - """A request to verify the validity of a previous response. - - @cvar mode: "X{C{check_authentication}}" - @type mode: str - - @ivar assoc_handle: The X{association handle} the response was signed with. - @type assoc_handle: str - @ivar signed: The message with the signature which wants checking. - @type signed: L{Message} - - @ivar invalidate_handle: An X{association handle} the client is asking - about the validity of. Optional, may be C{None}. - @type invalidate_handle: str - - @see: U{OpenID Specs, Mode: check_authentication - <http://openid.net/specs.bml#mode-check_authentication>} - """ - mode = "check_authentication" - - required_fields = ["identity", "return_to", "response_nonce"] - - def __init__(self, assoc_handle, signed, invalidate_handle=None): - """Construct me. - - These parameters are assigned directly as class attributes, see - my L{class documentation<CheckAuthRequest>} for their descriptions. - - @type assoc_handle: str - @type signed: L{Message} - @type invalidate_handle: str - """ - self.assoc_handle = assoc_handle - self.signed = signed - self.invalidate_handle = invalidate_handle - self.namespace = OPENID2_NS - - - def fromMessage(klass, message, op_endpoint=UNUSED): - """Construct me from an OpenID Message. - - @param message: An OpenID check_authentication Message - @type message: L{openid.message.Message} - - @returntype: L{CheckAuthRequest} - """ - self = klass.__new__(klass) - self.message = message - self.namespace = message.getOpenIDNamespace() - self.assoc_handle = message.getArg(OPENID_NS, 'assoc_handle') - self.sig = message.getArg(OPENID_NS, 'sig') - - if (self.assoc_handle is None or - self.sig is None): - fmt = "%s request missing required parameter from message %s" - raise ProtocolError( - message, text=fmt % (self.mode, message)) - - self.invalidate_handle = message.getArg(OPENID_NS, 'invalidate_handle') - - self.signed = message.copy() - # openid.mode is currently check_authentication because - # that's the mode of this request. But the signature - # was made on something with a different openid.mode. - # http://article.gmane.org/gmane.comp.web.openid.general/537 - if self.signed.hasKey(OPENID_NS, "mode"): - self.signed.setArg(OPENID_NS, "mode", "id_res") - - return self - - fromMessage = classmethod(fromMessage) - - def answer(self, signatory): - """Respond to this request. - - Given a L{Signatory}, I can check the validity of the signature and - the X{C{invalidate_handle}}. - - @param signatory: The L{Signatory} to use to check the signature. - @type signatory: L{Signatory} - - @returns: A response with an X{C{is_valid}} (and, if - appropriate X{C{invalidate_handle}}) field. - @returntype: L{OpenIDResponse} - """ - is_valid = signatory.verify(self.assoc_handle, self.signed) - # Now invalidate that assoc_handle so it this checkAuth message cannot - # be replayed. - signatory.invalidate(self.assoc_handle, dumb=True) - response = OpenIDResponse(self) - valid_str = (is_valid and "true") or "false" - response.fields.setArg(OPENID_NS, 'is_valid', valid_str) - - if self.invalidate_handle: - assoc = signatory.getAssociation(self.invalidate_handle, dumb=False) - if not assoc: - response.fields.setArg( - OPENID_NS, 'invalidate_handle', self.invalidate_handle) - return response - - - def __str__(self): - if self.invalidate_handle: - ih = " invalidate? %r" % (self.invalidate_handle,) - else: - ih = "" - s = "<%s handle: %r sig: %r: signed: %r%s>" % ( - self.__class__.__name__, self.assoc_handle, - self.sig, self.signed, ih) - return s - - -class PlainTextServerSession(object): - """An object that knows how to handle association requests with no - session type. - - @cvar session_type: The session_type for this association - session. There is no type defined for plain-text in the OpenID - specification, so we use 'no-encryption'. - @type session_type: str - - @see: U{OpenID Specs, Mode: associate - <http://openid.net/specs.bml#mode-associate>} - @see: AssociateRequest - """ - session_type = 'no-encryption' - allowed_assoc_types = ['HMAC-SHA1', 'HMAC-SHA256'] - - def fromMessage(cls, unused_request): - return cls() - - fromMessage = classmethod(fromMessage) - - def answer(self, secret): - return {'mac_key': oidutil.toBase64(secret)} - - -class DiffieHellmanSHA1ServerSession(object): - """An object that knows how to handle association requests with the - Diffie-Hellman session type. - - @cvar session_type: The session_type for this association - session. - @type session_type: str - - @ivar dh: The Diffie-Hellman algorithm values for this request - @type dh: DiffieHellman - - @ivar consumer_pubkey: The public key sent by the consumer in the - associate request - @type consumer_pubkey: long - - @see: U{OpenID Specs, Mode: associate - <http://openid.net/specs.bml#mode-associate>} - @see: AssociateRequest - """ - session_type = 'DH-SHA1' - hash_func = staticmethod(cryptutil.sha1) - allowed_assoc_types = ['HMAC-SHA1'] - - def __init__(self, dh, consumer_pubkey): - self.dh = dh - self.consumer_pubkey = consumer_pubkey - - def fromMessage(cls, message): - """ - @param message: The associate request message - @type message: openid.message.Message - - @returntype: L{DiffieHellmanSHA1ServerSession} - - @raises ProtocolError: When parameters required to establish the - session are missing. - """ - dh_modulus = message.getArg(OPENID_NS, 'dh_modulus') - dh_gen = message.getArg(OPENID_NS, 'dh_gen') - if (dh_modulus is None and dh_gen is not None or - dh_gen is None and dh_modulus is not None): - - if dh_modulus is None: - missing = 'modulus' - else: - missing = 'generator' - - raise ProtocolError(message, - 'If non-default modulus or generator is ' - 'supplied, both must be supplied. Missing %s' - % (missing,)) - - if dh_modulus or dh_gen: - dh_modulus = cryptutil.base64ToLong(dh_modulus) - dh_gen = cryptutil.base64ToLong(dh_gen) - dh = DiffieHellman(dh_modulus, dh_gen) - else: - dh = DiffieHellman.fromDefaults() - - consumer_pubkey = message.getArg(OPENID_NS, 'dh_consumer_public') - if consumer_pubkey is None: - raise ProtocolError(message, "Public key for DH-SHA1 session " - "not found in message %s" % (message,)) - - consumer_pubkey = cryptutil.base64ToLong(consumer_pubkey) - - return cls(dh, consumer_pubkey) - - fromMessage = classmethod(fromMessage) - - def answer(self, secret): - mac_key = self.dh.xorSecret(self.consumer_pubkey, - secret, - self.hash_func) - return { - 'dh_server_public': cryptutil.longToBase64(self.dh.public), - 'enc_mac_key': oidutil.toBase64(mac_key), - } - -class DiffieHellmanSHA256ServerSession(DiffieHellmanSHA1ServerSession): - session_type = 'DH-SHA256' - hash_func = staticmethod(cryptutil.sha256) - allowed_assoc_types = ['HMAC-SHA256'] - -class AssociateRequest(OpenIDRequest): - """A request to establish an X{association}. - - @cvar mode: "X{C{check_authentication}}" - @type mode: str - - @ivar assoc_type: The type of association. The protocol currently only - defines one value for this, "X{C{HMAC-SHA1}}". - @type assoc_type: str - - @ivar session: An object that knows how to handle association - requests of a certain type. - - @see: U{OpenID Specs, Mode: associate - <http://openid.net/specs.bml#mode-associate>} - """ - - mode = "associate" - - session_classes = { - 'no-encryption': PlainTextServerSession, - 'DH-SHA1': DiffieHellmanSHA1ServerSession, - 'DH-SHA256': DiffieHellmanSHA256ServerSession, - } - - def __init__(self, session, assoc_type): - """Construct me. - - The session is assigned directly as a class attribute. See my - L{class documentation<AssociateRequest>} for its description. - """ - super(AssociateRequest, self).__init__() - self.session = session - self.assoc_type = assoc_type - self.namespace = OPENID2_NS - - - def fromMessage(klass, message, op_endpoint=UNUSED): - """Construct me from an OpenID Message. - - @param message: The OpenID associate request - @type message: openid.message.Message - - @returntype: L{AssociateRequest} - """ - if message.isOpenID1(): - session_type = message.getArg(OPENID_NS, 'session_type') - if session_type == 'no-encryption': - oidutil.log('Received OpenID 1 request with a no-encryption ' - 'assocaition session type. Continuing anyway.') - elif not session_type: - session_type = 'no-encryption' - else: - session_type = message.getArg(OPENID2_NS, 'session_type') - if session_type is None: - raise ProtocolError(message, - text="session_type missing from request") - - try: - session_class = klass.session_classes[session_type] - except KeyError: - raise ProtocolError(message, - "Unknown session type %r" % (session_type,)) - - try: - session = session_class.fromMessage(message) - except ValueError, why: - raise ProtocolError(message, 'Error parsing %s session: %s' % - (session_class.session_type, why[0])) - - assoc_type = message.getArg(OPENID_NS, 'assoc_type', 'HMAC-SHA1') - if assoc_type not in session.allowed_assoc_types: - fmt = 'Session type %s does not support association type %s' - raise ProtocolError(message, fmt % (session_type, assoc_type)) - - self = klass(session, assoc_type) - self.message = message - self.namespace = message.getOpenIDNamespace() - return self - - fromMessage = classmethod(fromMessage) - - def answer(self, assoc): - """Respond to this request with an X{association}. - - @param assoc: The association to send back. - @type assoc: L{openid.association.Association} - - @returns: A response with the association information, encrypted - to the consumer's X{public key} if appropriate. - @returntype: L{OpenIDResponse} - """ - response = OpenIDResponse(self) - response.fields.updateArgs(OPENID_NS, { - 'expires_in': '%d' % (assoc.getExpiresIn(),), - 'assoc_type': self.assoc_type, - 'assoc_handle': assoc.handle, - }) - response.fields.updateArgs(OPENID_NS, - self.session.answer(assoc.secret)) - - if not (self.session.session_type == 'no-encryption' and - self.message.isOpenID1()): - # The session type "no-encryption" did not have a name - # in OpenID v1, it was just omitted. - response.fields.setArg( - OPENID_NS, 'session_type', self.session.session_type) - - return response - - def answerUnsupported(self, message, preferred_association_type=None, - preferred_session_type=None): - """Respond to this request indicating that the association - type or association session type is not supported.""" - if self.message.isOpenID1(): - raise ProtocolError(self.message) - - response = OpenIDResponse(self) - response.fields.setArg(OPENID_NS, 'error_code', 'unsupported-type') - response.fields.setArg(OPENID_NS, 'error', message) - - if preferred_association_type: - response.fields.setArg( - OPENID_NS, 'assoc_type', preferred_association_type) - - if preferred_session_type: - response.fields.setArg( - OPENID_NS, 'session_type', preferred_session_type) - - return response - -class CheckIDRequest(OpenIDRequest): - """A request to confirm the identity of a user. - - This class handles requests for openid modes X{C{checkid_immediate}} - and X{C{checkid_setup}}. - - @cvar mode: "X{C{checkid_immediate}}" or "X{C{checkid_setup}}" - @type mode: str - - @ivar immediate: Is this an immediate-mode request? - @type immediate: bool - - @ivar identity: The OP-local identifier being checked. - @type identity: str - - @ivar claimed_id: The claimed identifier. Not present in OpenID 1.x - messages. - @type claimed_id: str - - @ivar trust_root: "Are you Frank?" asks the checkid request. "Who wants - to know?" C{trust_root}, that's who. This URL identifies the party - making the request, and the user will use that to make her decision - about what answer she trusts them to have. Referred to as "realm" in - OpenID 2.0. - @type trust_root: str - - @ivar return_to: The URL to send the user agent back to to reply to this - request. - @type return_to: str - - @ivar assoc_handle: Provided in smart mode requests, a handle for a - previously established association. C{None} for dumb mode requests. - @type assoc_handle: str - """ - - def __init__(self, identity, return_to, trust_root=None, immediate=False, - assoc_handle=None, op_endpoint=None, claimed_id=None): - """Construct me. - - These parameters are assigned directly as class attributes, see - my L{class documentation<CheckIDRequest>} for their descriptions. - - @raises MalformedReturnURL: When the C{return_to} URL is not a URL. - """ - self.assoc_handle = assoc_handle - self.identity = identity - self.claimed_id = claimed_id or identity - self.return_to = return_to - self.trust_root = trust_root or return_to - self.op_endpoint = op_endpoint - assert self.op_endpoint is not None - if immediate: - self.immediate = True - self.mode = "checkid_immediate" - else: - self.immediate = False - self.mode = "checkid_setup" - - if self.return_to is not None and \ - not TrustRoot.parse(self.return_to): - raise MalformedReturnURL(None, self.return_to) - if not self.trustRootValid(): - raise UntrustedReturnURL(None, self.return_to, self.trust_root) - self.message = None - - def _getNamespace(self): - warnings.warn('The "namespace" attribute of CheckIDRequest objects ' - 'is deprecated. Use "message.getOpenIDNamespace()" ' - 'instead', DeprecationWarning, stacklevel=2) - return self.message.getOpenIDNamespace() - - namespace = property(_getNamespace) - - def fromMessage(klass, message, op_endpoint): - """Construct me from an OpenID message. - - @raises ProtocolError: When not all required parameters are present - in the message. - - @raises MalformedReturnURL: When the C{return_to} URL is not a URL. - - @raises UntrustedReturnURL: When the C{return_to} URL is outside - the C{trust_root}. - - @param message: An OpenID checkid_* request Message - @type message: openid.message.Message - - @param op_endpoint: The endpoint URL of the server that this - message was sent to. - @type op_endpoint: str - - @returntype: L{CheckIDRequest} - """ - self = klass.__new__(klass) - self.message = message - self.op_endpoint = op_endpoint - mode = message.getArg(OPENID_NS, 'mode') - if mode == "checkid_immediate": - self.immediate = True - self.mode = "checkid_immediate" - else: - self.immediate = False - self.mode = "checkid_setup" - - self.return_to = message.getArg(OPENID_NS, 'return_to') - if message.isOpenID1() and not self.return_to: - fmt = "Missing required field 'return_to' from %r" - raise ProtocolError(message, text=fmt % (message,)) - - self.identity = message.getArg(OPENID_NS, 'identity') - self.claimed_id = message.getArg(OPENID_NS, 'claimed_id') - if message.isOpenID1(): - if self.identity is None: - s = "OpenID 1 message did not contain openid.identity" - raise ProtocolError(message, text=s) - else: - if self.identity and not self.claimed_id: - s = ("OpenID 2.0 message contained openid.identity but not " - "claimed_id") - raise ProtocolError(message, text=s) - elif self.claimed_id and not self.identity: - s = ("OpenID 2.0 message contained openid.claimed_id but not " - "identity") - raise ProtocolError(message, text=s) - - # There's a case for making self.trust_root be a TrustRoot - # here. But if TrustRoot isn't currently part of the "public" API, - # I'm not sure it's worth doing. - - if message.isOpenID1(): - trust_root_param = 'trust_root' - else: - trust_root_param = 'realm' - - # Using 'or' here is slightly different than sending a default - # argument to getArg, as it will treat no value and an empty - # string as equivalent. - self.trust_root = (message.getArg(OPENID_NS, trust_root_param) - or self.return_to) - - if not message.isOpenID1(): - if self.return_to is self.trust_root is None: - raise ProtocolError(message, "openid.realm required when " + - "openid.return_to absent") - - self.assoc_handle = message.getArg(OPENID_NS, 'assoc_handle') - - # Using TrustRoot.parse here is a bit misleading, as we're not - # parsing return_to as a trust root at all. However, valid URLs - # are valid trust roots, so we can use this to get an idea if it - # is a valid URL. Not all trust roots are valid return_to URLs, - # however (particularly ones with wildcards), so this is still a - # little sketchy. - if self.return_to is not None and \ - not TrustRoot.parse(self.return_to): - raise MalformedReturnURL(message, self.return_to) - - # I first thought that checking to see if the return_to is within - # the trust_root is premature here, a logic-not-decoding thing. But - # it was argued that this is really part of data validation. A - # request with an invalid trust_root/return_to is broken regardless of - # application, right? - if not self.trustRootValid(): - raise UntrustedReturnURL(message, self.return_to, self.trust_root) - - return self - - fromMessage = classmethod(fromMessage) - - def idSelect(self): - """Is the identifier to be selected by the IDP? - - @returntype: bool - """ - # So IDPs don't have to import the constant - return self.identity == IDENTIFIER_SELECT - - def trustRootValid(self): - """Is my return_to under my trust_root? - - @returntype: bool - """ - if not self.trust_root: - return True - tr = TrustRoot.parse(self.trust_root) - if tr is None: - raise MalformedTrustRoot(self.message, self.trust_root) - - if self.return_to is not None: - return tr.validateURL(self.return_to) - else: - return True - - def returnToVerified(self): - """Does the relying party publish the return_to URL for this - response under the realm? It is up to the provider to set a - policy for what kinds of realms should be allowed. This - return_to URL verification reduces vulnerability to data-theft - attacks based on open proxies, cross-site-scripting, or open - redirectors. - - This check should only be performed after making sure that the - return_to URL matches the realm. - - @see: L{trustRootValid} - - @raises openid.yadis.discover.DiscoveryFailure: if the realm - URL does not support Yadis discovery (and so does not - support the verification process). - - @raises openid.fetchers.HTTPFetchingError: if the realm URL - is not reachable. When this is the case, the RP may be hosted - on the user's intranet. - - @returntype: bool - - @returns: True if the realm publishes a document with the - return_to URL listed - - @since: 2.1.0 - """ - return verifyReturnTo(self.trust_root, self.return_to) - - def answer(self, allow, server_url=None, identity=None, claimed_id=None): - """Respond to this request. - - @param allow: Allow this user to claim this identity, and allow the - consumer to have this information? - @type allow: bool - - @param server_url: DEPRECATED. Passing C{op_endpoint} to the - L{Server} constructor makes this optional. - - When an OpenID 1.x immediate mode request does not succeed, - it gets back a URL where the request may be carried out - in a not-so-immediate fashion. Pass my URL in here (the - fully qualified address of this server's endpoint, i.e. - C{http://example.com/server}), and I will use it as a base for the - URL for a new request. - - Optional for requests where C{CheckIDRequest.immediate} is C{False} - or C{allow} is C{True}. - - @type server_url: str - - @param identity: The OP-local identifier to answer with. Only for use - when the relying party requested identifier selection. - @type identity: str or None - - @param claimed_id: The claimed identifier to answer with, for use - with identifier selection in the case where the claimed identifier - and the OP-local identifier differ, i.e. when the claimed_id uses - delegation. - - If C{identity} is provided but this is not, C{claimed_id} will - default to the value of C{identity}. When answering requests - that did not ask for identifier selection, the response - C{claimed_id} will default to that of the request. - - This parameter is new in OpenID 2.0. - @type claimed_id: str or None - - @returntype: L{OpenIDResponse} - - @change: Version 2.0 deprecates C{server_url} and adds C{claimed_id}. - - @raises NoReturnError: when I do not have a return_to. - """ - assert self.message is not None - - if not self.return_to: - raise NoReturnToError - - if not server_url: - if not self.message.isOpenID1() and not self.op_endpoint: - # In other words, that warning I raised in Server.__init__? - # You should pay attention to it now. - raise RuntimeError("%s should be constructed with op_endpoint " - "to respond to OpenID 2.0 messages." % - (self,)) - server_url = self.op_endpoint - - if allow: - mode = 'id_res' - elif self.message.isOpenID1(): - if self.immediate: - mode = 'id_res' - else: - mode = 'cancel' - else: - if self.immediate: - mode = 'setup_needed' - else: - mode = 'cancel' - - response = OpenIDResponse(self) - - if claimed_id and self.message.isOpenID1(): - namespace = self.message.getOpenIDNamespace() - raise VersionError("claimed_id is new in OpenID 2.0 and not " - "available for %s" % (namespace,)) - - if allow: - if self.identity == IDENTIFIER_SELECT: - if not identity: - raise ValueError( - "This request uses IdP-driven identifier selection." - "You must supply an identifier in the response.") - response_identity = identity - response_claimed_id = claimed_id or identity - - elif self.identity: - if identity and (self.identity != identity): - normalized_request_identity = urinorm(self.identity) - normalized_answer_identity = urinorm(identity) - - if (normalized_request_identity != - normalized_answer_identity): - raise ValueError( - "Request was for identity %r, cannot reply " - "with identity %r" % (self.identity, identity)) - - # The "identity" value in the response shall always be - # the same as that in the request, otherwise the RP is - # likely to not validate the response. - response_identity = self.identity - response_claimed_id = self.claimed_id - else: - if identity: - raise ValueError( - "This request specified no identity and you " - "supplied %r" % (identity,)) - response_identity = None - - if self.message.isOpenID1() and response_identity is None: - raise ValueError( - "Request was an OpenID 1 request, so response must " - "include an identifier." - ) - - response.fields.updateArgs(OPENID_NS, { - 'mode': mode, - 'return_to': self.return_to, - 'response_nonce': mkNonce(), - }) - - if server_url: - response.fields.setArg(OPENID_NS, 'op_endpoint', server_url) - - if response_identity is not None: - response.fields.setArg( - OPENID_NS, 'identity', response_identity) - if self.message.isOpenID2(): - response.fields.setArg( - OPENID_NS, 'claimed_id', response_claimed_id) - else: - response.fields.setArg(OPENID_NS, 'mode', mode) - if self.immediate: - if self.message.isOpenID1() and not server_url: - raise ValueError("setup_url is required for allow=False " - "in OpenID 1.x immediate mode.") - # Make a new request just like me, but with immediate=False. - setup_request = self.__class__( - self.identity, self.return_to, self.trust_root, - immediate=False, assoc_handle=self.assoc_handle, - op_endpoint=self.op_endpoint, claimed_id=self.claimed_id) - - # XXX: This API is weird. - setup_request.message = self.message - - setup_url = setup_request.encodeToURL(server_url) - response.fields.setArg(OPENID_NS, 'user_setup_url', setup_url) - - return response - - - def encodeToURL(self, server_url): - """Encode this request as a URL to GET. - - @param server_url: The URL of the OpenID server to make this request of. - @type server_url: str - - @returntype: str - - @raises NoReturnError: when I do not have a return_to. - """ - if not self.return_to: - raise NoReturnToError - - # Imported from the alternate reality where these classes are used - # in both the client and server code, so Requests are Encodable too. - # That's right, code imported from alternate realities all for the - # love of you, id_res/user_setup_url. - q = {'mode': self.mode, - 'identity': self.identity, - 'claimed_id': self.claimed_id, - 'return_to': self.return_to} - if self.trust_root: - if self.message.isOpenID1(): - q['trust_root'] = self.trust_root - else: - q['realm'] = self.trust_root - if self.assoc_handle: - q['assoc_handle'] = self.assoc_handle - - response = Message(self.message.getOpenIDNamespace()) - response.updateArgs(OPENID_NS, q) - return response.toURL(server_url) - - - def getCancelURL(self): - """Get the URL to cancel this request. - - Useful for creating a "Cancel" button on a web form so that operation - can be carried out directly without another trip through the server. - - (Except you probably want to make another trip through the server so - that it knows that the user did make a decision. Or you could simulate - this method by doing C{.answer(False).encodeToURL()}) - - @returntype: str - @returns: The return_to URL with openid.mode = cancel. - - @raises NoReturnError: when I do not have a return_to. - """ - if not self.return_to: - raise NoReturnToError - - if self.immediate: - raise ValueError("Cancel is not an appropriate response to " - "immediate mode requests.") - - response = Message(self.message.getOpenIDNamespace()) - response.setArg(OPENID_NS, 'mode', 'cancel') - return response.toURL(self.return_to) - - - def __repr__(self): - return '<%s id:%r im:%s tr:%r ah:%r>' % (self.__class__.__name__, - self.identity, - self.immediate, - self.trust_root, - self.assoc_handle) - - - -class OpenIDResponse(object): - """I am a response to an OpenID request. - - @ivar request: The request I respond to. - @type request: L{OpenIDRequest} - - @ivar fields: My parameters as a dictionary with each key mapping to - one value. Keys are parameter names with no leading "C{openid.}". - e.g. "C{identity}" and "C{mac_key}", never "C{openid.identity}". - @type fields: L{openid.message.Message} - - @ivar signed: The names of the fields which should be signed. - @type signed: list of str - """ - - # Implementer's note: In a more symmetric client/server - # implementation, there would be more types of OpenIDResponse - # object and they would have validated attributes according to the - # type of response. But as it is, Response objects in a server are - # basically write-only, their only job is to go out over the wire, - # so this is just a loose wrapper around OpenIDResponse.fields. - - def __init__(self, request): - """Make a response to an L{OpenIDRequest}. - - @type request: L{OpenIDRequest} - """ - self.request = request - self.fields = Message(request.namespace) - - def __str__(self): - return "%s for %s: %s" % ( - self.__class__.__name__, - self.request.__class__.__name__, - self.fields) - - - def toFormMarkup(self, form_tag_attrs=None): - """Returns the form markup for this response. - - @param form_tag_attrs: Dictionary of attributes to be added to - the form tag. 'accept-charset' and 'enctype' have defaults - that can be overridden. If a value is supplied for - 'action' or 'method', it will be replaced. - - @returntype: str - - @since: 2.1.0 - """ - return self.fields.toFormMarkup(self.request.return_to, - form_tag_attrs=form_tag_attrs) - - def toHTML(self, form_tag_attrs=None): - """Returns an HTML document that auto-submits the form markup - for this response. - - @returntype: str - - @see: toFormMarkup - - @since: 2.1.? - """ - return oidutil.autoSubmitHTML(self.toFormMarkup(form_tag_attrs)) - - def renderAsForm(self): - """Returns True if this response's encoding is - ENCODE_HTML_FORM. Convenience method for server authors. - - @returntype: bool - - @since: 2.1.0 - """ - return self.whichEncoding() == ENCODE_HTML_FORM - - - def needsSigning(self): - """Does this response require signing? - - @returntype: bool - """ - return self.fields.getArg(OPENID_NS, 'mode') == 'id_res' - - - # implements IEncodable - - def whichEncoding(self): - """How should I be encoded? - - @returns: one of ENCODE_URL, ENCODE_HTML_FORM, or ENCODE_KVFORM. - - @change: 2.1.0 added the ENCODE_HTML_FORM response. - """ - if self.request.mode in BROWSER_REQUEST_MODES: - if self.fields.getOpenIDNamespace() == OPENID2_NS and \ - len(self.encodeToURL()) > OPENID1_URL_LIMIT: - return ENCODE_HTML_FORM - else: - return ENCODE_URL - else: - return ENCODE_KVFORM - - - def encodeToURL(self): - """Encode a response as a URL for the user agent to GET. - - You will generally use this URL with a HTTP redirect. - - @returns: A URL to direct the user agent back to. - @returntype: str - """ - return self.fields.toURL(self.request.return_to) - - - def addExtension(self, extension_response): - """ - Add an extension response to this response message. - - @param extension_response: An object that implements the - extension interface for adding arguments to an OpenID - message. - @type extension_response: L{openid.extension} - - @returntype: None - """ - extension_response.toMessage(self.fields) - - - def encodeToKVForm(self): - """Encode a response in key-value colon/newline format. - - This is a machine-readable format used to respond to messages which - came directly from the consumer and not through the user agent. - - @see: OpenID Specs, - U{Key-Value Colon/Newline format<http://openid.net/specs.bml#keyvalue>} - - @returntype: str - """ - return self.fields.toKVForm() - - - -class WebResponse(object): - """I am a response to an OpenID request in terms a web server understands. - - I generally come from an L{Encoder}, either directly or from - L{Server.encodeResponse}. - - @ivar code: The HTTP code of this response. - @type code: int - - @ivar headers: Headers to include in this response. - @type headers: dict - - @ivar body: The body of this response. - @type body: str - """ - - def __init__(self, code=HTTP_OK, headers=None, body=""): - """Construct me. - - These parameters are assigned directly as class attributes, see - my L{class documentation<WebResponse>} for their descriptions. - """ - self.code = code - if headers is not None: - self.headers = headers - else: - self.headers = {} - self.body = body - - - -class Signatory(object): - """I sign things. - - I also check signatures. - - All my state is encapsulated in an - L{OpenIDStore<openid.store.interface.OpenIDStore>}, which means - I'm not generally pickleable but I am easy to reconstruct. - - @cvar SECRET_LIFETIME: The number of seconds a secret remains valid. - @type SECRET_LIFETIME: int - """ - - SECRET_LIFETIME = 14 * 24 * 60 * 60 # 14 days, in seconds - - # keys have a bogus server URL in them because the filestore - # really does expect that key to be a URL. This seems a little - # silly for the server store, since I expect there to be only one - # server URL. - _normal_key = 'http://localhost/|normal' - _dumb_key = 'http://localhost/|dumb' - - - def __init__(self, store): - """Create a new Signatory. - - @param store: The back-end where my associations are stored. - @type store: L{openid.store.interface.OpenIDStore} - """ - assert store is not None - self.store = store - - - def verify(self, assoc_handle, message): - """Verify that the signature for some data is valid. - - @param assoc_handle: The handle of the association used to sign the - data. - @type assoc_handle: str - - @param message: The signed message to verify - @type message: openid.message.Message - - @returns: C{True} if the signature is valid, C{False} if not. - @returntype: bool - """ - assoc = self.getAssociation(assoc_handle, dumb=True) - if not assoc: - oidutil.log("failed to get assoc with handle %r to verify " - "message %r" - % (assoc_handle, message)) - return False - - try: - valid = assoc.checkMessageSignature(message) - except ValueError, ex: - oidutil.log("Error in verifying %s with %s: %s" % (message, - assoc, - ex)) - return False - return valid - - - def sign(self, response): - """Sign a response. - - I take a L{OpenIDResponse}, create a signature for everything - in its L{signed<OpenIDResponse.signed>} list, and return a new - copy of the response object with that signature included. - - @param response: A response to sign. - @type response: L{OpenIDResponse} - - @returns: A signed copy of the response. - @returntype: L{OpenIDResponse} - """ - signed_response = deepcopy(response) - assoc_handle = response.request.assoc_handle - if assoc_handle: - # normal mode - # disabling expiration check because even if the association - # is expired, we still need to know some properties of the - # association so that we may preserve those properties when - # creating the fallback association. - assoc = self.getAssociation(assoc_handle, dumb=False, - checkExpiration=False) - - if not assoc or assoc.expiresIn <= 0: - # fall back to dumb mode - signed_response.fields.setArg( - OPENID_NS, 'invalidate_handle', assoc_handle) - assoc_type = assoc and assoc.assoc_type or 'HMAC-SHA1' - if assoc and assoc.expiresIn <= 0: - # now do the clean-up that the disabled checkExpiration - # code didn't get to do. - self.invalidate(assoc_handle, dumb=False) - assoc = self.createAssociation(dumb=True, assoc_type=assoc_type) - else: - # dumb mode. - assoc = self.createAssociation(dumb=True) - - try: - signed_response.fields = assoc.signMessage(signed_response.fields) - except kvform.KVFormError, err: - raise EncodingError(response, explanation=str(err)) - return signed_response - - - def createAssociation(self, dumb=True, assoc_type='HMAC-SHA1'): - """Make a new association. - - @param dumb: Is this association for a dumb-mode transaction? - @type dumb: bool - - @param assoc_type: The type of association to create. Currently - there is only one type defined, C{HMAC-SHA1}. - @type assoc_type: str - - @returns: the new association. - @returntype: L{openid.association.Association} - """ - secret = cryptutil.getBytes(getSecretSize(assoc_type)) - uniq = oidutil.toBase64(cryptutil.getBytes(4)) - handle = '{%s}{%x}{%s}' % (assoc_type, int(time.time()), uniq) - - assoc = Association.fromExpiresIn( - self.SECRET_LIFETIME, handle, secret, assoc_type) - - if dumb: - key = self._dumb_key - else: - key = self._normal_key - self.store.storeAssociation(key, assoc) - return assoc - - - def getAssociation(self, assoc_handle, dumb, checkExpiration=True): - """Get the association with the specified handle. - - @type assoc_handle: str - - @param dumb: Is this association used with dumb mode? - @type dumb: bool - - @returns: the association, or None if no valid association with that - handle was found. - @returntype: L{openid.association.Association} - """ - # Hmm. We've created an interface that deals almost entirely with - # assoc_handles. The only place outside the Signatory that uses this - # (and thus the only place that ever sees Association objects) is - # when creating a response to an association request, as it must have - # the association's secret. - - if assoc_handle is None: - raise ValueError("assoc_handle must not be None") - - if dumb: - key = self._dumb_key - else: - key = self._normal_key - assoc = self.store.getAssociation(key, assoc_handle) - if assoc is not None and assoc.expiresIn <= 0: - oidutil.log("requested %sdumb key %r is expired (by %s seconds)" % - ((not dumb) and 'not-' or '', - assoc_handle, assoc.expiresIn)) - if checkExpiration: - self.store.removeAssociation(key, assoc_handle) - assoc = None - return assoc - - - def invalidate(self, assoc_handle, dumb): - """Invalidates the association with the given handle. - - @type assoc_handle: str - - @param dumb: Is this association used with dumb mode? - @type dumb: bool - """ - if dumb: - key = self._dumb_key - else: - key = self._normal_key - self.store.removeAssociation(key, assoc_handle) - - - -class Encoder(object): - """I encode responses in to L{WebResponses<WebResponse>}. - - If you don't like L{WebResponses<WebResponse>}, you can do - your own handling of L{OpenIDResponses<OpenIDResponse>} with - L{OpenIDResponse.whichEncoding}, L{OpenIDResponse.encodeToURL}, and - L{OpenIDResponse.encodeToKVForm}. - """ - - responseFactory = WebResponse - - - def encode(self, response): - """Encode a response to a L{WebResponse}. - - @raises EncodingError: When I can't figure out how to encode this - message. - """ - encode_as = response.whichEncoding() - if encode_as == ENCODE_KVFORM: - wr = self.responseFactory(body=response.encodeToKVForm()) - if isinstance(response, Exception): - wr.code = HTTP_ERROR - elif encode_as == ENCODE_URL: - location = response.encodeToURL() - wr = self.responseFactory(code=HTTP_REDIRECT, - headers={'location': location}) - elif encode_as == ENCODE_HTML_FORM: - wr = self.responseFactory(code=HTTP_OK, - body=response.toFormMarkup()) - else: - # Can't encode this to a protocol message. You should probably - # render it to HTML and show it to the user. - raise EncodingError(response) - return wr - - - -class SigningEncoder(Encoder): - """I encode responses in to L{WebResponses<WebResponse>}, signing them when required. - """ - - def __init__(self, signatory): - """Create a L{SigningEncoder}. - - @param signatory: The L{Signatory} I will make signatures with. - @type signatory: L{Signatory} - """ - self.signatory = signatory - - - def encode(self, response): - """Encode a response to a L{WebResponse}, signing it first if appropriate. - - @raises EncodingError: When I can't figure out how to encode this - message. - - @raises AlreadySigned: When this response is already signed. - - @returntype: L{WebResponse} - """ - # the isinstance is a bit of a kludge... it means there isn't really - # an adapter to make the interfaces quite match. - if (not isinstance(response, Exception)) and response.needsSigning(): - if not self.signatory: - raise ValueError( - "Must have a store to sign this request: %s" % - (response,), response) - if response.fields.hasKey(OPENID_NS, 'sig'): - raise AlreadySigned(response) - response = self.signatory.sign(response) - return super(SigningEncoder, self).encode(response) - - - -class Decoder(object): - """I decode an incoming web request in to a L{OpenIDRequest}. - """ - - _handlers = { - 'checkid_setup': CheckIDRequest.fromMessage, - 'checkid_immediate': CheckIDRequest.fromMessage, - 'check_authentication': CheckAuthRequest.fromMessage, - 'associate': AssociateRequest.fromMessage, - } - - def __init__(self, server): - """Construct a Decoder. - - @param server: The server which I am decoding requests for. - (Necessary because some replies reference their server.) - @type server: L{Server} - """ - self.server = server - - def decode(self, query): - """I transform query parameters into an L{OpenIDRequest}. - - If the query does not seem to be an OpenID request at all, I return - C{None}. - - @param query: The query parameters as a dictionary with each - key mapping to one value. - @type query: dict - - @raises ProtocolError: When the query does not seem to be a valid - OpenID request. - - @returntype: L{OpenIDRequest} - """ - if not query: - return None - - try: - message = Message.fromPostArgs(query) - except InvalidOpenIDNamespace, err: - # It's useful to have a Message attached to a ProtocolError, so we - # override the bad ns value to build a Message out of it. Kinda - # kludgy, since it's made of lies, but the parts that aren't lies - # are more useful than a 'None'. - query = query.copy() - query['openid.ns'] = OPENID2_NS - message = Message.fromPostArgs(query) - raise ProtocolError(message, str(err)) - - mode = message.getArg(OPENID_NS, 'mode') - if not mode: - fmt = "No mode value in message %s" - raise ProtocolError(message, text=fmt % (message,)) - - handler = self._handlers.get(mode, self.defaultDecoder) - return handler(message, self.server.op_endpoint) - - - def defaultDecoder(self, message, server): - """Called to decode queries when no handler for that mode is found. - - @raises ProtocolError: This implementation always raises - L{ProtocolError}. - """ - mode = message.getArg(OPENID_NS, 'mode') - fmt = "Unrecognized OpenID mode %r" - raise ProtocolError(message, text=fmt % (mode,)) - - - -class Server(object): - """I handle requests for an OpenID server. - - Some types of requests (those which are not C{checkid} requests) may be - handed to my L{handleRequest} method, and I will take care of it and - return a response. - - For your convenience, I also provide an interface to L{Decoder.decode} - and L{SigningEncoder.encode} through my methods L{decodeRequest} and - L{encodeResponse}. - - All my state is encapsulated in an - L{OpenIDStore<openid.store.interface.OpenIDStore>}, which means - I'm not generally pickleable but I am easy to reconstruct. - - Example:: - - oserver = Server(FileOpenIDStore(data_path), "http://example.com/op") - request = oserver.decodeRequest(query) - if request.mode in ['checkid_immediate', 'checkid_setup']: - if self.isAuthorized(request.identity, request.trust_root): - response = request.answer(True) - elif request.immediate: - response = request.answer(False) - else: - self.showDecidePage(request) - return - else: - response = oserver.handleRequest(request) - - webresponse = oserver.encode(response) - - @ivar signatory: I'm using this for associate requests and to sign things. - @type signatory: L{Signatory} - - @ivar decoder: I'm using this to decode things. - @type decoder: L{Decoder} - - @ivar encoder: I'm using this to encode things. - @type encoder: L{Encoder} - - @ivar op_endpoint: My URL. - @type op_endpoint: str - - @ivar negotiator: I use this to determine which kinds of - associations I can make and how. - @type negotiator: L{openid.association.SessionNegotiator} - """ - - signatoryClass = Signatory - encoderClass = SigningEncoder - decoderClass = Decoder - - def __init__(self, store, op_endpoint=None): - """A new L{Server}. - - @param store: The back-end where my associations are stored. - @type store: L{openid.store.interface.OpenIDStore} - - @param op_endpoint: My URL, the fully qualified address of this - server's endpoint, i.e. C{http://example.com/server} - @type op_endpoint: str - - @change: C{op_endpoint} is new in library version 2.0. It - currently defaults to C{None} for compatibility with - earlier versions of the library, but you must provide it - if you want to respond to any version 2 OpenID requests. - """ - self.store = store - self.signatory = self.signatoryClass(self.store) - self.encoder = self.encoderClass(self.signatory) - self.decoder = self.decoderClass(self) - self.negotiator = default_negotiator.copy() - - if not op_endpoint: - warnings.warn("%s.%s constructor requires op_endpoint parameter " - "for OpenID 2.0 servers" % - (self.__class__.__module__, self.__class__.__name__), - stacklevel=2) - self.op_endpoint = op_endpoint - - - def handleRequest(self, request): - """Handle a request. - - Give me a request, I will give you a response. Unless it's a type - of request I cannot handle myself, in which case I will raise - C{NotImplementedError}. In that case, you can handle it yourself, - or add a method to me for handling that request type. - - @raises NotImplementedError: When I do not have a handler defined - for that type of request. - - @returntype: L{OpenIDResponse} - """ - handler = getattr(self, 'openid_' + request.mode, None) - if handler is not None: - return handler(request) - else: - raise NotImplementedError( - "%s has no handler for a request of mode %r." % - (self, request.mode)) - - - def openid_check_authentication(self, request): - """Handle and respond to C{check_authentication} requests. - - @returntype: L{OpenIDResponse} - """ - return request.answer(self.signatory) - - - def openid_associate(self, request): - """Handle and respond to C{associate} requests. - - @returntype: L{OpenIDResponse} - """ - # XXX: TESTME - assoc_type = request.assoc_type - session_type = request.session.session_type - if self.negotiator.isAllowed(assoc_type, session_type): - assoc = self.signatory.createAssociation(dumb=False, - assoc_type=assoc_type) - return request.answer(assoc) - else: - message = ('Association type %r is not supported with ' - 'session type %r' % (assoc_type, session_type)) - (preferred_assoc_type, preferred_session_type) = \ - self.negotiator.getAllowedType() - return request.answerUnsupported( - message, - preferred_assoc_type, - preferred_session_type) - - - def decodeRequest(self, query): - """Transform query parameters into an L{OpenIDRequest}. - - If the query does not seem to be an OpenID request at all, I return - C{None}. - - @param query: The query parameters as a dictionary with each - key mapping to one value. - @type query: dict - - @raises ProtocolError: When the query does not seem to be a valid - OpenID request. - - @returntype: L{OpenIDRequest} - - @see: L{Decoder.decode} - """ - return self.decoder.decode(query) - - - def encodeResponse(self, response): - """Encode a response to a L{WebResponse}, signing it first if appropriate. - - @raises EncodingError: When I can't figure out how to encode this - message. - - @raises AlreadySigned: When this response is already signed. - - @returntype: L{WebResponse} - - @see: L{SigningEncoder.encode} - """ - return self.encoder.encode(response) - - - -class ProtocolError(Exception): - """A message did not conform to the OpenID protocol. - - @ivar message: The query that is failing to be a valid OpenID request. - @type message: openid.message.Message - """ - - def __init__(self, message, text=None, reference=None, contact=None): - """When an error occurs. - - @param message: The message that is failing to be a valid - OpenID request. - @type message: openid.message.Message - - @param text: A message about the encountered error. Set as C{args[0]}. - @type text: str - """ - self.openid_message = message - self.reference = reference - self.contact = contact - assert type(message) not in [str, unicode] - Exception.__init__(self, text) - - - def getReturnTo(self): - """Get the return_to argument from the request, if any. - - @returntype: str - """ - if self.openid_message is None: - return None - else: - return self.openid_message.getArg(OPENID_NS, 'return_to') - - def hasReturnTo(self): - """Did this request have a return_to parameter? - - @returntype: bool - """ - return self.getReturnTo() is not None - - def toMessage(self): - """Generate a Message object for sending to the relying party, - after encoding. - """ - namespace = self.openid_message.getOpenIDNamespace() - reply = Message(namespace) - reply.setArg(OPENID_NS, 'mode', 'error') - reply.setArg(OPENID_NS, 'error', str(self)) - - if self.contact is not None: - reply.setArg(OPENID_NS, 'contact', str(self.contact)) - - if self.reference is not None: - reply.setArg(OPENID_NS, 'reference', str(self.reference)) - - return reply - - # implements IEncodable - - def encodeToURL(self): - return self.toMessage().toURL(self.getReturnTo()) - - def encodeToKVForm(self): - return self.toMessage().toKVForm() - - def toFormMarkup(self): - """Encode to HTML form markup for POST. - - @since: 2.1.0 - """ - return self.toMessage().toFormMarkup(self.getReturnTo()) - - def toHTML(self): - """Encode to a full HTML page, wrapping the form markup in a page - that will autosubmit the form. - - @since: 2.1.? - """ - return oidutil.autoSubmitHTML(self.toFormMarkup()) - - def whichEncoding(self): - """How should I be encoded? - - @returns: one of ENCODE_URL, ENCODE_KVFORM, or None. If None, - I cannot be encoded as a protocol message and should be - displayed to the user. - """ - if self.hasReturnTo(): - if self.openid_message.getOpenIDNamespace() == OPENID2_NS and \ - len(self.encodeToURL()) > OPENID1_URL_LIMIT: - return ENCODE_HTML_FORM - else: - return ENCODE_URL - - if self.openid_message is None: - return None - - mode = self.openid_message.getArg(OPENID_NS, 'mode') - if mode: - if mode not in BROWSER_REQUEST_MODES: - return ENCODE_KVFORM - - # According to the OpenID spec as of this writing, we are probably - # supposed to switch on request type here (GET versus POST) to figure - # out if we're supposed to print machine-readable or human-readable - # content at this point. GET/POST seems like a pretty lousy way of - # making the distinction though, as it's just as possible that the - # user agent could have mistakenly been directed to post to the - # server URL. - - # Basically, if your request was so broken that you didn't manage to - # include an openid.mode, I'm not going to worry too much about - # returning you something you can't parse. - return None - - - -class VersionError(Exception): - """Raised when an operation was attempted that is not compatible with - the protocol version being used.""" - - - -class NoReturnToError(Exception): - """Raised when a response to a request cannot be generated because - the request contains no return_to URL. - """ - pass - - - -class EncodingError(Exception): - """Could not encode this as a protocol message. - - You should probably render it and show it to the user. - - @ivar response: The response that failed to encode. - @type response: L{OpenIDResponse} - """ - - def __init__(self, response, explanation=None): - Exception.__init__(self, response) - self.response = response - self.explanation = explanation - - def __str__(self): - if self.explanation: - s = '%s: %s' % (self.__class__.__name__, - self.explanation) - else: - s = '%s for Response %s' % ( - self.__class__.__name__, self.response) - return s - - -class AlreadySigned(EncodingError): - """This response is already signed.""" - - - -class UntrustedReturnURL(ProtocolError): - """A return_to is outside the trust_root.""" - - def __init__(self, message, return_to, trust_root): - ProtocolError.__init__(self, message) - self.return_to = return_to - self.trust_root = trust_root - - def __str__(self): - return "return_to %r not under trust_root %r" % (self.return_to, - self.trust_root) - - -class MalformedReturnURL(ProtocolError): - """The return_to URL doesn't look like a valid URL.""" - def __init__(self, openid_message, return_to): - self.return_to = return_to - ProtocolError.__init__(self, openid_message) - - - -class MalformedTrustRoot(ProtocolError): - """The trust root is not well-formed. - - @see: OpenID Specs, U{openid.trust_root<http://openid.net/specs.bml#mode-checkid_immediate>} - """ - pass - - -#class IEncodable: # Interface -# def encodeToURL(return_to): -# """Encode a response as a URL for redirection. -# -# @returns: A URL to direct the user agent back to. -# @returntype: str -# """ -# pass -# -# def encodeToKvform(): -# """Encode a response in key-value colon/newline format. -# -# This is a machine-readable format used to respond to messages which -# came directly from the consumer and not through the user agent. -# -# @see: OpenID Specs, -# U{Key-Value Colon/Newline format<http://openid.net/specs.bml#keyvalue>} -# -# @returntype: str -# """ -# pass -# -# def whichEncoding(): -# """How should I be encoded? -# -# @returns: one of ENCODE_URL, ENCODE_KVFORM, or None. If None, -# I cannot be encoded as a protocol message and should be -# displayed to the user. -# """ -# pass diff --git a/askbot/deps/openid/server/trustroot.py b/askbot/deps/openid/server/trustroot.py deleted file mode 100644 index f78d2992..00000000 --- a/askbot/deps/openid/server/trustroot.py +++ /dev/null @@ -1,454 +0,0 @@ -# -*- test-case-name: openid.test.test_rpverify -*- -""" -This module contains the C{L{TrustRoot}} class, which helps handle -trust root checking. This module is used by the -C{L{openid.server.server}} module, but it is also available to server -implementers who wish to use it for additional trust root checking. - -It also implements relying party return_to URL verification, based on -the realm. -""" - -__all__ = [ - 'TrustRoot', - 'RP_RETURN_TO_URL_TYPE', - 'extractReturnToURLs', - 'returnToMatches', - 'verifyReturnTo', - ] - -from askbot.deps.openid import oidutil -from askbot.deps.openid import urinorm -from askbot.deps.openid.yadis import services - -from urlparse import urlparse, urlunparse -import re - -############################################ -_protocols = ['http', 'https'] -_top_level_domains = [ - 'ac', 'ad', 'ae', 'aero', 'af', 'ag', 'ai', 'al', 'am', 'an', - 'ao', 'aq', 'ar', 'arpa', 'as', 'asia', 'at', 'au', 'aw', - 'ax', 'az', 'ba', 'bb', 'bd', 'be', 'bf', 'bg', 'bh', 'bi', - 'biz', 'bj', 'bm', 'bn', 'bo', 'br', 'bs', 'bt', 'bv', 'bw', - 'by', 'bz', 'ca', 'cat', 'cc', 'cd', 'cf', 'cg', 'ch', 'ci', - 'ck', 'cl', 'cm', 'cn', 'co', 'com', 'coop', 'cr', 'cu', 'cv', - 'cx', 'cy', 'cz', 'de', 'dj', 'dk', 'dm', 'do', 'dz', 'ec', - 'edu', 'ee', 'eg', 'er', 'es', 'et', 'eu', 'fi', 'fj', 'fk', - 'fm', 'fo', 'fr', 'ga', 'gb', 'gd', 'ge', 'gf', 'gg', 'gh', - 'gi', 'gl', 'gm', 'gn', 'gov', 'gp', 'gq', 'gr', 'gs', 'gt', - 'gu', 'gw', 'gy', 'hk', 'hm', 'hn', 'hr', 'ht', 'hu', 'id', - 'ie', 'il', 'im', 'in', 'info', 'int', 'io', 'iq', 'ir', 'is', - 'it', 'je', 'jm', 'jo', 'jobs', 'jp', 'ke', 'kg', 'kh', 'ki', - 'km', 'kn', 'kp', 'kr', 'kw', 'ky', 'kz', 'la', 'lb', 'lc', - 'li', 'lk', 'lr', 'ls', 'lt', 'lu', 'lv', 'ly', 'ma', 'mc', - 'md', 'me', 'mg', 'mh', 'mil', 'mk', 'ml', 'mm', 'mn', 'mo', - 'mobi', 'mp', 'mq', 'mr', 'ms', 'mt', 'mu', 'museum', 'mv', - 'mw', 'mx', 'my', 'mz', 'na', 'name', 'nc', 'ne', 'net', 'nf', - 'ng', 'ni', 'nl', 'no', 'np', 'nr', 'nu', 'nz', 'om', 'org', - 'pa', 'pe', 'pf', 'pg', 'ph', 'pk', 'pl', 'pm', 'pn', 'pr', - 'pro', 'ps', 'pt', 'pw', 'py', 'qa', 're', 'ro', 'rs', 'ru', - 'rw', 'sa', 'sb', 'sc', 'sd', 'se', 'sg', 'sh', 'si', 'sj', - 'sk', 'sl', 'sm', 'sn', 'so', 'sr', 'st', 'su', 'sv', 'sy', - 'sz', 'tc', 'td', 'tel', 'tf', 'tg', 'th', 'tj', 'tk', 'tl', - 'tm', 'tn', 'to', 'tp', 'tr', 'travel', 'tt', 'tv', 'tw', - 'tz', 'ua', 'ug', 'uk', 'us', 'uy', 'uz', 'va', 'vc', 've', - 'vg', 'vi', 'vn', 'vu', 'wf', 'ws', 'xn--0zwm56d', - 'xn--11b5bs3a9aj6g', 'xn--80akhbyknj4f', 'xn--9t4b11yi5a', - 'xn--deba0ad', 'xn--g6w251d', 'xn--hgbk6aj7f53bba', - 'xn--hlcj6aya9esc7a', 'xn--jxalpdlp', 'xn--kgbechtv', - 'xn--zckzah', 'ye', 'yt', 'yu', 'za', 'zm', 'zw'] - -# Build from RFC3986, section 3.2.2. Used to reject hosts with invalid -# characters. -host_segment_re = re.compile( - r"(?:[-a-zA-Z0-9!$&'\(\)\*+,;=._~]|%[a-zA-Z0-9]{2})+$") - -class RealmVerificationRedirected(Exception): - """Attempting to verify this realm resulted in a redirect. - - @since: 2.1.0 - """ - def __init__(self, relying_party_url, rp_url_after_redirects): - self.relying_party_url = relying_party_url - self.rp_url_after_redirects = rp_url_after_redirects - - def __str__(self): - return ("Attempting to verify %r resulted in " - "redirect to %r" % - (self.relying_party_url, - self.rp_url_after_redirects)) - - -def _parseURL(url): - try: - url = urinorm.urinorm(url) - except ValueError: - return None - proto, netloc, path, params, query, frag = urlparse(url) - if not path: - # Python <2.4 does not parse URLs with no path properly - if not query and '?' in netloc: - netloc, query = netloc.split('?', 1) - - path = '/' - - path = urlunparse(('', '', path, params, query, frag)) - - if ':' in netloc: - try: - host, port = netloc.split(':') - except ValueError: - return None - - if not re.match(r'\d+$', port): - return None - else: - host = netloc - port = '' - - host = host.lower() - if not host_segment_re.match(host): - return None - - return proto, host, port, path - -class TrustRoot(object): - """ - This class represents an OpenID trust root. The C{L{parse}} - classmethod accepts a trust root string, producing a - C{L{TrustRoot}} object. The method OpenID server implementers - would be most likely to use is the C{L{isSane}} method, which - checks the trust root for given patterns that indicate that the - trust root is too broad or points to a local network resource. - - @sort: parse, isSane - """ - - def __init__(self, unparsed, proto, wildcard, host, port, path): - self.unparsed = unparsed - self.proto = proto - self.wildcard = wildcard - self.host = host - self.port = port - self.path = path - - def isSane(self): - """ - This method checks the to see if a trust root represents a - reasonable (sane) set of URLs. 'http://*.com/', for example - is not a reasonable pattern, as it cannot meaningfully specify - the site claiming it. This function attempts to find many - related examples, but it can only work via heuristics. - Negative responses from this method should be treated as - advisory, used only to alert the user to examine the trust - root carefully. - - - @return: Whether the trust root is sane - - @rtype: C{bool} - """ - - if self.host == 'localhost': - return True - - host_parts = self.host.split('.') - if self.wildcard: - assert host_parts[0] == '', host_parts - del host_parts[0] - - # If it's an absolute domain name, remove the empty string - # from the end. - if host_parts and not host_parts[-1]: - del host_parts[-1] - - if not host_parts: - return False - - # Do not allow adjacent dots - if '' in host_parts: - return False - - tld = host_parts[-1] - if tld not in _top_level_domains: - return False - - if len(host_parts) == 1: - return False - - if self.wildcard: - if len(tld) == 2 and len(host_parts[-2]) <= 3: - # It's a 2-letter tld with a short second to last segment - # so there needs to be more than two segments specified - # (e.g. *.co.uk is insane) - return len(host_parts) > 2 - - # Passed all tests for insanity. - return True - - def validateURL(self, url): - """ - Validates a URL against this trust root. - - - @param url: The URL to check - - @type url: C{str} - - - @return: Whether the given URL is within this trust root. - - @rtype: C{bool} - """ - - url_parts = _parseURL(url) - if url_parts is None: - return False - - proto, host, port, path = url_parts - - if proto != self.proto: - return False - - if port != self.port: - return False - - if '*' in host: - return False - - if not self.wildcard: - if host != self.host: - return False - elif ((not host.endswith(self.host)) and - ('.' + host) != self.host): - return False - - if path != self.path: - path_len = len(self.path) - trust_prefix = self.path[:path_len] - url_prefix = path[:path_len] - - # must be equal up to the length of the path, at least - if trust_prefix != url_prefix: - return False - - # These characters must be on the boundary between the end - # of the trust root's path and the start of the URL's - # path. - if '?' in self.path: - allowed = '&' - else: - allowed = '?/' - - return (self.path[-1] in allowed or - path[path_len] in allowed) - - return True - - def parse(cls, trust_root): - """ - This method creates a C{L{TrustRoot}} instance from the given - input, if possible. - - - @param trust_root: This is the trust root to parse into a - C{L{TrustRoot}} object. - - @type trust_root: C{str} - - - @return: A C{L{TrustRoot}} instance if trust_root parses as a - trust root, C{None} otherwise. - - @rtype: C{NoneType} or C{L{TrustRoot}} - """ - url_parts = _parseURL(trust_root) - if url_parts is None: - return None - - proto, host, port, path = url_parts - - # check for valid prototype - if proto not in _protocols: - return None - - # check for URI fragment - if path.find('#') != -1: - return None - - # extract wildcard if it is there - if host.find('*', 1) != -1: - # wildcard must be at start of domain: *.foo.com, not foo.*.com - return None - - if host.startswith('*'): - # Starts with star, so must have a dot after it (if a - # domain is specified) - if len(host) > 1 and host[1] != '.': - return None - - host = host[1:] - wilcard = True - else: - wilcard = False - - # we have a valid trust root - tr = cls(trust_root, proto, wilcard, host, port, path) - - return tr - - parse = classmethod(parse) - - def checkSanity(cls, trust_root_string): - """str -> bool - - is this a sane trust root? - """ - trust_root = cls.parse(trust_root_string) - if trust_root is None: - return False - else: - return trust_root.isSane() - - checkSanity = classmethod(checkSanity) - - def checkURL(cls, trust_root, url): - """quick func for validating a url against a trust root. See the - TrustRoot class if you need more control.""" - tr = cls.parse(trust_root) - return tr is not None and tr.validateURL(url) - - checkURL = classmethod(checkURL) - - def buildDiscoveryURL(self): - """Return a discovery URL for this realm. - - This function does not check to make sure that the realm is - valid. Its behaviour on invalid inputs is undefined. - - @rtype: str - - @returns: The URL upon which relying party discovery should be run - in order to verify the return_to URL - - @since: 2.1.0 - """ - if self.wildcard: - # Use "www." in place of the star - assert self.host.startswith('.'), self.host - www_domain = 'www' + self.host - return '%s://%s%s' % (self.proto, www_domain, self.path) - else: - return self.unparsed - - def __repr__(self): - return "TrustRoot(%r, %r, %r, %r, %r, %r)" % ( - self.unparsed, self.proto, self.wildcard, self.host, self.port, - self.path) - - def __str__(self): - return repr(self) - -# The URI for relying party discovery, used in realm verification. -# -# XXX: This should probably live somewhere else (like in -# openid.consumer or openid.yadis somewhere) -RP_RETURN_TO_URL_TYPE = 'http://specs.openid.net/auth/2.0/return_to' - -def _extractReturnURL(endpoint): - """If the endpoint is a relying party OpenID return_to endpoint, - return the endpoint URL. Otherwise, return None. - - This function is intended to be used as a filter for the Yadis - filtering interface. - - @see: C{L{openid.yadis.services}} - @see: C{L{openid.yadis.filters}} - - @param endpoint: An XRDS BasicServiceEndpoint, as returned by - performing Yadis dicovery. - - @returns: The endpoint URL or None if the endpoint is not a - relying party endpoint. - @rtype: str or NoneType - """ - if endpoint.matchTypes([RP_RETURN_TO_URL_TYPE]): - return endpoint.uri - else: - return None - -def returnToMatches(allowed_return_to_urls, return_to): - """Is the return_to URL under one of the supplied allowed - return_to URLs? - - @since: 2.1.0 - """ - - for allowed_return_to in allowed_return_to_urls: - # A return_to pattern works the same as a realm, except that - # it's not allowed to use a wildcard. We'll model this by - # parsing it as a realm, and not trying to match it if it has - # a wildcard. - - return_realm = TrustRoot.parse(allowed_return_to) - if (# Parses as a trust root - return_realm is not None and - - # Does not have a wildcard - not return_realm.wildcard and - - # Matches the return_to that we passed in with it - return_realm.validateURL(return_to) - ): - return True - - # No URL in the list matched - return False - -def getAllowedReturnURLs(relying_party_url): - """Given a relying party discovery URL return a list of return_to URLs. - - @since: 2.1.0 - """ - (rp_url_after_redirects, return_to_urls) = services.getServiceEndpoints( - relying_party_url, _extractReturnURL) - - if rp_url_after_redirects != relying_party_url: - # Verification caused a redirect - raise RealmVerificationRedirected( - relying_party_url, rp_url_after_redirects) - - return return_to_urls - -# _vrfy parameter is there to make testing easier -def verifyReturnTo(realm_str, return_to, _vrfy=getAllowedReturnURLs): - """Verify that a return_to URL is valid for the given realm. - - This function builds a discovery URL, performs Yadis discovery on - it, makes sure that the URL does not redirect, parses out the - return_to URLs, and finally checks to see if the current return_to - URL matches the return_to. - - @raises DiscoveryFailure: When Yadis discovery fails - @returns: True if the return_to URL is valid for the realm - - @since: 2.1.0 - """ - realm = TrustRoot.parse(realm_str) - if realm is None: - # The realm does not parse as a URL pattern - return False - - try: - allowable_urls = _vrfy(realm.buildDiscoveryURL()) - except RealmVerificationRedirected, err: - oidutil.log(str(err)) - return False - - if returnToMatches(allowable_urls, return_to): - return True - else: - oidutil.log("Failed to validate return_to %r for realm %r, was not " - "in %s" % (return_to, realm_str, allowable_urls)) - return False diff --git a/askbot/deps/openid/sreg.py b/askbot/deps/openid/sreg.py deleted file mode 100644 index a002b128..00000000 --- a/askbot/deps/openid/sreg.py +++ /dev/null @@ -1,7 +0,0 @@ -"""moved to L{openid.extensions.sreg}""" - -import warnings -warnings.warn("openid.sreg has moved to openid.extensions.sreg", - DeprecationWarning) - -from askbot.deps.openid.extensions.sreg import * diff --git a/askbot/deps/openid/store/__init__.py b/askbot/deps/openid/store/__init__.py deleted file mode 100644 index 76509b51..00000000 --- a/askbot/deps/openid/store/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -""" -This package contains the modules related to this library's use of -persistent storage. - -@sort: interface, filestore, sqlstore, memstore -""" - -__all__ = ['interface', 'filestore', 'sqlstore', 'memstore', 'nonce'] diff --git a/askbot/deps/openid/store/filestore.py b/askbot/deps/openid/store/filestore.py deleted file mode 100644 index 47de4964..00000000 --- a/askbot/deps/openid/store/filestore.py +++ /dev/null @@ -1,426 +0,0 @@ -""" -This module contains an C{L{OpenIDStore}} implementation backed by -flat files. -""" - -import string -import os -import os.path -import time - -from errno import EEXIST, ENOENT - -try: - from tempfile import mkstemp -except ImportError: - # Python < 2.3 - import warnings - warnings.filterwarnings("ignore", - "tempnam is a potential security risk", - RuntimeWarning, - "openid.store.filestore") - - def mkstemp(dir): - for _ in range(5): - name = os.tempnam(dir) - try: - fd = os.open(name, os.O_CREAT | os.O_EXCL | os.O_RDWR, 0600) - except OSError, why: - if why.errno != EEXIST: - raise - else: - return fd, name - - raise RuntimeError('Failed to get temp file after 5 attempts') - -from askbot.deps.openid.association import Association -from askbot.deps.openid.store.interface import OpenIDStore -from askbot.deps.openid.store import nonce -from askbot.deps.openid import cryptutil, oidutil - -_filename_allowed = string.ascii_letters + string.digits + '.' -try: - # 2.4 - set -except NameError: - try: - # 2.3 - import sets - except ImportError: - # Python < 2.2 - d = {} - for c in _filename_allowed: - d[c] = None - _isFilenameSafe = d.has_key - del d - else: - _isFilenameSafe = sets.Set(_filename_allowed).__contains__ -else: - _isFilenameSafe = set(_filename_allowed).__contains__ - -def _safe64(s): - h64 = oidutil.toBase64(cryptutil.sha1(s)) - h64 = h64.replace('+', '_') - h64 = h64.replace('/', '.') - h64 = h64.replace('=', '') - return h64 - -def _filenameEscape(s): - filename_chunks = [] - for c in s: - if _isFilenameSafe(c): - filename_chunks.append(c) - else: - filename_chunks.append('_%02X' % ord(c)) - return ''.join(filename_chunks) - -def _removeIfPresent(filename): - """Attempt to remove a file, returning whether the file existed at - the time of the call. - - str -> bool - """ - try: - os.unlink(filename) - except OSError, why: - if why.errno == ENOENT: - # Someone beat us to it, but it's gone, so that's OK - return 0 - else: - raise - else: - # File was present - return 1 - -def _ensureDir(dir_name): - """Create dir_name as a directory if it does not exist. If it - exists, make sure that it is, in fact, a directory. - - Can raise OSError - - str -> NoneType - """ - try: - os.makedirs(dir_name) - except OSError, why: - if why.errno != EEXIST or not os.path.isdir(dir_name): - raise - -class FileOpenIDStore(OpenIDStore): - """ - This is a filesystem-based store for OpenID associations and - nonces. This store should be safe for use in concurrent systems - on both windows and unix (excluding NFS filesystems). There are a - couple race conditions in the system, but those failure cases have - been set up in such a way that the worst-case behavior is someone - having to try to log in a second time. - - Most of the methods of this class are implementation details. - People wishing to just use this store need only pay attention to - the C{L{__init__}} method. - - Methods of this object can raise OSError if unexpected filesystem - conditions, such as bad permissions or missing directories, occur. - """ - - def __init__(self, directory): - """ - Initializes a new FileOpenIDStore. This initializes the - nonce and association directories, which are subdirectories of - the directory passed in. - - @param directory: This is the directory to put the store - directories in. - - @type directory: C{str} - """ - # Make absolute - directory = os.path.normpath(os.path.abspath(directory)) - - self.nonce_dir = os.path.join(directory, 'nonces') - - self.association_dir = os.path.join(directory, 'associations') - - # Temp dir must be on the same filesystem as the assciations - # directory - self.temp_dir = os.path.join(directory, 'temp') - - self.max_nonce_age = 6 * 60 * 60 # Six hours, in seconds - - self._setup() - - def _setup(self): - """Make sure that the directories in which we store our data - exist. - - () -> NoneType - """ - _ensureDir(self.nonce_dir) - _ensureDir(self.association_dir) - _ensureDir(self.temp_dir) - - def _mktemp(self): - """Create a temporary file on the same filesystem as - self.association_dir. - - The temporary directory should not be cleaned if there are any - processes using the store. If there is no active process using - the store, it is safe to remove all of the files in the - temporary directory. - - () -> (file, str) - """ - fd, name = mkstemp(dir=self.temp_dir) - try: - file_obj = os.fdopen(fd, 'wb') - return file_obj, name - except: - _removeIfPresent(name) - raise - - def getAssociationFilename(self, server_url, handle): - """Create a unique filename for a given server url and - handle. This implementation does not assume anything about the - format of the handle. The filename that is returned will - contain the domain name from the server URL for ease of human - inspection of the data directory. - - (str, str) -> str - """ - if server_url.find('://') == -1: - raise ValueError('Bad server URL: %r' % server_url) - - proto, rest = server_url.split('://', 1) - domain = _filenameEscape(rest.split('/', 1)[0]) - url_hash = _safe64(server_url) - if handle: - handle_hash = _safe64(handle) - else: - handle_hash = '' - - filename = '%s-%s-%s-%s' % (proto, domain, url_hash, handle_hash) - - return os.path.join(self.association_dir, filename) - - def storeAssociation(self, server_url, association): - """Store an association in the association directory. - - (str, Association) -> NoneType - """ - association_s = association.serialize() - filename = self.getAssociationFilename(server_url, association.handle) - tmp_file, tmp = self._mktemp() - - try: - try: - tmp_file.write(association_s) - os.fsync(tmp_file.fileno()) - finally: - tmp_file.close() - - try: - os.rename(tmp, filename) - except OSError, why: - if why.errno != EEXIST: - raise - - # We only expect EEXIST to happen only on Windows. It's - # possible that we will succeed in unlinking the existing - # file, but not in putting the temporary file in place. - try: - os.unlink(filename) - except OSError, why: - if why.errno == ENOENT: - pass - else: - raise - - # Now the target should not exist. Try renaming again, - # giving up if it fails. - os.rename(tmp, filename) - except: - # If there was an error, don't leave the temporary file - # around. - _removeIfPresent(tmp) - raise - - def getAssociation(self, server_url, handle=None): - """Retrieve an association. If no handle is specified, return - the association with the latest expiration. - - (str, str or NoneType) -> Association or NoneType - """ - if handle is None: - handle = '' - - # The filename with the empty handle is a prefix of all other - # associations for the given server URL. - filename = self.getAssociationFilename(server_url, handle) - - if handle: - return self._getAssociation(filename) - else: - association_files = os.listdir(self.association_dir) - matching_files = [] - # strip off the path to do the comparison - name = os.path.basename(filename) - for association_file in association_files: - if association_file.startswith(name): - matching_files.append(association_file) - - matching_associations = [] - # read the matching files and sort by time issued - for name in matching_files: - full_name = os.path.join(self.association_dir, name) - association = self._getAssociation(full_name) - if association is not None: - matching_associations.append( - (association.issued, association)) - - matching_associations.sort() - - # return the most recently issued one. - if matching_associations: - (_, assoc) = matching_associations[-1] - return assoc - else: - return None - - def _getAssociation(self, filename): - try: - assoc_file = file(filename, 'rb') - except IOError, why: - if why.errno == ENOENT: - # No association exists for that URL and handle - return None - else: - raise - else: - try: - assoc_s = assoc_file.read() - finally: - assoc_file.close() - - try: - association = Association.deserialize(assoc_s) - except ValueError: - _removeIfPresent(filename) - return None - - # Clean up expired associations - if association.getExpiresIn() == 0: - _removeIfPresent(filename) - return None - else: - return association - - def removeAssociation(self, server_url, handle): - """Remove an association if it exists. Do nothing if it does not. - - (str, str) -> bool - """ - assoc = self.getAssociation(server_url, handle) - if assoc is None: - return 0 - else: - filename = self.getAssociationFilename(server_url, handle) - return _removeIfPresent(filename) - - def useNonce(self, server_url, timestamp, salt): - """Return whether this nonce is valid. - - str -> bool - """ - if abs(timestamp - time.time()) > nonce.SKEW: - return False - - if server_url: - proto, rest = server_url.split('://', 1) - else: - # Create empty proto / rest values for empty server_url, - # which is part of a consumer-generated nonce. - proto, rest = '', '' - - domain = _filenameEscape(rest.split('/', 1)[0]) - url_hash = _safe64(server_url) - salt_hash = _safe64(salt) - - filename = '%08x-%s-%s-%s-%s' % (timestamp, proto, domain, - url_hash, salt_hash) - - filename = os.path.join(self.nonce_dir, filename) - try: - fd = os.open(filename, os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0200) - except OSError, why: - if why.errno == EEXIST: - return False - else: - raise - else: - os.close(fd) - return True - - def _allAssocs(self): - all_associations = [] - - association_filenames = map( - lambda filename: os.path.join(self.association_dir, filename), - os.listdir(self.association_dir)) - for association_filename in association_filenames: - try: - association_file = file(association_filename, 'rb') - except IOError, why: - if why.errno == ENOENT: - oidutil.log("%s disappeared during %s._allAssocs" % ( - association_filename, self.__class__.__name__)) - else: - raise - else: - try: - assoc_s = association_file.read() - finally: - association_file.close() - - # Remove expired or corrupted associations - try: - association = Association.deserialize(assoc_s) - except ValueError: - _removeIfPresent(association_filename) - else: - all_associations.append( - (association_filename, association)) - - return all_associations - - def cleanup(self): - """Remove expired entries from the database. This is - potentially expensive, so only run when it is acceptable to - take time. - - () -> NoneType - """ - self.cleanupAssociations() - self.cleanupNonces() - - def cleanupAssociations(self): - removed = 0 - for assoc_filename, assoc in self._allAssocs(): - if assoc.getExpiresIn() == 0: - _removeIfPresent(assoc_filename) - removed += 1 - return removed - - def cleanupNonces(self): - nonces = os.listdir(self.nonce_dir) - now = time.time() - - removed = 0 - # Check all nonces for expiry - for nonce_fname in nonces: - timestamp = nonce_fname.split('-', 1)[0] - timestamp = int(timestamp, 16) - if abs(timestamp - now) > nonce.SKEW: - filename = os.path.join(self.nonce_dir, nonce_fname) - _removeIfPresent(filename) - removed += 1 - return removed diff --git a/askbot/deps/openid/store/interface.py b/askbot/deps/openid/store/interface.py deleted file mode 100644 index bb90972f..00000000 --- a/askbot/deps/openid/store/interface.py +++ /dev/null @@ -1,197 +0,0 @@ -""" -This module contains the definition of the C{L{OpenIDStore}} -interface. -""" - -class OpenIDStore(object): - """ - This is the interface for the store objects the OpenID library - uses. It is a single class that provides all of the persistence - mechanisms that the OpenID library needs, for both servers and - consumers. - - @change: Version 2.0 removed the C{storeNonce}, C{getAuthKey}, and C{isDumb} - methods, and changed the behavior of the C{L{useNonce}} method - to support one-way nonces. It added C{L{cleanupNonces}}, - C{L{cleanupAssociations}}, and C{L{cleanup}}. - - @sort: storeAssociation, getAssociation, removeAssociation, - useNonce - """ - - def storeAssociation(self, server_url, association): - """ - This method puts a C{L{Association - <openid.association.Association>}} object into storage, - retrievable by server URL and handle. - - - @param server_url: The URL of the identity server that this - association is with. Because of the way the server - portion of the library uses this interface, don't assume - there are any limitations on the character set of the - input string. In particular, expect to see unescaped - non-url-safe characters in the server_url field. - - @type server_url: C{str} - - - @param association: The C{L{Association - <openid.association.Association>}} to store. - - @type association: C{L{Association - <openid.association.Association>}} - - - @return: C{None} - - @rtype: C{NoneType} - """ - raise NotImplementedError - - def getAssociation(self, server_url, handle=None): - """ - This method returns an C{L{Association - <openid.association.Association>}} object from storage that - matches the server URL and, if specified, handle. It returns - C{None} if no such association is found or if the matching - association is expired. - - If no handle is specified, the store may return any - association which matches the server URL. If multiple - associations are valid, the recommended return value for this - method is the one most recently issued. - - This method is allowed (and encouraged) to garbage collect - expired associations when found. This method must not return - expired associations. - - - @param server_url: The URL of the identity server to get the - association for. Because of the way the server portion of - the library uses this interface, don't assume there are - any limitations on the character set of the input string. - In particular, expect to see unescaped non-url-safe - characters in the server_url field. - - @type server_url: C{str} - - - @param handle: This optional parameter is the handle of the - specific association to get. If no specific handle is - provided, any valid association matching the server URL is - returned. - - @type handle: C{str} or C{NoneType} - - - @return: The C{L{Association - <openid.association.Association>}} for the given identity - server. - - @rtype: C{L{Association <openid.association.Association>}} or - C{NoneType} - """ - raise NotImplementedError - - def removeAssociation(self, server_url, handle): - """ - This method removes the matching association if it's found, - and returns whether the association was removed or not. - - - @param server_url: The URL of the identity server the - association to remove belongs to. Because of the way the - server portion of the library uses this interface, don't - assume there are any limitations on the character set of - the input string. In particular, expect to see unescaped - non-url-safe characters in the server_url field. - - @type server_url: C{str} - - - @param handle: This is the handle of the association to - remove. If there isn't an association found that matches - both the given URL and handle, then there was no matching - handle found. - - @type handle: C{str} - - - @return: Returns whether or not the given association existed. - - @rtype: C{bool} or C{int} - """ - raise NotImplementedError - - def useNonce(self, server_url, timestamp, salt): - """Called when using a nonce. - - This method should return C{True} if the nonce has not been - used before, and store it for a while to make sure nobody - tries to use the same value again. If the nonce has already - been used or the timestamp is not current, return C{False}. - - You may use L{openid.store.nonce.SKEW} for your timestamp window. - - @change: In earlier versions, round-trip nonces were used and - a nonce was only valid if it had been previously stored - with C{storeNonce}. Version 2.0 uses one-way nonces, - requiring a different implementation here that does not - depend on a C{storeNonce} call. (C{storeNonce} is no - longer part of the interface.) - - @param server_url: The URL of the server from which the nonce - originated. - - @type server_url: C{str} - - @param timestamp: The time that the nonce was created (to the - nearest second), in seconds since January 1 1970 UTC. - @type timestamp: C{int} - - @param salt: A random string that makes two nonces from the - same server issued during the same second unique. - @type salt: str - - @return: Whether or not the nonce was valid. - - @rtype: C{bool} - """ - raise NotImplementedError - - def cleanupNonces(self): - """Remove expired nonces from the store. - - Discards any nonce from storage that is old enough that its - timestamp would not pass L{useNonce}. - - This method is not called in the normal operation of the - library. It provides a way for store admins to keep - their storage from filling up with expired data. - - @return: the number of nonces expired. - @returntype: int - """ - raise NotImplementedError - - def cleanupAssociations(self): - """Remove expired associations from the store. - - This method is not called in the normal operation of the - library. It provides a way for store admins to keep - their storage from filling up with expired data. - - @return: the number of associations expired. - @returntype: int - """ - raise NotImplementedError - - def cleanup(self): - """Shortcut for C{L{cleanupNonces}()}, C{L{cleanupAssociations}()}. - - This method is not called in the normal operation of the - library. It provides a way for store admins to keep - their storage from filling up with expired data. - """ - return self.cleanupNonces(), self.cleanupAssociations() diff --git a/askbot/deps/openid/store/memstore.py b/askbot/deps/openid/store/memstore.py deleted file mode 100644 index a088c0d0..00000000 --- a/askbot/deps/openid/store/memstore.py +++ /dev/null @@ -1,125 +0,0 @@ -"""A simple store using only in-process memory.""" - -from askbot.deps.openid.store import nonce - -import copy -import time - -class ServerAssocs(object): - def __init__(self): - self.assocs = {} - - def set(self, assoc): - self.assocs[assoc.handle] = assoc - - def get(self, handle): - return self.assocs.get(handle) - - def remove(self, handle): - try: - del self.assocs[handle] - except KeyError: - return False - else: - return True - - def best(self): - """Returns association with the oldest issued date. - - or None if there are no associations. - """ - best = None - for assoc in self.assocs.values(): - if best is None or best.issued < assoc.issued: - best = assoc - return best - - def cleanup(self): - """Remove expired associations. - - @return: tuple of (removed associations, remaining associations) - """ - remove = [] - for handle, assoc in self.assocs.iteritems(): - if assoc.getExpiresIn() == 0: - remove.append(handle) - for handle in remove: - del self.assocs[handle] - return len(remove), len(self.assocs) - - - -class MemoryStore(object): - """In-process memory store. - - Use for single long-running processes. No persistence supplied. - """ - def __init__(self): - self.server_assocs = {} - self.nonces = {} - - def _getServerAssocs(self, server_url): - try: - return self.server_assocs[server_url] - except KeyError: - assocs = self.server_assocs[server_url] = ServerAssocs() - return assocs - - def storeAssociation(self, server_url, assoc): - assocs = self._getServerAssocs(server_url) - assocs.set(copy.deepcopy(assoc)) - - def getAssociation(self, server_url, handle=None): - assocs = self._getServerAssocs(server_url) - if handle is None: - return assocs.best() - else: - return assocs.get(handle) - - def removeAssociation(self, server_url, handle): - assocs = self._getServerAssocs(server_url) - return assocs.remove(handle) - - def useNonce(self, server_url, timestamp, salt): - if abs(timestamp - time.time()) > nonce.SKEW: - return False - - anonce = (str(server_url), int(timestamp), str(salt)) - if anonce in self.nonces: - return False - else: - self.nonces[anonce] = None - return True - - def cleanupNonces(self): - now = time.time() - expired = [] - for anonce in self.nonces.iterkeys(): - if abs(anonce[1] - now) > nonce.SKEW: - # removing items while iterating over the set could be bad. - expired.append(anonce) - - for anonce in expired: - del self.nonces[anonce] - return len(expired) - - def cleanupAssociations(self): - remove_urls = [] - removed_assocs = 0 - for server_url, assocs in self.server_assocs.iteritems(): - removed, remaining = assocs.cleanup() - removed_assocs += removed - if not remaining: - remove_urls.append(server_url) - - # Remove entries from server_assocs that had none remaining. - for server_url in remove_urls: - del self.server_assocs[server_url] - return removed_assocs - - def __eq__(self, other): - return ((self.server_assocs == other.server_assocs) and - (self.nonces == other.nonces)) - - def __ne__(self, other): - return not (self == other) diff --git a/askbot/deps/openid/store/nonce.py b/askbot/deps/openid/store/nonce.py deleted file mode 100644 index 7847f9fa..00000000 --- a/askbot/deps/openid/store/nonce.py +++ /dev/null @@ -1,98 +0,0 @@ -__all__ = [ - 'split', - 'mkNonce', - 'checkTimestamp', - ] - -from askbot.deps.openid import cryptutil -from time import strptime, strftime, gmtime, time -from calendar import timegm -import string - -NONCE_CHARS = string.ascii_letters + string.digits - -# Keep nonces for five hours (allow five hours for the combination of -# request time and clock skew). This is probably way more than is -# necessary, but there is not much overhead in storing nonces. -SKEW = 60 * 60 * 5 - -time_fmt = '%Y-%m-%dT%H:%M:%SZ' -time_str_len = len('0000-00-00T00:00:00Z') - -def split(nonce_string): - """Extract a timestamp from the given nonce string - - @param nonce_string: the nonce from which to extract the timestamp - @type nonce_string: str - - @returns: A pair of a Unix timestamp and the salt characters - @returntype: (int, str) - - @raises ValueError: if the nonce does not start with a correctly - formatted time string - """ - timestamp_str = nonce_string[:time_str_len] - try: - timestamp = timegm(strptime(timestamp_str, time_fmt)) - except AssertionError: # Python 2.2 - timestamp = -1 - if timestamp < 0: - raise ValueError('time out of range') - return timestamp, nonce_string[time_str_len:] - -def checkTimestamp(nonce_string, allowed_skew=SKEW, now=None): - """Is the timestamp that is part of the specified nonce string - within the allowed clock-skew of the current time? - - @param nonce_string: The nonce that is being checked - @type nonce_string: str - - @param allowed_skew: How many seconds should be allowed for - completing the request, allowing for clock skew. - @type allowed_skew: int - - @param now: The current time, as a Unix timestamp - @type now: int - - @returntype: bool - @returns: Whether the timestamp is correctly formatted and within - the allowed skew of the current time. - """ - try: - stamp, _ = split(nonce_string) - except ValueError: - return False - else: - if now is None: - now = time() - - # Time after which we should not use the nonce - past = now - allowed_skew - - # Time that is too far in the future for us to allow - future = now + allowed_skew - - # the stamp is not too far in the future and is not too far in - # the past - return past <= stamp <= future - -def mkNonce(when=None): - """Generate a nonce with the current timestamp - - @param when: Unix timestamp representing the issue time of the - nonce. Defaults to the current time. - @type when: int - - @returntype: str - @returns: A string that should be usable as a one-way nonce - - @see: time - """ - salt = cryptutil.randomString(6, NONCE_CHARS) - if when is None: - t = gmtime() - else: - t = gmtime(when) - - time_str = strftime(time_fmt, t) - return time_str + salt diff --git a/askbot/deps/openid/store/sqlstore.py b/askbot/deps/openid/store/sqlstore.py deleted file mode 100644 index cceebd58..00000000 --- a/askbot/deps/openid/store/sqlstore.py +++ /dev/null @@ -1,516 +0,0 @@ -""" -This module contains C{L{OpenIDStore}} implementations that use -various SQL databases to back them. - -Example of how to initialize a store database:: - - python -c 'from askbot.deps.openid.store import sqlstore; import pysqlite2.dbapi2; sqlstore.SQLiteStore(pysqlite2.dbapi2.connect("cstore.db")).createTables()' -""" -import re -import time - -from askbot.deps.openid.association import Association -from askbot.deps.openid.store.interface import OpenIDStore -from askbot.deps.openid.store import nonce - -def _inTxn(func): - def wrapped(self, *args, **kwargs): - return self._callInTransaction(func, self, *args, **kwargs) - - if hasattr(func, '__name__'): - try: - wrapped.__name__ = func.__name__[4:] - except TypeError: - pass - - if hasattr(func, '__doc__'): - wrapped.__doc__ = func.__doc__ - - return wrapped - -class SQLStore(OpenIDStore): - """ - This is the parent class for the SQL stores, which contains the - logic common to all of the SQL stores. - - The table names used are determined by the class variables - C{L{associations_table}} and - C{L{nonces_table}}. To change the name of the tables used, pass - new table names into the constructor. - - To create the tables with the proper schema, see the - C{L{createTables}} method. - - This class shouldn't be used directly. Use one of its subclasses - instead, as those contain the code necessary to use a specific - database. - - All methods other than C{L{__init__}} and C{L{createTables}} - should be considered implementation details. - - - @cvar associations_table: This is the default name of the table to - keep associations in - - @cvar nonces_table: This is the default name of the table to keep - nonces in. - - - @sort: __init__, createTables - """ - - associations_table = 'oid_associations' - nonces_table = 'oid_nonces' - - def __init__(self, conn, associations_table=None, nonces_table=None): - """ - This creates a new SQLStore instance. It requires an - established database connection be given to it, and it allows - overriding the default table names. - - - @param conn: This must be an established connection to a - database of the correct type for the SQLStore subclass - you're using. - - @type conn: A python database API compatible connection - object. - - - @param associations_table: This is an optional parameter to - specify the name of the table used for storing - associations. The default value is specified in - C{L{SQLStore.associations_table}}. - - @type associations_table: C{str} - - - @param nonces_table: This is an optional parameter to specify - the name of the table used for storing nonces. The - default value is specified in C{L{SQLStore.nonces_table}}. - - @type nonces_table: C{str} - """ - self.conn = conn - self.cur = None - self._statement_cache = {} - self._table_names = { - 'associations': associations_table or self.associations_table, - 'nonces': nonces_table or self.nonces_table, - } - self.max_nonce_age = 6 * 60 * 60 # Six hours, in seconds - - # DB API extension: search for "Connection Attributes .Error, - # .ProgrammingError, etc." in - # http://www.python.org/dev/peps/pep-0249/ - if (hasattr(self.conn, 'IntegrityError') and - hasattr(self.conn, 'OperationalError')): - self.exceptions = self.conn - - if not (hasattr(self.exceptions, 'IntegrityError') and - hasattr(self.exceptions, 'OperationalError')): - raise RuntimeError("Error using database connection module " - "(Maybe it can't be imported?)") - - def blobDecode(self, blob): - """Convert a blob as returned by the SQL engine into a str object. - - str -> str""" - return blob - - def blobEncode(self, s): - """Convert a str object into the necessary object for storing - in the database as a blob.""" - return s - - def _getSQL(self, sql_name): - try: - return self._statement_cache[sql_name] - except KeyError: - sql = getattr(self, sql_name) - sql %= self._table_names - self._statement_cache[sql_name] = sql - return sql - - def _execSQL(self, sql_name, *args): - sql = self._getSQL(sql_name) - # Kludge because we have reports of postgresql not quoting - # arguments if they are passed in as unicode instead of str. - # Currently the strings in our tables just have ascii in them, - # so this ought to be safe. - def unicode_to_str(arg): - if isinstance(arg, unicode): - return str(arg) - else: - return arg - str_args = map(unicode_to_str, args) - self.cur.execute(sql, str_args) - - def __getattr__(self, attr): - # if the attribute starts with db_, use a default - # implementation that looks up the appropriate SQL statement - # as an attribute of this object and executes it. - if attr[:3] == 'db_': - sql_name = attr[3:] + '_sql' - def func(*args): - return self._execSQL(sql_name, *args) - setattr(self, attr, func) - return func - else: - raise AttributeError('Attribute %r not found' % (attr,)) - - def _callInTransaction(self, func, *args, **kwargs): - """Execute the given function inside of a transaction, with an - open cursor. If no exception is raised, the transaction is - comitted, otherwise it is rolled back.""" - # No nesting of transactions - self.conn.rollback() - - try: - self.cur = self.conn.cursor() - try: - ret = func(*args, **kwargs) - finally: - self.cur.close() - self.cur = None - except: - self.conn.rollback() - raise - else: - self.conn.commit() - - return ret - - def txn_createTables(self): - """ - This method creates the database tables necessary for this - store to work. It should not be called if the tables already - exist. - """ - self.db_create_nonce() - self.db_create_assoc() - - createTables = _inTxn(txn_createTables) - - def txn_storeAssociation(self, server_url, association): - """Set the association for the server URL. - - Association -> NoneType - """ - a = association - self.db_set_assoc( - server_url, - a.handle, - self.blobEncode(a.secret), - a.issued, - a.lifetime, - a.assoc_type) - - storeAssociation = _inTxn(txn_storeAssociation) - - def txn_getAssociation(self, server_url, handle=None): - """Get the most recent association that has been set for this - server URL and handle. - - str -> NoneType or Association - """ - if handle is not None: - self.db_get_assoc(server_url, handle) - else: - self.db_get_assocs(server_url) - - rows = self.cur.fetchall() - if len(rows) == 0: - return None - else: - associations = [] - for values in rows: - assoc = Association(*values) - assoc.secret = self.blobDecode(assoc.secret) - if assoc.getExpiresIn() == 0: - self.txn_removeAssociation(server_url, assoc.handle) - else: - associations.append((assoc.issued, assoc)) - - if associations: - associations.sort() - return associations[-1][1] - else: - return None - - getAssociation = _inTxn(txn_getAssociation) - - def txn_removeAssociation(self, server_url, handle): - """Remove the association for the given server URL and handle, - returning whether the association existed at all. - - (str, str) -> bool - """ - self.db_remove_assoc(server_url, handle) - return self.cur.rowcount > 0 # -1 is undefined - - removeAssociation = _inTxn(txn_removeAssociation) - - def txn_useNonce(self, server_url, timestamp, salt): - """Return whether this nonce is present, and if it is, then - remove it from the set. - - str -> bool""" - if abs(timestamp - time.time()) > nonce.SKEW: - return False - - try: - self.db_add_nonce(server_url, timestamp, salt) - except self.exceptions.IntegrityError: - # The key uniqueness check failed - return False - else: - # The nonce was successfully added - return True - - useNonce = _inTxn(txn_useNonce) - - def txn_cleanupNonces(self): - self.db_clean_nonce(int(time.time()) - nonce.SKEW) - return self.cur.rowcount - - cleanupNonces = _inTxn(txn_cleanupNonces) - - def txn_cleanupAssociations(self): - self.db_clean_assoc(int(time.time())) - return self.cur.rowcount - - cleanupAssociations = _inTxn(txn_cleanupAssociations) - - -class SQLiteStore(SQLStore): - """ - This is an SQLite-based specialization of C{L{SQLStore}}. - - To create an instance, see C{L{SQLStore.__init__}}. To create the - tables it will use, see C{L{SQLStore.createTables}}. - - All other methods are implementation details. - """ - - create_nonce_sql = """ - CREATE TABLE %(nonces)s ( - server_url VARCHAR, - timestamp INTEGER, - salt CHAR(40), - UNIQUE(server_url, timestamp, salt) - ); - """ - - create_assoc_sql = """ - CREATE TABLE %(associations)s - ( - server_url VARCHAR(2047), - handle VARCHAR(255), - secret BLOB(128), - issued INTEGER, - lifetime INTEGER, - assoc_type VARCHAR(64), - PRIMARY KEY (server_url, handle) - ); - """ - - set_assoc_sql = ('INSERT OR REPLACE INTO %(associations)s ' - '(server_url, handle, secret, issued, ' - 'lifetime, assoc_type) ' - 'VALUES (?, ?, ?, ?, ?, ?);') - get_assocs_sql = ('SELECT handle, secret, issued, lifetime, assoc_type ' - 'FROM %(associations)s WHERE server_url = ?;') - get_assoc_sql = ( - 'SELECT handle, secret, issued, lifetime, assoc_type ' - 'FROM %(associations)s WHERE server_url = ? AND handle = ?;') - - get_expired_sql = ('SELECT server_url ' - 'FROM %(associations)s WHERE issued + lifetime < ?;') - - remove_assoc_sql = ('DELETE FROM %(associations)s ' - 'WHERE server_url = ? AND handle = ?;') - - clean_assoc_sql = 'DELETE FROM %(associations)s WHERE issued + lifetime < ?;' - - add_nonce_sql = 'INSERT INTO %(nonces)s VALUES (?, ?, ?);' - - clean_nonce_sql = 'DELETE FROM %(nonces)s WHERE timestamp < ?;' - - def blobDecode(self, buf): - return str(buf) - - def blobEncode(self, s): - return buffer(s) - - def useNonce(self, *args, **kwargs): - # Older versions of the sqlite wrapper do not raise - # IntegrityError as they should, so we have to detect the - # message from the OperationalError. - try: - return super(SQLiteStore, self).useNonce(*args, **kwargs) - except self.exceptions.OperationalError, why: - if re.match('^columns .* are not unique$', why[0]): - return False - else: - raise - -class MySQLStore(SQLStore): - """ - This is a MySQL-based specialization of C{L{SQLStore}}. - - Uses InnoDB tables for transaction support. - - To create an instance, see C{L{SQLStore.__init__}}. To create the - tables it will use, see C{L{SQLStore.createTables}}. - - All other methods are implementation details. - """ - - try: - import MySQLdb as exceptions - except ImportError: - exceptions = None - - create_nonce_sql = """ - CREATE TABLE %(nonces)s ( - server_url BLOB NOT NULL, - timestamp INTEGER NOT NULL, - salt CHAR(40) NOT NULL, - PRIMARY KEY (server_url(255), timestamp, salt) - ) - ENGINE=InnoDB; - """ - - create_assoc_sql = """ - CREATE TABLE %(associations)s - ( - server_url BLOB NOT NULL, - handle VARCHAR(255) NOT NULL, - secret BLOB NOT NULL, - issued INTEGER NOT NULL, - lifetime INTEGER NOT NULL, - assoc_type VARCHAR(64) NOT NULL, - PRIMARY KEY (server_url(255), handle) - ) - ENGINE=InnoDB; - """ - - set_assoc_sql = ('REPLACE INTO %(associations)s ' - 'VALUES (%%s, %%s, %%s, %%s, %%s, %%s);') - get_assocs_sql = ('SELECT handle, secret, issued, lifetime, assoc_type' - ' FROM %(associations)s WHERE server_url = %%s;') - get_expired_sql = ('SELECT server_url ' - 'FROM %(associations)s WHERE issued + lifetime < %%s;') - - get_assoc_sql = ( - 'SELECT handle, secret, issued, lifetime, assoc_type' - ' FROM %(associations)s WHERE server_url = %%s AND handle = %%s;') - remove_assoc_sql = ('DELETE FROM %(associations)s ' - 'WHERE server_url = %%s AND handle = %%s;') - - clean_assoc_sql = 'DELETE FROM %(associations)s WHERE issued + lifetime < %%s;' - - add_nonce_sql = 'INSERT INTO %(nonces)s VALUES (%%s, %%s, %%s);' - - clean_nonce_sql = 'DELETE FROM %(nonces)s WHERE timestamp < %%s;' - - def blobDecode(self, blob): - if type(blob) is str: - # Versions of MySQLdb >= 1.2.2 - return blob - else: - # Versions of MySQLdb prior to 1.2.2 (as far as we can tell) - return blob.tostring() - -class PostgreSQLStore(SQLStore): - """ - This is a PostgreSQL-based specialization of C{L{SQLStore}}. - - To create an instance, see C{L{SQLStore.__init__}}. To create the - tables it will use, see C{L{SQLStore.createTables}}. - - All other methods are implementation details. - """ - - try: - import psycopg as exceptions - except ImportError: - # psycopg2 has the dbapi extension where the exception classes - # are available on the connection object. A psycopg2 - # connection will use the correct exception classes because of - # this, and a psycopg connection will fall through to use the - # psycopg imported above. - exceptions = None - - create_nonce_sql = """ - CREATE TABLE %(nonces)s ( - server_url VARCHAR(2047) NOT NULL, - timestamp INTEGER NOT NULL, - salt CHAR(40) NOT NULL, - PRIMARY KEY (server_url, timestamp, salt) - ); - """ - - create_assoc_sql = """ - CREATE TABLE %(associations)s - ( - server_url VARCHAR(2047) NOT NULL, - handle VARCHAR(255) NOT NULL, - secret BYTEA NOT NULL, - issued INTEGER NOT NULL, - lifetime INTEGER NOT NULL, - assoc_type VARCHAR(64) NOT NULL, - PRIMARY KEY (server_url, handle), - CONSTRAINT secret_length_constraint CHECK (LENGTH(secret) <= 128) - ); - """ - - def db_set_assoc(self, server_url, handle, secret, issued, lifetime, assoc_type): - """ - Set an association. This is implemented as a method because - REPLACE INTO is not supported by PostgreSQL (and is not - standard SQL). - """ - result = self.db_get_assoc(server_url, handle) - rows = self.cur.fetchall() - if len(rows): - # Update the table since this associations already exists. - return self.db_update_assoc(secret, issued, lifetime, assoc_type, - server_url, handle) - else: - # Insert a new record because this association wasn't - # found. - return self.db_new_assoc(server_url, handle, secret, issued, - lifetime, assoc_type) - - new_assoc_sql = ('INSERT INTO %(associations)s ' - 'VALUES (%%s, %%s, %%s, %%s, %%s, %%s);') - update_assoc_sql = ('UPDATE %(associations)s SET ' - 'secret = %%s, issued = %%s, ' - 'lifetime = %%s, assoc_type = %%s ' - 'WHERE server_url = %%s AND handle = %%s;') - get_assocs_sql = ('SELECT handle, secret, issued, lifetime, assoc_type' - ' FROM %(associations)s WHERE server_url = %%s;') - get_expired_sql = ('SELECT server_url ' - 'FROM %(associations)s WHERE issued + lifetime < %%s;') - - get_assoc_sql = ( - 'SELECT handle, secret, issued, lifetime, assoc_type' - ' FROM %(associations)s WHERE server_url = %%s AND handle = %%s;') - remove_assoc_sql = ('DELETE FROM %(associations)s ' - 'WHERE server_url = %%s AND handle = %%s;') - - clean_assoc_sql = 'DELETE FROM %(associations)s WHERE issued + lifetime < %%s;' - - add_nonce_sql = 'INSERT INTO %(nonces)s VALUES (%%s, %%s, %%s);' - - clean_nonce_sql = 'DELETE FROM %(nonces)s WHERE timestamp < %%s;' - - def blobEncode(self, blob): - try: - from psycopg2 import Binary - except ImportError: - from psycopg import Binary - - return Binary(blob) diff --git a/askbot/deps/openid/test/__init__.py b/askbot/deps/openid/test/__init__.py deleted file mode 100644 index e69de29b..00000000 --- a/askbot/deps/openid/test/__init__.py +++ /dev/null diff --git a/askbot/deps/openid/test/cryptutil.py b/askbot/deps/openid/test/cryptutil.py deleted file mode 100644 index 62d59ce2..00000000 --- a/askbot/deps/openid/test/cryptutil.py +++ /dev/null @@ -1,108 +0,0 @@ -import sys -import random -import os.path - -from askbot.deps.openid import cryptutil - -# Most of the purpose of this test is to make sure that cryptutil can -# find a good source of randomness on this machine. - -def test_cryptrand(): - # It's possible, but HIGHLY unlikely that a correct implementation - # will fail by returning the same number twice - - s = cryptutil.getBytes(32) - t = cryptutil.getBytes(32) - assert len(s) == 32 - assert len(t) == 32 - assert s != t - - a = cryptutil.randrange(2L ** 128) - b = cryptutil.randrange(2L ** 128) - assert type(a) is long - assert type(b) is long - assert b != a - - # Make sure that we can generate random numbers that are larger - # than platform int size - cryptutil.randrange(long(sys.maxint) + 1L) - -def test_reversed(): - if hasattr(cryptutil, 'reversed'): - cases = [ - ('', ''), - ('a', 'a'), - ('ab', 'ba'), - ('abc', 'cba'), - ('abcdefg', 'gfedcba'), - ([], []), - ([1], [1]), - ([1,2], [2,1]), - ([1,2,3], [3,2,1]), - (range(1000), range(999, -1, -1)), - ] - - for case, expected in cases: - expected = list(expected) - actual = list(cryptutil.reversed(case)) - assert actual == expected, (case, expected, actual) - twice = list(cryptutil.reversed(actual)) - assert twice == list(case), (actual, case, twice) - -def test_binaryLongConvert(): - MAX = sys.maxint - for iteration in xrange(500): - n = 0L - for i in range(10): - n += long(random.randrange(MAX)) - - s = cryptutil.longToBinary(n) - assert type(s) is str - n_prime = cryptutil.binaryToLong(s) - assert n == n_prime, (n, n_prime) - - cases = [ - ('\x00', 0L), - ('\x01', 1L), - ('\x7F', 127L), - ('\x00\xFF', 255L), - ('\x00\x80', 128L), - ('\x00\x81', 129L), - ('\x00\x80\x00', 32768L), - ('OpenID is cool', 1611215304203901150134421257416556L) - ] - - for s, n in cases: - n_prime = cryptutil.binaryToLong(s) - s_prime = cryptutil.longToBinary(n) - assert n == n_prime, (s, n, n_prime) - assert s == s_prime, (n, s, s_prime) - -def test_longToBase64(): - f = file(os.path.join(os.path.dirname(__file__), 'n2b64')) - try: - for line in f: - parts = line.strip().split(' ') - assert parts[0] == cryptutil.longToBase64(long(parts[1])) - finally: - f.close() - -def test_base64ToLong(): - f = file(os.path.join(os.path.dirname(__file__), 'n2b64')) - try: - for line in f: - parts = line.strip().split(' ') - assert long(parts[1]) == cryptutil.base64ToLong(parts[0]) - finally: - f.close() - - -def test(): - test_reversed() - test_binaryLongConvert() - test_cryptrand() - test_longToBase64() - test_base64ToLong() - -if __name__ == '__main__': - test() diff --git a/askbot/deps/openid/test/data/accept.txt b/askbot/deps/openid/test/data/accept.txt deleted file mode 100644 index 0853321a..00000000 --- a/askbot/deps/openid/test/data/accept.txt +++ /dev/null @@ -1,118 +0,0 @@ -# Accept: [Accept: header value from RFC2616, -# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html] -# Available: [whitespace-separated content types] -# Expected: [Accept-header like list, containing the available content -# types with their q-values] - -Accept: */* -Available: text/plain -Expected: text/plain; q=1.0 - -Accept: */* -Available: text/plain, text/html -Expected: text/plain; q=1.0, text/html; q=1.0 - -# The order matters -Accept: */* -Available: text/html, text/plain -Expected: text/html; q=1.0, text/plain; q=1.0 - -Accept: text/*, */*; q=0.9 -Available: text/plain, image/jpeg -Expected: text/plain; q=1.0, image/jpeg; q=0.9 - -Accept: text/*, */*; q=0.9 -Available: image/jpeg, text/plain -Expected: text/plain; q=1.0, image/jpeg; q=0.9 - -# wildcard subtypes still reject differing main types -Accept: text/* -Available: image/jpeg, text/plain -Expected: text/plain; q=1.0 - -Accept: text/html -Available: text/html -Expected: text/html; q=1.0 - -Accept: text/html, text/* -Available: text/html -Expected: text/html; q=1.0 - -Accept: text/html, text/* -Available: text/plain, text/html -Expected: text/plain; q=1.0, text/html; q=1.0 - -Accept: text/html, text/*; q=0.9 -Available: text/plain, text/html -Expected: text/html; q=1.0, text/plain; q=0.9 - -# If a more specific type has a higher q-value, then the higher value wins -Accept: text/*; q=0.9, text/html -Available: text/plain, text/html -Expected: text/html; q=1.0, text/plain; q=0.9 - -Accept: */*, text/*; q=0.9, text/html; q=0.1 -Available: text/plain, text/html, image/monkeys -Expected: image/monkeys; q=1.0, text/plain; q=0.9, text/html; q=0.1 - -Accept: text/*, text/html; q=0 -Available: text/html -Expected: - -Accept: text/*, text/html; q=0 -Available: text/html, text/plain -Expected: text/plain; q=1.0 - -Accept: text/html -Available: text/plain -Expected: - -Accept: application/xrds+xml, text/html; q=0.9 -Available: application/xrds+xml, text/html -Expected: application/xrds+xml; q=1.0, text/html; q=0.9 - -Accept: application/xrds+xml, */*; q=0.9 -Available: application/xrds+xml, text/html -Expected: application/xrds+xml; q=1.0, text/html; q=0.9 - -Accept: application/xrds+xml, application/xhtml+xml; q=0.9, text/html; q=0.8, text/xml; q=0.7 -Available: application/xrds+xml, text/html -Expected: application/xrds+xml; q=1.0, text/html; q=0.8 - -# See http://www.rfc-editor.org/rfc/rfc3023.txt, section A.13 -Accept: application/xrds -Available: application/xrds+xml -Expected: - -Accept: application/xrds+xml -Available: application/xrds -Expected: - -Accept: application/xml -Available: application/xrds+xml -Expected: - -Available: application/xrds+xml -Accept: application/xml -Expected: - - - -################################################# -# The tests below this line are documentation of how this library -# works. If the implementation changes, it's acceptable to change the -# test to reflect that. These are specified so that we can make sure -# that the current implementation actually works the way that we -# expect it to given these inputs. - -Accept: text/html;level=1 -Available: text/html -Expected: text/html; q=1.0 - -Accept: text/html; level=1, text/html; level=9; q=0.1 -Available: text/html -Expected: text/html; q=1.0 - -Accept: text/html; level=9; q=0.1, text/html; level=1 -Available: text/html -Expected: text/html; q=1.0 diff --git a/askbot/deps/openid/test/data/example-xrds.xml b/askbot/deps/openid/test/data/example-xrds.xml deleted file mode 100644 index 101ba3bd..00000000 --- a/askbot/deps/openid/test/data/example-xrds.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Sample XRDS file at: NAME --> -<xrds:XRDS - xmlns:xrds="xri://$xrds" - xmlns="xri://$xrd*($v*2.0)"> - <XRD> - - <Service priority="0"> - <Type>http://example.com/</Type> - <URI>http://www.openidenabled.com/</URI> - </Service> - - </XRD> -</xrds:XRDS> diff --git a/askbot/deps/openid/test/data/openid-1.2-consumer-sqlitestore.db b/askbot/deps/openid/test/data/openid-1.2-consumer-sqlitestore.db Binary files differdeleted file mode 100644 index 11444b26..00000000 --- a/askbot/deps/openid/test/data/openid-1.2-consumer-sqlitestore.db +++ /dev/null diff --git a/askbot/deps/openid/test/data/test1-discover.txt b/askbot/deps/openid/test/data/test1-discover.txt deleted file mode 100644 index 7ec9b878..00000000 --- a/askbot/deps/openid/test/data/test1-discover.txt +++ /dev/null @@ -1,137 +0,0 @@ -equiv -Status: 200 OK -Content-Type: text/html - -<html> -<head> -<meta http-equiv="YADIS_HEADER" content="URL_BASE/xrds"> -<title>Joe Schmoe's Homepage</title> -</head> -<body> -<h1>Joe Schmoe's Homepage</h1> -<p>Blah blah blah blah blah blah blah</p> -</body> -</html> - -header -Status: 200 OK -Content-Type: text/html -YADIS_HEADER: URL_BASE/xrds - -<html> -<head> -<title>Joe Schmoe's Homepage</title> -</head> -<body> -<h1>Joe Schmoe's Homepage</h1> -<p>Blah blah blah blah blah blah blah</p> -</body> - -xrds -Status: 200 OK -Content-Type: application/xrds+xml - -<XRDS Content> - -xrds_ctparam -Status: 200 OK -Content-Type: application/xrds+xml; charset=UTF8 - -<XRDS Content> - -xrds_ctcase -Status: 200 OK -Content-Type: appliCATION/XRDS+xml - -<XRDS Content> - -xrds_html -Status: 200 OK -Content-Type: text/html - -<XRDS Content> - -redir_equiv -Status: 302 Found -Content-Type: text/plain -Location: URL_BASE/equiv - -You are presently being redirected. - -redir_header -Status: 302 Found -Content-Type: text/plain -Location: URL_BASE/header - -You are presently being redirected. - -redir_xrds -Status: 302 Found -Content-Type: application/xrds+xml -Location: URL_BASE/xrds - -<XRDS Content> - -redir_xrds_html -Status: 302 Found -Content-Type: text/plain -Location: URL_BASE/xrds_html - -You are presently being redirected. - -redir_redir_equiv -Status: 302 Found -Content-Type: text/plain -Location: URL_BASE/redir_equiv - -You are presently being redirected. - -lowercase_header -Status: 200 OK -Content-Type: text/html -x-xrds-location: URL_BASE/xrds - -<html> -<head> -<title>Joe Schmoe's Homepage</title> -</head> -<body> -<h1>Joe Schmoe's Homepage</h1> -<p>Blah blah blah blah blah blah blah</p> -</body> - -404_server_response -Status: 404 Not Found - -EEk! - -500_server_response -Status: 500 Server error - -EEk! - -201_server_response -Status: 201 Created - -EEk! - -404_with_header -Status: 404 Not Found -YADIS_HEADER: URL_BASE/xrds - -EEk! - -404_with_meta -Status: 404 Not Found -Content-Type: text/html - -<html> -<head> -<meta http-equiv="YADIS_HEADER" content="URL_BASE/xrds"> -<title>Joe Schmoe's Homepage</title> -</head> -<body> -<h1>Joe Schmoe's Homepage</h1> -<p>Blah blah blah blah blah blah blah</p> -</body> -</html> diff --git a/askbot/deps/openid/test/data/test1-parsehtml.txt b/askbot/deps/openid/test/data/test1-parsehtml.txt deleted file mode 100644 index feee692e..00000000 --- a/askbot/deps/openid/test/data/test1-parsehtml.txt +++ /dev/null @@ -1,152 +0,0 @@ -found -<!-- minimal well-formed success case --> -<html><head><meta http-equiv="X-XRDS-Location" content="found"></head></html> - -found -<!-- minimal well-formed success case, xhtml closing, whitespace --> -<html><head><meta http-equiv="X-XRDS-Location" content="found" /></head></html> - -found -<!-- minimal well-formed success case, xhtml closing, no whitespace --> -<html><head><meta http-equiv="X-XRDS-Location" content="found"/></head></html> - -found -<!-- minimal success case --> -<html><head><meta http-equiv="X-XRDS-Location" content="found"> - -found -<!-- ignore bogus top-level tags --> -</porky><html><head><meta http-equiv="X-XRDS-Location" content="found"> - -found -<!-- Case folding for header name --> -<html><head><meta http-equiv="x-xrds-location" content="found"> - -found -<!-- missing <html> tag --> -<head><meta http-equiv="X-XRDS-Location" content="found"> - -found -<!-- javascript in head --> -<html><head><script type="text/javascript">document.write("<body>");</script><META http-equiv="X-XRDS-Location" content="found"> - -EOF -<!-- no closing script tag --> -<html><head><script type="text/javascript">document.write("<body>");<META http-equiv="X-XRDS-Location" content="found"> - -found -<!-- case folding for tag names --> -<html><head><META http-equiv="X-XRDS-Location" content="found"> - -found -<!-- Stop after first one found --> -<html><head> -<meta http-equiv="x-xrds-location" content="found"> -<meta http-equiv="x-xrds-location" content="not-found"> - -& -<!-- standard entity --> -<head><meta http-equiv="X-XRDS-Location" content="&"> - -found -<!-- hex entity --> -<html> - <head> - <meta http-equiv="X-XRDS-Location" content="found"> - </head> -</html> - -found -<!-- decimal entity --> -<html> - <head> - <meta http-equiv="X-XRDS-Location" content="found"> - </head> -</html> - -/ -<!-- hex entity --> -<html> - <head> - <meta http-equiv="X-XRDS-Location" content="/"> - </head> -</html> - - -<!-- empty string --> -<html><head><meta http-equiv="X-XRDS-Location" content=""> - -EOF -<!-- No markup, except this comment --> - -None -<!-- No meta, just standard HTML --> -<html> - <head> - <title>A boring document</title> - </head> - <body> - <h1>A boring document</h1> - <p>There's really nothing interesting about this</p> - </body> -</html> - -EOF -<!-- No <html> or <head> --> -<meta http-equiv="X-XRDS-Location" content="found"> - -EOF -<!-- No <head> tag --> -<html><meta http-equiv="X-XRDS-Location" content="found"> - -None -<!-- No <html> or <head> and a <body> --> -<body><meta http-equiv="X-XRDS-Location" content="found"> - -None -<!-- <head> and <html> reversed --> -<head><html><meta http-equiv="X-XRDS-Location" content="found"> - -None -<!-- <meta> is inside of <body> --> -<html><head><body><meta http-equiv="X-XRDS-Location" content="found"> - -None -<!-- <meta> is inside comment --> -<html> - <head> - <!--<meta http-equiv="X-XRDS-Location" content="found">--> - </head> -</html> - -None -<!-- <meta> is inside of <body> --> -<html> - <head> - <title>Someone's blog</title> - </head> - <body> - <h1>My blog</h1> - <p>This is my blog</p> - <h2>Comments</h2> - <p><meta http-equiv="X-XRDS-Location" content="found"></p> - </body> -</html> - -None -<!-- short head tag --> -<html><head/> -<meta http-equiv="X-XRDS-Location" content="found"> - -None -<!-- <body> comes first --> -<body><html><head> -<meta http-equiv="X-XRDS-Location" content="found"> - -None -<!-- </body> comes first --> -</body><html><head> -<meta http-equiv="X-XRDS-Location" content="found"> - -None -<!bad processing instruction!> diff --git a/askbot/deps/openid/test/data/test_discover/openid.html b/askbot/deps/openid/test/data/test_discover/openid.html deleted file mode 100644 index 1a57d44f..00000000 --- a/askbot/deps/openid/test/data/test_discover/openid.html +++ /dev/null @@ -1,11 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> -<html> - <head> - <title>Identity Page for Smoker</title> - <link rel="openid.server" href="http://www.myopenid.com/server" /> - <link rel="openid.delegate" href="http://smoker.myopenid.com/" /> - </head> - <body> - <p>foo</p> - </body> -</html> diff --git a/askbot/deps/openid/test/data/test_discover/openid2.html b/askbot/deps/openid/test/data/test_discover/openid2.html deleted file mode 100644 index a74c042e..00000000 --- a/askbot/deps/openid/test/data/test_discover/openid2.html +++ /dev/null @@ -1,11 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> -<html> - <head> - <title>Identity Page for Smoker</title> - <link rel="openid2.provider" href="http://www.myopenid.com/server" /> - <link rel="openid2.local_id" href="http://smoker.myopenid.com/" /> - </head> - <body> - <p>foo</p> - </body> -</html> diff --git a/askbot/deps/openid/test/data/test_discover/openid2_xrds.xml b/askbot/deps/openid/test/data/test_discover/openid2_xrds.xml deleted file mode 100644 index 8091ab94..00000000 --- a/askbot/deps/openid/test/data/test_discover/openid2_xrds.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<xrds:XRDS xmlns:xrds="xri://$xrds" - xmlns="xri://$xrd*($v*2.0)" - > - <XRD> - <Service priority="10"> - <Type>http://specs.openid.net/auth/2.0/signon</Type> - <URI>http://www.myopenid.com/server</URI> - <LocalID>http://smoker.myopenid.com/</LocalID> - </Service> - </XRD> -</xrds:XRDS> diff --git a/askbot/deps/openid/test/data/test_discover/openid2_xrds_no_local_id.xml b/askbot/deps/openid/test/data/test_discover/openid2_xrds_no_local_id.xml deleted file mode 100644 index e6a0eb97..00000000 --- a/askbot/deps/openid/test/data/test_discover/openid2_xrds_no_local_id.xml +++ /dev/null @@ -1,11 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<xrds:XRDS xmlns:xrds="xri://$xrds" - xmlns="xri://$xrd*($v*2.0)" - > - <XRD> - <Service priority="10"> - <Type>http://specs.openid.net/auth/2.0/signon</Type> - <URI>http://www.myopenid.com/server</URI> - </Service> - </XRD> -</xrds:XRDS> diff --git a/askbot/deps/openid/test/data/test_discover/openid_1_and_2.html b/askbot/deps/openid/test/data/test_discover/openid_1_and_2.html deleted file mode 100644 index 5e581287..00000000 --- a/askbot/deps/openid/test/data/test_discover/openid_1_and_2.html +++ /dev/null @@ -1,11 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> -<html> - <head> - <title>Identity Page for Smoker</title> - <link rel="openid2.provider openid.server" href="http://www.myopenid.com/server" /> - <link rel="openid2.local_id openid.delegate" href="http://smoker.myopenid.com/" /> - </head> - <body> - <p>foo</p> - </body> -</html> diff --git a/askbot/deps/openid/test/data/test_discover/openid_1_and_2_xrds.xml b/askbot/deps/openid/test/data/test_discover/openid_1_and_2_xrds.xml deleted file mode 100644 index 6d85d57e..00000000 --- a/askbot/deps/openid/test/data/test_discover/openid_1_and_2_xrds.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<xrds:XRDS xmlns:xrds="xri://$xrds" - xmlns="xri://$xrd*($v*2.0)" - xmlns:openid="http://openid.net/xmlns/1.0" - > - <XRD> - - <Service priority="10"> - <Type>http://specs.openid.net/auth/2.0/signon</Type> - <Type>http://openid.net/signon/1.1</Type> - <URI>http://www.myopenid.com/server</URI> - <LocalID>http://smoker.myopenid.com/</LocalID> - <openid:Delegate>http://smoker.myopenid.com/</openid:Delegate> - </Service> - </XRD> -</xrds:XRDS> diff --git a/askbot/deps/openid/test/data/test_discover/openid_1_and_2_xrds_bad_delegate.xml b/askbot/deps/openid/test/data/test_discover/openid_1_and_2_xrds_bad_delegate.xml deleted file mode 100644 index db7282e2..00000000 --- a/askbot/deps/openid/test/data/test_discover/openid_1_and_2_xrds_bad_delegate.xml +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<xrds:XRDS xmlns:xrds="xri://$xrds" - xmlns="xri://$xrd*($v*2.0)" - xmlns:openid="http://openid.net/xmlns/1.0" - > - <XRD> - - <Service priority="10"> - <Type>http://specs.openid.net/auth/2.0/signon</Type> - <Type>http://openid.net/signon/1.0</Type> - <Type>http://openid.net/signon/1.1</Type> - <URI>http://www.myopenid.com/server</URI> - <LocalID>http://smoker.myopenid.com/</LocalID> - <openid:Delegate>http://localid.mismatch.invalid/</openid:Delegate> - </Service> - </XRD> -</xrds:XRDS> diff --git a/askbot/deps/openid/test/data/test_discover/openid_and_yadis.html b/askbot/deps/openid/test/data/test_discover/openid_and_yadis.html deleted file mode 100644 index 3befa6fc..00000000 --- a/askbot/deps/openid/test/data/test_discover/openid_and_yadis.html +++ /dev/null @@ -1,12 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> -<html> - <head> - <title>Identity Page for Smoker</title> - <meta http-equiv="X-XRDS-Location" content="http://someuser.unittest/xrds" /> - <link rel="openid.server" href="http://www.myopenid.com/server" /> - <link rel="openid.delegate" href="http://smoker.myopenid.com/" /> - </head> - <body> - <p>foo</p> - </body> -</html> diff --git a/askbot/deps/openid/test/data/test_discover/openid_no_delegate.html b/askbot/deps/openid/test/data/test_discover/openid_no_delegate.html deleted file mode 100644 index f5180b3d..00000000 --- a/askbot/deps/openid/test/data/test_discover/openid_no_delegate.html +++ /dev/null @@ -1,10 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> -<html> - <head> - <title>Identity Page for Smoker</title> - <link rel="openid.server" href="http://www.myopenid.com/server" /> - </head> - <body> - <p>foo</p> - </body> -</html> diff --git a/askbot/deps/openid/test/data/test_discover/yadis_0entries.xml b/askbot/deps/openid/test/data/test_discover/yadis_0entries.xml deleted file mode 100644 index f161a0b3..00000000 --- a/askbot/deps/openid/test/data/test_discover/yadis_0entries.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<xrds:XRDS xmlns:xrds="xri://$xrds" - xmlns="xri://$xrd*($v*2.0)" - xmlns:openid="http://openid.net/xmlns/1.0" - > - <XRD> - <Service > - <Type>http://is-not-openid.unittest/</Type> - <URI>http://noffing.unittest./</URI> - </Service> - </XRD> -</xrds:XRDS> diff --git a/askbot/deps/openid/test/data/test_discover/yadis_2_bad_local_id.xml b/askbot/deps/openid/test/data/test_discover/yadis_2_bad_local_id.xml deleted file mode 100644 index 68c2ce1f..00000000 --- a/askbot/deps/openid/test/data/test_discover/yadis_2_bad_local_id.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<xrds:XRDS xmlns:xrds="xri://$xrds" - xmlns="xri://$xrd*($v*2.0)" - xmlns:openid="http://openid.net/xmlns/1.0" - > - <XRD> - - <Service priority="10"> - <Type>http://specs.openid.net/auth/2.0/signon</Type> - <URI>http://www.myopenid.com/server</URI> - <LocalID>http://smoker.myopenid.com/</LocalID> - <LocalID>http://localid.mismatch.invalid/</LocalID> - </Service> - </XRD> -</xrds:XRDS> diff --git a/askbot/deps/openid/test/data/test_discover/yadis_2entries_delegate.xml b/askbot/deps/openid/test/data/test_discover/yadis_2entries_delegate.xml deleted file mode 100644 index 372955b0..00000000 --- a/askbot/deps/openid/test/data/test_discover/yadis_2entries_delegate.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<xrds:XRDS xmlns:xrds="xri://$xrds" - xmlns="xri://$xrd*($v*2.0)" - xmlns:openid="http://openid.net/xmlns/1.0" - > - <XRD> - <CanonicalID>=!1000</CanonicalID> - - <Service priority="10"> - <Type>http://openid.net/signon/1.0</Type> - <URI>http://www.myopenid.com/server</URI> - <openid:Delegate>http://smoker.myopenid.com/</openid:Delegate> - </Service> - - <Service priority="20"> - <Type>http://openid.net/signon/1.0</Type> - <URI>http://www.livejournal.com/openid/server.bml</URI> - <openid:Delegate>http://frank.livejournal.com/</openid:Delegate> - </Service> - - </XRD> -</xrds:XRDS> diff --git a/askbot/deps/openid/test/data/test_discover/yadis_2entries_idp.xml b/askbot/deps/openid/test/data/test_discover/yadis_2entries_idp.xml deleted file mode 100644 index 9a07b3d4..00000000 --- a/askbot/deps/openid/test/data/test_discover/yadis_2entries_idp.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<xrds:XRDS xmlns:xrds="xri://$xrds" - xmlns="xri://$xrd*($v*2.0)" - xmlns:openid="http://openid.net/xmlns/1.0" - > - <XRD> - <CanonicalID>=!1000</CanonicalID> - - <Service priority="10"> - <Type>http://specs.openid.net/auth/2.0/signon</Type> - <URI>http://www.myopenid.com/server</URI> - <openid:LocalID>http://smoker.myopenid.com/</openid:LocalID> - </Service> - - <Service priority="20"> - <Type>http://specs.openid.net/auth/2.0/server</Type> - <URI>http://www.livejournal.com/openid/server.bml</URI> - </Service> - - </XRD> -</xrds:XRDS> diff --git a/askbot/deps/openid/test/data/test_discover/yadis_another_delegate.xml b/askbot/deps/openid/test/data/test_discover/yadis_another_delegate.xml deleted file mode 100644 index 2f3b9af3..00000000 --- a/askbot/deps/openid/test/data/test_discover/yadis_another_delegate.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<xrds:XRDS xmlns:xrds="xri://$xrds" - xmlns="xri://$xrd*($v*2.0)" - xmlns:openid="http://openid.net/xmlns/1.0" - > - <XRD> - - <Service priority="10"> - <Type>http://openid.net/signon/1.0</Type> - <URI>http://vroom.unittest/server</URI> - <openid:Delegate>http://smoker.myopenid.com/</openid:Delegate> - </Service> - </XRD> -</xrds:XRDS> diff --git a/askbot/deps/openid/test/data/test_discover/yadis_idp.xml b/askbot/deps/openid/test/data/test_discover/yadis_idp.xml deleted file mode 100644 index f570d043..00000000 --- a/askbot/deps/openid/test/data/test_discover/yadis_idp.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<xrds:XRDS xmlns:xrds="xri://$xrds" - xmlns="xri://$xrd*($v*2.0)" - xmlns:openid="http://openid.net/xmlns/1.0" - > - <XRD> - <Service priority="10"> - <Type>http://specs.openid.net/auth/2.0/server</Type> - <URI>http://www.myopenid.com/server</URI> - </Service> - </XRD> -</xrds:XRDS> diff --git a/askbot/deps/openid/test/data/test_discover/yadis_idp_delegate.xml b/askbot/deps/openid/test/data/test_discover/yadis_idp_delegate.xml deleted file mode 100644 index 54106005..00000000 --- a/askbot/deps/openid/test/data/test_discover/yadis_idp_delegate.xml +++ /dev/null @@ -1,13 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<xrds:XRDS xmlns:xrds="xri://$xrds" - xmlns="xri://$xrd*($v*2.0)" - xmlns:openid="http://openid.net/xmlns/1.0" - > - <XRD> - <Service> - <Type>http://specs.openid.net/auth/2.0/server</Type> - <URI>http://www.myopenid.com/server</URI> - <openid:Delegate>http://smoker.myopenid.com/</openid:Delegate> - </Service> - </XRD> -</xrds:XRDS> diff --git a/askbot/deps/openid/test/data/test_discover/yadis_no_delegate.xml b/askbot/deps/openid/test/data/test_discover/yadis_no_delegate.xml deleted file mode 100644 index fbd67349..00000000 --- a/askbot/deps/openid/test/data/test_discover/yadis_no_delegate.xml +++ /dev/null @@ -1,11 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<xrds:XRDS xmlns:xrds="xri://$xrds" - xmlns="xri://$xrd*($v*2.0)" - > - <XRD> - <Service priority="10"> - <Type>http://openid.net/signon/1.0</Type> - <URI>http://www.myopenid.com/server</URI> - </Service> - </XRD> -</xrds:XRDS> diff --git a/askbot/deps/openid/test/data/test_etxrd/README b/askbot/deps/openid/test/data/test_etxrd/README deleted file mode 100644 index 3739cf1c..00000000 --- a/askbot/deps/openid/test/data/test_etxrd/README +++ /dev/null @@ -1,12 +0,0 @@ -delegated-20060809.xrds - results from proxy.xri.net, determined by - Drummond and Kevin to be incorrect. -delegated-20060809-r1.xrds - Drummond's 1st correction -delegated-20060809-r2.xrds - Drummond's 2nd correction - -spoofs: keturn's (=!E4)'s attempts to log in with Drummond's i-number (=!D2) -spoof1.xrds -spoof2.xrds -spoof3.xrds - attempt to steal @!C0!D2 by having "at least one" CanonicalID - match the $res service ProviderID. - -ref.xrds - resolving @ootao*test.ref, which refers to a neustar XRI. diff --git a/askbot/deps/openid/test/data/test_etxrd/delegated-20060809-r1.xrds b/askbot/deps/openid/test/data/test_etxrd/delegated-20060809-r1.xrds deleted file mode 100644 index f994b140..00000000 --- a/askbot/deps/openid/test/data/test_etxrd/delegated-20060809-r1.xrds +++ /dev/null @@ -1,34 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<XRDS ref="xri://@ootao*test1" xmlns="xri://$xrds"> - <XRD xmlns="xri://$xrd*($v*2.0)"> - <Query>*ootao</Query> - <Status code="100"/> - <Expires>2006-08-09T22:07:13.000Z</Expires> - <ProviderID>xri://@</ProviderID> - <LocalID priority="10">!5BAD.2AA.3C72.AF46</LocalID> - <CanonicalID priority="10">@!5BAD.2AA.3C72.AF46</CanonicalID> - <Service priority="10"> - <Type>xri://$res*auth*($v*2.0)</Type> - <ProviderID>xri://!!1003</ProviderID> - <MediaType>application/xrds+xml;trust=none</MediaType> - <URI priority="10">http://resolve.ezibroker.net/resolve/@ootao/</URI> - </Service> - <Service priority="10"> - <Type select="true">http://openid.net/signon/1.0</Type> - <ProviderID/> - <URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI> - </Service> - </XRD> - <XRD xmlns="xri://$xrd*($v*2.0)"> - <Query>*test1</Query> - <Status code="100">SUCCESS</Status> - <ProviderID>xri://!!1003</ProviderID> - <LocalID>!0000.0000.3B9A.CA01</LocalID> - <CanonicalID>@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01</CanonicalID> - <Service> - <Type select="true">http://openid.net/signon/1.0</Type> - <ProviderID/> - <URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI> - </Service> - </XRD> -</XRDS> diff --git a/askbot/deps/openid/test/data/test_etxrd/delegated-20060809-r2.xrds b/askbot/deps/openid/test/data/test_etxrd/delegated-20060809-r2.xrds deleted file mode 100644 index 68c08dc4..00000000 --- a/askbot/deps/openid/test/data/test_etxrd/delegated-20060809-r2.xrds +++ /dev/null @@ -1,34 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<XRDS ref="xri://@ootao*test1" xmlns="xri://$xrds"> - <XRD xmlns="xri://$xrd*($v*2.0)"> - <Query>*ootao</Query> - <Status code="100"/> - <Expires>2006-08-09T22:07:13.000Z</Expires> - <ProviderID>xri://@</ProviderID> - <LocalID priority="10">!5BAD.2AA.3C72.AF46</LocalID> - <CanonicalID priority="10">@!5BAD.2AA.3C72.AF46</CanonicalID> - <Service priority="10"> - <Type>xri://$res*auth*($v*2.0)</Type> - <ProviderID>xri://@!5BAD.2AA.3C72.AF46</ProviderID> - <MediaType>application/xrds+xml;trust=none</MediaType> - <URI priority="10">http://resolve.ezibroker.net/resolve/@ootao/</URI> - </Service> - <Service priority="10"> - <Type select="true">http://openid.net/signon/1.0</Type> - <ProviderID/> - <URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI> - </Service> - </XRD> - <XRD xmlns="xri://$xrd*($v*2.0)"> - <Query>*test1</Query> - <Status code="100">SUCCESS</Status> - <ProviderID>xri://@!5BAD.2AA.3C72.AF46</ProviderID> - <LocalID>!0000.0000.3B9A.CA01</LocalID> - <CanonicalID>@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01</CanonicalID> - <Service> - <Type select="true">http://openid.net/signon/1.0</Type> - <ProviderID/> - <URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI> - </Service> - </XRD> -</XRDS> diff --git a/askbot/deps/openid/test/data/test_etxrd/delegated-20060809.xrds b/askbot/deps/openid/test/data/test_etxrd/delegated-20060809.xrds deleted file mode 100644 index 073ee688..00000000 --- a/askbot/deps/openid/test/data/test_etxrd/delegated-20060809.xrds +++ /dev/null @@ -1,34 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<XRDS ref="xri://@ootao*test1" xmlns="xri://$xrds"> - <XRD xmlns="xri://$xrd*($v*2.0)"> - <Query>*ootao</Query> - <Status code="100"/> - <Expires>2006-08-09T22:07:13.000Z</Expires> - <ProviderID>xri://@</ProviderID> - <LocalID priority="10">!5BAD.2AA.3C72.AF46</LocalID> - <CanonicalID priority="10">@!5BAD.2AA.3C72.AF46</CanonicalID> - <Service priority="10"> - <Type>xri://$res*auth*($v*2.0)</Type> - <ProviderID/> - <MediaType>application/xrds+xml;trust=none</MediaType> - <URI priority="10">http://resolve.ezibroker.net/resolve/@ootao/</URI> - </Service> - <Service priority="10"> - <Type select="true">http://openid.net/signon/1.0</Type> - <ProviderID/> - <URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI> - </Service> - </XRD> - <XRD xmlns="xri://$xrd*($v*2.0)"> - <Query>*test1</Query> - <Status code="100">SUCCESS</Status> - <ProviderID>xri://!!1003</ProviderID> - <LocalID>!0000.0000.3B9A.CA01</LocalID> - <CanonicalID>@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01</CanonicalID> - <Service> - <Type select="true">http://openid.net/signon/1.0</Type> - <ProviderID/> - <URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI> - </Service> - </XRD> -</XRDS> diff --git a/askbot/deps/openid/test/data/test_etxrd/no-xrd.xml b/askbot/deps/openid/test/data/test_etxrd/no-xrd.xml deleted file mode 100644 index ca66f735..00000000 --- a/askbot/deps/openid/test/data/test_etxrd/no-xrd.xml +++ /dev/null @@ -1,7 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<xrds:XRDS - xmlns:xrds="xri://$xrds" - xmlns:openid="http://openid.net/xmlns/1.0" - xmlns:typekey="http://typekey.com/xmlns/1.0" - xmlns="xri://$xrd*($v*2.0)"> -</xrds:XRDS> diff --git a/askbot/deps/openid/test/data/test_etxrd/not-xrds.xml b/askbot/deps/openid/test/data/test_etxrd/not-xrds.xml deleted file mode 100644 index 7f5bfd51..00000000 --- a/askbot/deps/openid/test/data/test_etxrd/not-xrds.xml +++ /dev/null @@ -1,2 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<x></x> diff --git a/askbot/deps/openid/test/data/test_etxrd/prefixsometimes.xrds b/askbot/deps/openid/test/data/test_etxrd/prefixsometimes.xrds deleted file mode 100644 index 5522a6e5..00000000 --- a/askbot/deps/openid/test/data/test_etxrd/prefixsometimes.xrds +++ /dev/null @@ -1,34 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<XRDS ref="xri://@ootao*test1" xmlns="xri://$xrds"> - <XRD xmlns="xri://$xrd*($v*2.0)"> - <Query>*ootao</Query> - <Status code="100"/> - <Expires>2006-08-09T22:07:13.000Z</Expires> - <ProviderID>xri://@</ProviderID> - <LocalID priority="10">!5BAD.2AA.3C72.AF46</LocalID> - <CanonicalID priority="10">@!5BAD.2AA.3C72.AF46</CanonicalID> - <Service priority="10"> - <Type>xri://$res*auth*($v*2.0)</Type> - <ProviderID>xri://@!5BAD.2AA.3C72.AF46</ProviderID> - <MediaType>application/xrds+xml;trust=none</MediaType> - <URI priority="10">http://resolve.ezibroker.net/resolve/@ootao/</URI> - </Service> - <Service priority="10"> - <Type select="true">http://openid.net/signon/1.0</Type> - <ProviderID/> - <URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI> - </Service> - </XRD> - <XRD xmlns="xri://$xrd*($v*2.0)"> - <Query>*test1</Query> - <Status code="100">SUCCESS</Status> - <ProviderID>xri://@!5BAD.2AA.3C72.AF46</ProviderID> - <LocalID>!0000.0000.3B9A.CA01</LocalID> - <CanonicalID>xri://@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01</CanonicalID> - <Service> - <Type select="true">http://openid.net/signon/1.0</Type> - <ProviderID/> - <URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI> - </Service> - </XRD> -</XRDS> diff --git a/askbot/deps/openid/test/data/test_etxrd/ref.xrds b/askbot/deps/openid/test/data/test_etxrd/ref.xrds deleted file mode 100644 index 69cf683d..00000000 --- a/askbot/deps/openid/test/data/test_etxrd/ref.xrds +++ /dev/null @@ -1,109 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<XRDS ref="xri://@ootao*test.ref" xmlns="xri://$xrds"> - <XRD xmlns="xri://$xrd*($v*2.0)"> - <Query>*ootao</Query> - <Status code="100"/> - <Expires>2006-08-15T18:56:09.000Z</Expires> - <ProviderID>xri://@</ProviderID> - <LocalID priority="10">!5BAD.2AA.3C72.AF46</LocalID> - <CanonicalID priority="10">@!5BAD.2AA.3C72.AF46</CanonicalID> - <Service priority="10"> - <Type>xri://$res*auth*($v*2.0)</Type> - <ProviderID/> - <MediaType>application/xrds+xml;trust=none</MediaType> - <URI priority="10">http://resolve.ezibroker.net/resolve/@ootao/</URI> - </Service> - <Service priority="10"> - <Type select="true">http://openid.net/signon/1.0</Type> - <ProviderID/> - <URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI> - </Service> - </XRD> - <XRD xmlns="xri://$xrd*($v*2.0)"> - <Query>*test.ref</Query> - <Status code="100">SUCCESS</Status> - <ProviderID>xri://!!1003</ProviderID> - <LocalID>!0000.0000.3B9A.CA03</LocalID> - <CanonicalID>@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA03</CanonicalID> - <Ref>@!BAE.A650.823B.2475</Ref> - <Service> - <Type select="true">http://openid.net/signon/1.0</Type> - <ProviderID/> - <URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI> - </Service> - </XRD> - <XRDS ref="xri://@!BAE.A650.823B.2475" xmlns="xri://$xrds"> - <XRD xmlns="xri://$xrd*($v*2.0)"> - <Query>!BAE.A650.823B.2475</Query> - <Status code="100"/> - <Expires>2006-08-15T18:56:10.000Z</Expires> - <ProviderID>xri://@</ProviderID> - <LocalID priority="10">!BAE.A650.823B.2475</LocalID> - <CanonicalID priority="10">@!BAE.A650.823B.2475</CanonicalID> - <Service priority="10"> - <Type select="true">(+wdnc)</Type> - <ProviderID/> - <Path select="true">(+wdnc)</Path> - <URI append="none" priority="10">http://www.tcpacompliance.us</URI> - </Service> - <Service priority="10"> - <Type match="content" select="true">xri://$res*auth*($v*2.0)</Type> - <ProviderID/> - <MediaType match="content" select="false">application/xrds+xml;trust=none</MediaType> - <URI priority="10">http://dev.dready.org/cgi-bin/xri</URI> - </Service> - <Service priority="10"> - <Type match="content" select="true">(+i-name)</Type> - <ProviderID/> - <Path match="content" select="true">(+i-name)</Path> - <URI append="none" priority="10">http://www.inames.net</URI> - </Service> - <Service priority="10"> - <Type select="true">xri://+i-service*(+contact)*($v*1.0)</Type> - <Type match="default" select="false"/> - <ProviderID>xri://!!1001</ProviderID> - <Path select="true">(+contact)</Path> - <Path match="null" select="false"/> - <MediaType select="false">text/html</MediaType> - <MediaType match="default" select="false"/> - <URI append="none" priority="10">http://www.neustar.biz</URI> - </Service> - </XRD> - </XRDS> - <XRD xmlns="xri://$xrd*($v*2.0)"> - <Query>!BAE.A650.823B.2475</Query> - <Status code="100"/> - <Expires>2006-08-15T18:56:10.000Z</Expires> - <ProviderID>xri://@</ProviderID> - <LocalID priority="10">!BAE.A650.823B.2475</LocalID> - <CanonicalID priority="10">@!BAE.A650.823B.2475</CanonicalID> - <Service priority="10"> - <Type select="true">(+wdnc)</Type> - <ProviderID/> - <Path select="true">(+wdnc)</Path> - <URI append="none" priority="10">http://www.tcpacompliance.us</URI> - </Service> - <Service priority="10"> - <Type match="content" select="true">xri://$res*auth*($v*2.0)</Type> - <ProviderID/> - <MediaType match="content" select="false">application/xrds+xml;trust=none</MediaType> - <URI priority="10">http://dev.dready.org/cgi-bin/xri</URI> - </Service> - <Service priority="10"> - <Type match="content" select="true">(+i-name)</Type> - <ProviderID/> - <Path match="content" select="true">(+i-name)</Path> - <URI append="none" priority="10">http://www.inames.net</URI> - </Service> - <Service priority="10"> - <Type select="true">xri://+i-service*(+contact)*($v*1.0)</Type> - <Type match="default" select="false"/> - <ProviderID>xri://!!1001</ProviderID> - <Path select="true">(+contact)</Path> - <Path match="null" select="false"/> - <MediaType select="false">text/html</MediaType> - <MediaType match="default" select="false"/> - <URI append="none" priority="10">http://www.neustar.biz</URI> - </Service> - </XRD> -</XRDS>
\ No newline at end of file diff --git a/askbot/deps/openid/test/data/test_etxrd/sometimesprefix.xrds b/askbot/deps/openid/test/data/test_etxrd/sometimesprefix.xrds deleted file mode 100644 index eff75554..00000000 --- a/askbot/deps/openid/test/data/test_etxrd/sometimesprefix.xrds +++ /dev/null @@ -1,34 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<XRDS ref="xri://@ootao*test1" xmlns="xri://$xrds"> - <XRD xmlns="xri://$xrd*($v*2.0)"> - <Query>*ootao</Query> - <Status code="100"/> - <Expires>2006-08-09T22:07:13.000Z</Expires> - <ProviderID>xri://@</ProviderID> - <LocalID priority="10">!5BAD.2AA.3C72.AF46</LocalID> - <CanonicalID priority="10">xri://@!5BAD.2AA.3C72.AF46</CanonicalID> - <Service priority="10"> - <Type>xri://$res*auth*($v*2.0)</Type> - <ProviderID>xri://@!5BAD.2AA.3C72.AF46</ProviderID> - <MediaType>application/xrds+xml;trust=none</MediaType> - <URI priority="10">http://resolve.ezibroker.net/resolve/@ootao/</URI> - </Service> - <Service priority="10"> - <Type select="true">http://openid.net/signon/1.0</Type> - <ProviderID/> - <URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI> - </Service> - </XRD> - <XRD xmlns="xri://$xrd*($v*2.0)"> - <Query>*test1</Query> - <Status code="100">SUCCESS</Status> - <ProviderID>xri://@!5BAD.2AA.3C72.AF46</ProviderID> - <LocalID>!0000.0000.3B9A.CA01</LocalID> - <CanonicalID>@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01</CanonicalID> - <Service> - <Type select="true">http://openid.net/signon/1.0</Type> - <ProviderID/> - <URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI> - </Service> - </XRD> -</XRDS> diff --git a/askbot/deps/openid/test/data/test_etxrd/spoof1.xrds b/askbot/deps/openid/test/data/test_etxrd/spoof1.xrds deleted file mode 100644 index 8e870c81..00000000 --- a/askbot/deps/openid/test/data/test_etxrd/spoof1.xrds +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<XRDS ref="xri://=keturn*isDrummond" xmlns="xri://$xrds"> - <XRD xmlns="xri://$xrd*($v*2.0)"> - <Query>*keturn</Query> - <ProviderID>xri://=</ProviderID> - <LocalID>!E4</LocalID> - <CanonicalID>=!E4</CanonicalID> - - <Service> - <Type>xri://$res*auth*($v*2.0)</Type> - <URI>http://keturn.example.com/resolve/</URI> - <ProviderID>=!E4</ProviderID> - </Service> - </XRD> - <XRD xmlns="xri://$xrd*($v*2.0)"> - <Query>*isDrummond</Query> - <ProviderID>=!E4</ProviderID> - <LocalID>!D2</LocalID> - <CanonicalID>=!D2</CanonicalID> - <Service> - <Type>http://openid.net/signon/1.0</Type> - <URI>http://keturn.example.com/openid</URI> - </Service> - </XRD> -</XRDS> diff --git a/askbot/deps/openid/test/data/test_etxrd/spoof2.xrds b/askbot/deps/openid/test/data/test_etxrd/spoof2.xrds deleted file mode 100644 index 7547561e..00000000 --- a/askbot/deps/openid/test/data/test_etxrd/spoof2.xrds +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<XRDS ref="xri://=keturn*isDrummond" xmlns="xri://$xrds"> - <XRD xmlns="xri://$xrd*($v*2.0)"> - <Query>*keturn</Query> - <ProviderID>xri://=</ProviderID> - <LocalID>!E4</LocalID> - <CanonicalID>=!E4</CanonicalID> - - <Service> - <Type>xri://$res*auth*($v*2.0)</Type> - <URI>http://keturn.example.com/resolve/</URI> - <ProviderID>xri://=</ProviderID> - </Service> - </XRD> - <XRD xmlns="xri://$xrd*($v*2.0)"> - <Query>*isDrummond</Query> - <ProviderID>xri://=</ProviderID> - <LocalID>!D2</LocalID> - <CanonicalID>=!D2</CanonicalID> - <Service> - <Type>http://openid.net/signon/1.0</Type> - <URI>http://keturn.example.com/openid</URI> - </Service> - </XRD> -</XRDS> diff --git a/askbot/deps/openid/test/data/test_etxrd/spoof3.xrds b/askbot/deps/openid/test/data/test_etxrd/spoof3.xrds deleted file mode 100644 index f4c43c9b..00000000 --- a/askbot/deps/openid/test/data/test_etxrd/spoof3.xrds +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<XRDS ref="xri://=keturn*isDrummond" xmlns="xri://$xrds"> - <XRD xmlns="xri://$xrd*($v*2.0)"> - <Query>*keturn</Query> - <ProviderID>xri://@</ProviderID> - <LocalID>@E4</LocalID> - <CanonicalID>@!E4</CanonicalID> - - <Service> - <Type>xri://$res*auth*($v*2.0)</Type> - <URI>http://keturn.example.com/resolve/</URI> - <ProviderID>@!E4</ProviderID> - </Service> - </XRD> - <XRD xmlns="xri://$xrd*($v*2.0)"> - <Query>*is</Query> - <ProviderID>@!E4</ProviderID> - <LocalID>!D2</LocalID> - <CanonicalID>=!C0</CanonicalID> - <CanonicalID>=!E4!01</CanonicalID> - <Service> - <Type>xri://$res*auth*($v*2.0)</Type> - <URI>http://keturn.example.com/resolve/</URI> - <ProviderID>@!C0</ProviderID> - </Service> - </XRD> - <XRD xmlns="xri://$xrd*($v*2.0)"> - <Query>*drummond</Query> - <ProviderID>@!C0</ProviderID> - <LocalID>!D2</LocalID> - <CanonicalID>@!C0!D2</CanonicalID> - <Service> - <Type>http://openid.net/signon/1.0</Type> - <URI>http://keturn.example.com/openid</URI> - </Service> - </XRD> -</XRDS> diff --git a/askbot/deps/openid/test/data/test_etxrd/status222.xrds b/askbot/deps/openid/test/data/test_etxrd/status222.xrds deleted file mode 100644 index 84cd5c90..00000000 --- a/askbot/deps/openid/test/data/test_etxrd/status222.xrds +++ /dev/null @@ -1,9 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<XRDS ref="xri://=x" xmlns="xri://$xrds"> - <XRD xmlns="xri://$xrd*($v*2.0)"> - <Query>*x</Query> - <Status code="222">The subsegment does not exist</Status> - <Expires>2006-08-18T00:02:35.000Z</Expires> - <ProviderID>xri://=</ProviderID> - </XRD> -</XRDS>
\ No newline at end of file diff --git a/askbot/deps/openid/test/data/test_etxrd/subsegments.xrds b/askbot/deps/openid/test/data/test_etxrd/subsegments.xrds deleted file mode 100644 index 11d2e912..00000000 --- a/askbot/deps/openid/test/data/test_etxrd/subsegments.xrds +++ /dev/null @@ -1,58 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<XRDS ref="xri://=nishitani*masaki" xmlns="xri://$xrds"> - <XRD xmlns="xri://$xrd*($v*2.0)"> - <Query>*nishitani</Query> - <Status code="100"/> - <Expires>2007-12-25T11:33:39.000Z</Expires> - <ProviderID>xri://=</ProviderID> - <LocalID priority="10">!E117.EF2F.454B.C707</LocalID> - <CanonicalID priority="10">=!E117.EF2F.454B.C707</CanonicalID> - <Service priority="10"> - <Type select="true">http://openid.net/signon/1.0</Type> - <ProviderID>xri://!!1003!103</ProviderID> - <URI append="none" priority="1">https://linksafe.ezibroker.net/server/</URI> - </Service> - <Service priority="10"> - <Type select="true">xri://$res*auth*($v*2.0)</Type> - <ProviderID>xri://!!1003!103</ProviderID> - <MediaType>application/xrds+xml;trust=none</MediaType> - <URI priority="10">http://resolve.ezibroker.net/resolve/=nishitani/</URI> - </Service> - <Service priority="1"> - <Type match="content" select="true">xri://+i-service*(+forwarding)*($v*1.0)</Type> - <Type match="null" select="false"/> - <ProviderID>xri://!!1003!103</ProviderID> - <Path match="content">(+index)</Path> - <Path match="default"/> - <URI append="qxri" priority="1">http://linksafe-forward.ezibroker.net/forwarding/</URI> - </Service> - </XRD> - <XRD xmlns="xri://$xrd*($v*2.0)"> - <Query>*masaki</Query> - <Status code="100">SUCCESS</Status> - <ProviderID>xri://!!1003</ProviderID> - <LocalID>!0000.0000.3B9A.CA01</LocalID> - <CanonicalID>=!E117.EF2F.454B.C707!0000.0000.3B9A.CA01</CanonicalID> - <Service> - <Type select="true">http://openid.net/signon/1.0</Type> - <ProviderID>xri://!!1003!103</ProviderID> - <URI append="none" priority="1">https://linksafe.ezibroker.net/server/</URI> - </Service> - <Service> - <Type select="true">xri://+i-service*(+contact)*($v*1.0)</Type> - <Type match="null"/> - <ProviderID>xri://!!1003!103</ProviderID> - <Path select="true">(+contact)</Path> - <Path match="null"/> - <URI append="authority" priority="1">http://linksafe-contact.ezibroker.net/contact/</URI> - </Service> - <Service priority="1"> - <Type match="content" select="true">xri://+i-service*(+forwarding)*($v*1.0)</Type> - <Type match="null" select="false"/> - <ProviderID>xri://!!1003!103</ProviderID> - <Path match="content">(+index)</Path> - <Path match="default"/> - <URI append="qxri" priority="1">http://linksafe-forward.ezibroker.net/forwarding/</URI> - </Service> - </XRD> -</XRDS> diff --git a/askbot/deps/openid/test/data/test_etxrd/valid-populated-xrds.xml b/askbot/deps/openid/test/data/test_etxrd/valid-populated-xrds.xml deleted file mode 100644 index 60e5ca7b..00000000 --- a/askbot/deps/openid/test/data/test_etxrd/valid-populated-xrds.xml +++ /dev/null @@ -1,39 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<xrds:XRDS - xmlns:xrds="xri://$xrds" - xmlns:openid="http://openid.net/xmlns/1.0" - xmlns:typekey="http://typekey.com/xmlns/1.0" - xmlns="xri://$xrd*($v*2.0)"> - <XRD> - - <Service priority="0"> - <Type>http://openid.net/signon/1.0</Type> - <URI>http://www.myopenid.com/server</URI> - <openid:Delegate>http://josh.myopenid.com/</openid:Delegate> - </Service> - - <Service priority="20"> - <Type>http://lid.netmesh.org/sso/2.0b5</Type> - <Type>http://lid.netmesh.org/2.0b5</Type> - <URI>http://mylid.net/josh</URI> - </Service> - - <Service priority="10"> - <Type>http://openid.net/signon/1.0</Type> - <URI>http://www.livejournal.com/openid/server.bml</URI> - <openid:Delegate>http://www.livejournal.com/users/nedthealpaca/</openid:Delegate> - </Service> - - <Service priority="15"> - <Type>http://typekey.com/services/1.0</Type> - <typekey:MemberName>joshhoyt</typekey:MemberName> - </Service> - - <Service priority="5"> - <Type>http://openid.net/signon/1.0</Type> - <URI>http://www.schtuff.com/openid</URI> - <openid:Delegate>http://users.schtuff.com/josh</openid:Delegate> - </Service> - - </XRD> -</xrds:XRDS> diff --git a/askbot/deps/openid/test/data/trustroot.txt b/askbot/deps/openid/test/data/trustroot.txt deleted file mode 100644 index 3d948a4f..00000000 --- a/askbot/deps/openid/test/data/trustroot.txt +++ /dev/null @@ -1,150 +0,0 @@ -======================================== -Trust root parsing checking -======================================== - ----------------------------------------- -21: Does not parse ----------------------------------------- -baz.org -*.foo.com -http://*.schtuff.*/ -ftp://foo.com -ftp://*.foo.com -http://*.foo.com:80:90/ -http:/// -http:// -foo.*.com -http://foo.*.com -http://www.* -http://*foo.com/ -http://foo.com\/ -http://localhost:1900foo/ -http://foo.com/invalid#fragment -http://Ï€.pi.com/ -http://lambda.com/Λ - - - -5 - ----------------------------------------- -15: Insane ----------------------------------------- -http://*/ -https://*/ -http://*.com -http://*.com/ -https://*.com/ -http://*.com.au/ -http://*.co.uk/ -http://*.foo.notatld/ -https://*.foo.notatld/ -http://*.museum/ -https://*.museum/ -http://www.schtuffcom/ -http://it/ -http://..it/ -http://.it/ - ----------------------------------------- -18: Sane ----------------------------------------- -http://*.schtuff.com./ -http://*.schtuff.com/ -http://*.foo.schtuff.com/ -http://*.schtuff.com -http://www.schtuff.com/ -http://www.schtuff.com./ -http://www.schutff.com -http://*.this.that.schtuff.com/ -http://*.foo.com/path -http://*.foo.com/path?action=foo2 -http://x.foo.com/path?action=foo2 -http://x.foo.com/path?action=%3D -http://localhost:8081/ -http://localhost:8082/?action=openid -https://foo.com/ -http://kink.fm/should/be/sane -http://beta.lingu.no/ -http://goathack.livejournal.org:8020/openid/login.bml - -======================================== -return_to matching -======================================== - ----------------------------------------- -45: matches ----------------------------------------- -http://*/ http://cnn.com/ -http://*/ http://livejournal.com/ -http://*/ http://met.museum/ -http://*:8081/ http://met.museum:8081/ -http://localhost:8081/x?action=openid http://localhost:8081/x?action=openid -http://*.foo.com http://b.foo.com -http://*.foo.com http://b.foo.com/ -http://*.foo.com/ http://b.foo.com -http://b.foo.com http://b.foo.com -http://b.foo.com http://b.foo.com/ -http://b.foo.com/ http://b.foo.com -http://*.b.foo.com http://b.foo.com -http://*.b.foo.com http://b.foo.com/ -http://*.b.foo.com/ http://b.foo.com -http://*.b.foo.com http://x.b.foo.com -http://*.b.foo.com http://w.x.b.foo.com -http://*.bar.co.uk http://www.bar.co.uk -http://*.uoregon.edu http://x.cs.uoregon.edu -http://x.com/abc http://x.com/abc -http://127.1/abc http://127.1/abc -http://10.0.0.1/abc http://10.0.0.1/abc -http://x.com/abc http://x.com/abc/def -http://*.x.com http://x.com/gallery -http://*.x.com http://foo.x.com/gallery -http://foo.x.com http://foo.x.com/gallery/xxx -http://*.x.com/gallery http://foo.x.com/gallery -http://localhost:8082/?action=openid http://localhost:8082/?action=openid -http://goathack.livejournal.org:8020/ http://goathack.livejournal.org:8020/openid/login.bml -https://foo.com https://foo.com -http://Foo.com http://foo.com -http://foo.com http://Foo.com -http://foo.com:80/ http://foo.com/ -http://foo.com/?x=y http://foo.com/?x=y&a=b -http://foo.com/x http://foo.com/x?y -http://mylid.net/j3h. http://mylid.net/j3h.?x=y -http://j3h.us http://j3h.us?ride=unicycle -https://www.filmclans.com:443/mattmartin/FilmClans https://www.filmclans.com/mattmartin/FilmClans/Logon.aspx?nonce=BVjqSOee -http://foo.com:80 http://foo.com -http://foo.com http://foo.com:80 -http://foo.com http://foo.com/ -http://foo.com/ http://foo.com -http://foo.com/ http://foo.com:80 -http://foo.com:80/ http://foo.com:80/stuff -http://foo.com:80/ http://foo.com/stuff -http://foo.com/ HTTP://foo.com/ - ----------------------------------------- -24: does not match ----------------------------------------- -http://*/ ftp://foo.com/ -http://*/ xxx -http://*.x.com/abc http://foo.x.com -http://*.x.com/abc http://*.x.com -http://*.com/ http://*.com/ -http://x.com/abc http://x.com/ -http://x.com/abc http://x.com/a -http://x.com/abc http://x.com/ab -http://x.com/abc http://x.com/abcd -http://*.cs.uoregon.edu http://x.uoregon.edu -http://*.foo.com http://bar.com -http://*.foo.com http://www.bar.com -http://*.bar.co.uk http://xxx.co.uk -https://foo.com http://foo.com -http://foo.com https://foo.com -http://foo.com:81 http://foo.com:80 -http://*:80 http://foo.com:81 -http://foo.com/?a=b http://foo.com/?x=y -http://foo.com/?a=b http://foo.com/?x=y&a=b -http://foo.com/?a=b http://foo.com/ -http://*.oo.com/ http://foo.com/ -http://foo.com/* http://foo.com/anything -http://foo.com http://foo.com:443 -https://foo.com https://foo.com:80 diff --git a/askbot/deps/openid/test/datadriven.py b/askbot/deps/openid/test/datadriven.py deleted file mode 100644 index 2dbcfd0d..00000000 --- a/askbot/deps/openid/test/datadriven.py +++ /dev/null @@ -1,47 +0,0 @@ -import unittest -import types - -class DataDrivenTestCase(unittest.TestCase): - cases = [] - - def generateCases(cls): - return cls.cases - - generateCases = classmethod(generateCases) - - def loadTests(cls): - tests = [] - for case in cls.generateCases(): - if isinstance(case, tuple): - test = cls(*case) - elif isinstance(case, dict): - test = cls(**case) - else: - test = cls(case) - tests.append(test) - return tests - - loadTests = classmethod(loadTests) - - def __init__(self, description): - unittest.TestCase.__init__(self, 'runOneTest') - self.description = description - - def shortDescription(self): - return '%s for %s' % (self.__class__.__name__, self.description) - -def loadTests(module_name): - loader = unittest.defaultTestLoader - this_module = __import__(module_name, {}, {}, [None]) - - tests = [] - for name in dir(this_module): - obj = getattr(this_module, name) - if (isinstance(obj, (type, types.ClassType)) and - issubclass(obj, unittest.TestCase)): - if hasattr(obj, 'loadTests'): - tests.extend(obj.loadTests()) - else: - tests.append(loader.loadTestsFromTestCase(obj)) - - return unittest.TestSuite(tests) diff --git a/askbot/deps/openid/test/dh.py b/askbot/deps/openid/test/dh.py deleted file mode 100644 index c4478d15..00000000 --- a/askbot/deps/openid/test/dh.py +++ /dev/null @@ -1,70 +0,0 @@ -import os.path -from askbot.deps.openid.dh import DiffieHellman, strxor - -def test_strxor(): - NUL = '\x00' - - cases = [ - (NUL, NUL, NUL), - ('\x01', NUL, '\x01'), - ('a', 'a', NUL), - ('a', NUL, 'a'), - ('abc', NUL * 3, 'abc'), - ('x' * 10, NUL * 10, 'x' * 10), - ('\x01', '\x02', '\x03'), - ('\xf0', '\x0f', '\xff'), - ('\xff', '\x0f', '\xf0'), - ] - - for aa, bb, expected in cases: - actual = strxor(aa, bb) - assert actual == expected, (aa, bb, expected, actual) - - exc_cases = [ - ('', 'a'), - ('foo', 'ba'), - (NUL * 3, NUL * 4), - (''.join(map(chr, xrange(256))), - ''.join(map(chr, xrange(128)))), - ] - - for aa, bb in exc_cases: - try: - unexpected = strxor(aa, bb) - except ValueError: - pass - else: - assert False, 'Expected ValueError, got %r' % (unexpected,) - -def test1(): - dh1 = DiffieHellman.fromDefaults() - dh2 = DiffieHellman.fromDefaults() - secret1 = dh1.getSharedSecret(dh2.public) - secret2 = dh2.getSharedSecret(dh1.public) - assert secret1 == secret2 - return secret1 - -def test_exchange(): - s1 = test1() - s2 = test1() - assert s1 != s2 - -def test_public(): - f = file(os.path.join(os.path.dirname(__file__), 'dhpriv')) - dh = DiffieHellman.fromDefaults() - try: - for line in f: - parts = line.strip().split(' ') - dh._setPrivate(long(parts[0])) - - assert dh.public == long(parts[1]) - finally: - f.close() - -def test(): - test_exchange() - test_public() - test_strxor() - -if __name__ == '__main__': - test() diff --git a/askbot/deps/openid/test/dhpriv b/askbot/deps/openid/test/dhpriv deleted file mode 100644 index 0fa52314..00000000 --- a/askbot/deps/openid/test/dhpriv +++ /dev/null @@ -1,29 +0,0 @@ -130706940119084053627151828062879423433929180135817317038378606310097533503449582079984816816837125851552273641820339909167103200910805078308128174143174269944095368580519322913514764528012639683546377014716235962867583443566164615728897857285824741767070432119909660645255499710701356135207437699643611094585 139808169914464096465921128085565621767096724855516655439365028496569658038844954238931647642811548254956660405394116677296461848124300258439895306367561416289126854788101396379292925819850897858045772500578222021901631436550118958972312221974009238050517034542286574826081826542722270952769078386418682059418 -91966407878983240112417790733941098492087186469785726449910011271065622315680646030230288265496017310433513856308693810812043160919214636748486185212617634222158204354206411031403206076739932806412551605172319515223573351072757800448643935018534945933808900467686115619932664888581913179496050117713298715475 88086484332488517006277516020842172054013692832175783214603951240851750819999098631851571207693874357651112736088114133607400684776234181681933311972926752846692615822043533641407510569745606256772455614745111122033229877596984718963046218854103292937700694160593653595134512369959987897086639788909618660591 -94633950701209990078055218830969910271587805983595045023718108184189787131629772007048606080263109446462048743696369276578815611098215686598630889831104860221067872883514840819381234786050098278403321905311637820524177879167250981289318356078312300538871435101338967079907049912435983871847334104247675360099 136836393035803488129856151345450008294260680733328546556640578838845312279198933806383329293483852515700876505956362639881210101974254765087350842271260064592406308509078284840473735904755203614987286456952991025347168970462354352741159076541157478949094536405618626397435745496863324654768971213730622037771 -24685127248019769965088146297942173464487677364928435784091685260262292485380918213538979925891771204729738138857126454465630594391449913947358655368215901119137728648638547728497517587701248406019427282237279437409508871300675355166059811431191200555457304463617727969228965042729205402243355816702436970430 103488011917988946858248200111251786178288940265978921633592888293430082248387786443813155999158786903216094876295371112716734481877806417714913656921169196196571699893360825510307056269738593971532017994987406325068886420548597161498019372380511676314312298122272401348856314619382867707981701472607230523868 -116791045850880292989786005885944774698035781824784400772676299590038746153860847252706167458966356897309533614849402276819438194497464696186624618374179812548893947178936305721131565012344462048549467883494038577857638815386798694225798517783768606048713198211730870155881426709644960689953998714045816205549 25767875422998856261320430397505398614439586659207416236135894343577952114994718158163212134503751463610021489053571733974769536157057815413209619147486931502025658987681202196476489081257777148377685478756033509708349637895740799542063593586769082830323796978935454479273531157121440998804334199442003857410 -75582226959658406842894734694860761896800153014775231713388264961517169436476322183886891849966756849783437334069692683523296295601533803799559985845105706728538458624387103621364117548643541824878550074680443708148686601108223917493525070861593238005735446708555769966855130921562955491250908613793521520082 51100990616369611694975829054222013346248289055987940844427061856603230021472379888102172458517294080775792439385531234808129302064303666640376750139242970123503857186428797403843206765926798353022284672682073397573130625177187185114726049347844460311761033584101482859992951420083621362870301150543916815123 -22852401165908224137274273646590366934616265607879280260563022941455466297431255072303172649495519837876946233272420969249841381161312477263365567831938496555136366981954001163034914812189448922853839616662859772087929140818377228980710884492996109434435597500854043325062122184466315338260530734979159890875 35017410720028595029711778101507729481023945551700945988329114663345341120595162378885287946069695772429641825579528116641336456773227542256911497084242947904528367986325800537695079726856460817606404224094336361853766354225558025931211551975334149258299477750615397616908655079967952372222383056221992235704 -37364490883518159794654045194678325635036705086417851509136183713863262621334636905291385255662750747808690129471989906644041585863034419130023070856805511017402434123099100618568335168939301014148587149578150068910141065808373976114927339040964292334109797421173369274978107389084873550233108940239410902552 40916262212189137562350357241447034318002130016858244002788189310078477605649010031339865625243230798681216437501833540185827501244378529230150467789369234869122179247196276164931090039290879808162629109742198951942358028123056268054775108592325500609335947248599688175189333996086475013450537086042387719925 -42030470670714872936404499074069849778147578537708230270030877866700844337372497704027708080369726758812896818567830863540507961487472657570488625639077418109017434494794778542739932765561706796300920251933107517954265066804108669800167526425723377411855061131982689717887180411017924173629124764378241885274 124652439272864857598747946875599560379786580730218192165733924418687522301721706620565030507816884907589477351553268146177293719586287258662025940181301472851649975563004543250656807255226609296537922304346339513054316391667044301386950180277940536542183725690479451746977789001659540839582630251935163344393 -33176766914206542084736303652243484580303865879984981189372762326078776390896986743451688462101732968104375838228070296418541745483112261133079756514082093269959937647525005374035326747696591842313517634077723301677759648869372517403529488493581781546743147639937580084065663597330159470577639629864369972900 67485835091897238609131069363014775606263390149204621594445803179810038685760826651889895397414961195533694176706808504447269558421955735607423135937153901140512527504198912146656610630396284977496295289999655140295415981288181545277299615922576281262872097567020980675200178329219970170480653040350512964539 -131497983897702298481056962402569646971797912524360547236788650961059980711719600424210346263081838703940277066368168874781981151411096949736205282734026497995296147418292226818536168555712128736975034272678008697869326747592750850184857659420541708058277866000692785617873742438060271311159568468507825422571 5400380840349873337222394910303409203226429752629134721503171858543984393161548520471799318518954232197106728096866840965784563043721652790856860155702760027304915133166173298206604451826182024471262142046935060360564569939062438160049193241369468208458085699995573492688298015026628427440418009025072261296 -83265103005695640943261961853521077357830295830250157593141844209296716788437615940096402365505416686459260302419338241462783388722843946886845478224048360927114533590583464979009731440049610985062455108831881153988321298531365779084012803908832525921630534096740755274371500276660832724874701671184539131864 141285570207910287798371174771658911045525474449663877845558585668334618068814605961306961485855329182957174312715910923324965889174835444049526313968571611940626279733302104955951067959291852710640374412577070764165811275030632465290729619533330733368808295932659463215921521905553936914975786500018720073003 -68435028583616495789148116911096163791710022987677894923742899873596891423986951658100606742052014161171185231735413902875605720814417622409817842932759492013585936536452615480700628719795872201528559780249210820284350401473564919576289210869896327937002173624497942136329576506818749730506884927872345019446 134655528287263100540003157571441260698452262106680191153945271167894435782028803135774578949200580551016388918860856991026082917835209212892423567114480975540305860034439015788120390011692862968771136814777768281366591257663821495720134621172848947971117885754539770645621669309650476331439675400544167728223 -97765390064836080322590528352647421920257073063706996347334558390461274981996865736612531330863478931481491964338380362350271734683183807511097331539820133036984271653285063355715726806139083282458695728902452215405696318402583540317419929113959816258829534543044153959951908676300847164682178008704099351835 92552521881196975294401505656851872247567784546370503402756239533783651371688190302773864319828182042605239246779598629409815474038541272600580320815319709309111399294952620375093803971373108792300726524826209329889463854451846561437729676142864421966497641824498079067929811613947148353921163336822026640804 -145767094672933012300753301037546647564595762930138884463767054235112032706630891961371504668013023047595721138624016493638510710257541241706724342585654715468628355455898091951826598092812212209834746162089753649871544789379424903025374228231365026585872808685759231756517703720396301355299998059523896918448 116669462839999965355861187716880953863237226719689755457884414384663576662696981997535568446560375442532084973721539944428004043491468494548231348032618218312515409944970197902589794303562379864012797605284844016184274353252071642511293089390472576498394410829972525726474727579603392265177009323768966538608 -34172517877854802711907683049441723730724885305592620486269966708379625109832852005775048584124451699198484092407720344962116726808090368739361658889584507734617844212547181476646725256303630128954338675520938806905779837227983648887192531356390902975904503218654196581612781227843742951241442641220856414232 126013077261793777773236390821108423367648447987653714614732477073177878509574051196587476846560696305938891953527959347566502332765820074506907037627115954790645652211088723122982633069089920979477728376746424256704724173255656757918995039125823421607024407307091796807227896314403153380323770001854211384322 -9979624731056222925878866378063961280844793874828281622845276060532093809300121084179730782833657205171434732875093693074415298975346410131191865198158876447591891117577190438695367929923494177555818480377241891190442070100052523008290671797937772993634966511431668500154258765510857129203107386972819651767 76559085024395996164590986654274454741199399364851956129137304209855150918182685643729981600389513229011956888957763987167398150792454613751473654448162776379362213885827651020309844507723069713820393068520302223477225569348080362344052033711960892643036147232270133731530049660264526964146237693063093765111 -18162696663677410793062235946366423954875282212790518677684260521370996677183041664345920941714064628111537529793170736292618705900247450994864220481135611781148410617609559050220262121494712903009168783279356915189941268264177631458029177102542745167475619936272581126346266816618866806564180995726437177435 63244550218824945129624987597134280916829928261688093445040235408899092619821698537312158783367974202557699994650667088974727356690181336666077506063310290098995215324552449858513870629176838494348632073938023916155113126203791709810160925798130199717340478393420816876665127594623142175853115698049952126277 -4817943161362708117912118300716778687157593557807116683477307391846133734701449509121209661982298574607233039490570567781316652698287671086985501523197566560479906850423709894582834963398034434055472063156147829131181965140631257939036683622084290629927807369457311894970308590034407761706800045378158588657 61612160237840981966750225147965256022861527286827877531373888434780789812764688703260066154973576040405676432586962624922734102370509771313805122788566405984830112657060375568510809122230960988304085950306616401218206390412815884549481965750553137717475620505076144744211331973240555181377832337912951699135 -36363324947629373144612372870171042343590861026293829791335153646774927623889458346817049419803031378037141773848560341251355283891019532059644644509836766167835557471311319194033709837770615526356168418160386395260066262292757953919140150454538786106958252854181965875293629955562111756775391296856504912587 86831561031659073326747216166881733513938228972332631084118628692228329095617884068498116676787029033973607066377816508795286358748076949738854520048303930186595481606562375516134920902325649683618195251332651685732712539073110524182134321873838204219194459231650917098791250048469346563303077080880339797744 -26406869969418301728540993821409753036653370247174689204659006239823766914991146853283367848649039747728229875444327879875275718711878211919734397349994000106499628652960403076186651083084423734034070082770589453774926850920776427074440483233447839259180467805375782600203654373428926653730090468535611335253 100139935381469543084506312717977196291289016554846164338908226931204624582010530255955411615528804421371905642197394534614355186795223905217732992497673429554618838376065777445760355552020655667172127543653684405493978325270279321013143828897100500212200358450649158287605846102419527584313353072518101626851 -92613116984760565837109105383781193800503303131143575169488835702472221039082994091847595094556327985517286288659598094631489552181233202387028607421487026032402972597880028640156629614572656967808446397456622178472130864873587747608262139844319805074476178618930354824943672367046477408898479503054125369731 30023391082615178562263328892343821010986429338255434046051061316154579824472412477397496718186615690433045030046315908170615910505869972621853946234911296439134838951047107272129711854649412919542407760508235711897489847951451200722151978578883748353566191421685659370090024401368356823252748749449302536931 -31485815361342085113278193504381994806529237123359718043079410511224607873725611862217941085749929342777366642477711445011074784469367917758629403998067347054115844421430072631339788256386509261291675080191633908849638316409182455648806133048549359800886124554879661473112614246869101243501787363247762961784 114503770698890543429251666713050844656853278831559195214556474458830029271801818536133531843456707474500106283648085144619097572354066554819887152106174400667929098257361286338795493838820850475790977445807435511982704395422526800272723708548541616513134676140304653112325071112865020365664833601046215694089 -76882090884790547431641385530818076533805072109483843307806375918023300052767710853172670987385376253156912268523505310624133905633437815297307463917718596711590885553760690350221265675690787249135345226947453988081566088302642706234126002514517416493192624887800567412565527886687096028028124049522890448168 15056463217273240496622619354104573042767532856243223052125822509781815362480522535564283485059790932505429110157271454207173426525345813426696743168079246510944969446574354255284952839036431873039487144279164893710061580467579842173706653409487110282515691099753380094215805485573768509475850463001549608836 -52345178981230648108672997265819959243255047568833938156267924185186047373470984278294897653277996726416846430969793375429223610099546622112048283560483136389901514170116723365811871938630317974150540909650396429631704968748113009366339718498979597226137532343384889080245796447593572468846438769413505393967 32148494517199936472358017244372701214529606506776255341152991328091526865643069587953759877295255050519124541457805199596762210567333445908166076384465183589342153762720515477404466193879418014196727238972417616122646440870364200208488239778452378059236162633837824948613596114768455832408342040970780086 -41095268619128788015767564971105114602454449306041732792746397800275041704886345704294273937217484580365505320134717320083763349380629342859670693445658118959823430378844830923452105707338162448974869312012791385772125813291388247857971218575518319578818336960572244046567099555399203328678654466958536663208 92166550199033418923713824997841892577149715275633481076285269142670107687867024550593869464613175882141630640739938334001211714884975032600306279287443909448541179109981755796752132502127330056736913454039526413284519137059580845856736918773597087836203497066909257930043736166431682872083389105176299181629 -40049143661018504441607875135884755310012910557581028447435354354754245291878800571089144452035026644953322330676651798951447670184106450649737772686119714700743396359069052813433030118630105307022867200053964644574786137276428546712005171080129190959914708907200288299169344380390093918556722227705114244981 108159089972386282154772900619022507336076619354549601813179459338897131937353741544606392560724999980281424266891537298473163753022749859939445293926707568015958367188089915420630082556748668489756475027008449860889202622698060097015044886961901650857610841562477736791450080980702347705778074391774667412741 -69905259478181995876884927656894491893594530150260951315109404530530357998889589977208787140430938039028941393673520799460431992051993157468616168400324834880926190141581037597526917869362292931957289043707855837933490285814769110495657056206391880865972389421774822461752702336812585852278453803972600333734 71821415380277072313878763768684432371552628204186742842154591000123020597011744840460964835414360968627162765288463383113375595799297552681618876474019263288277398833725479226930770694271622605114061622753165584075733358178384410640349907375170170910499615355511313349300918885560131539570707695789106185664 -26945345439378873515011714350080059082081595419023056538696949766471272811362104837806324694947413603019863785876836706911406330379274553386254346050697348395574746891556054334903838949157798006141473389066020212044825140294048709654273698482867946522782450500680195477050110145664069582549935651920545151500 80313315938584480048642653013876614091607852535582224914294013785054094052454758327935781971746329853786568549510067442145637007308960551652864942042189241081946607011847245280773379099020221884296226818685556430275385068764313042226925852500883894269809033380734632866477789520106865758504064806906234130588 diff --git a/askbot/deps/openid/test/discoverdata.py b/askbot/deps/openid/test/discoverdata.py deleted file mode 100644 index f618290f..00000000 --- a/askbot/deps/openid/test/discoverdata.py +++ /dev/null @@ -1,125 +0,0 @@ -"""Module to make discovery data test cases available""" -import urlparse -import os.path - -from askbot.deps.openid.yadis.discover import DiscoveryResult, DiscoveryFailure -from askbot.deps.openid.yadis.constants import YADIS_HEADER_NAME - -tests_dir = os.path.dirname(__file__) -data_path = os.path.join(tests_dir, 'data') - -testlist = [ -# success, input_name, id_name, result_name - (True, "equiv", "equiv", "xrds"), - (True, "header", "header", "xrds"), - (True, "lowercase_header", "lowercase_header", "xrds"), - (True, "xrds", "xrds", "xrds"), - (True, "xrds_ctparam", "xrds_ctparam", "xrds_ctparam"), - (True, "xrds_ctcase", "xrds_ctcase", "xrds_ctcase"), - (False, "xrds_html", "xrds_html", "xrds_html"), - (True, "redir_equiv", "equiv", "xrds"), - (True, "redir_header", "header", "xrds"), - (True, "redir_xrds", "xrds", "xrds"), - (False, "redir_xrds_html", "xrds_html", "xrds_html"), - (True, "redir_redir_equiv", "equiv", "xrds"), - (False, "404_server_response", None, None), - (False, "404_with_header", None, None), - (False, "404_with_meta", None, None), - (False, "201_server_response", None, None), - (False, "500_server_response", None, None), - ] - -def getDataName(*components): - sanitized = [] - for part in components: - if part in ['.', '..']: - raise ValueError - elif part: - sanitized.append(part) - - if not sanitized: - raise ValueError - - return os.path.join(data_path, *sanitized) - -def getExampleXRDS(): - filename = getDataName('example-xrds.xml') - return file(filename).read() - -example_xrds = getExampleXRDS() -default_test_file = getDataName('test1-discover.txt') - -discover_tests = {} - -def readTests(filename): - data = file(filename).read() - tests = {} - for case in data.split('\f\n'): - (name, content) = case.split('\n', 1) - tests[name] = content - return tests - -def getData(filename, name): - global discover_tests - try: - file_tests = discover_tests[filename] - except KeyError: - file_tests = discover_tests[filename] = readTests(filename) - return file_tests[name] - -def fillTemplate(test_name, template, base_url, example_xrds): - mapping = [ - ('URL_BASE/', base_url), - ('<XRDS Content>', example_xrds), - ('YADIS_HEADER', YADIS_HEADER_NAME), - ('NAME', test_name), - ] - - for k, v in mapping: - template = template.replace(k, v) - - return template - -def generateSample(test_name, base_url, - example_xrds=example_xrds, - filename=default_test_file): - try: - template = getData(filename, test_name) - except IOError, why: - import errno - if why[0] == errno.ENOENT: - raise KeyError(filename) - else: - raise - - return fillTemplate(test_name, template, base_url, example_xrds) - -def generateResult(base_url, input_name, id_name, result_name, success): - input_url = urlparse.urljoin(base_url, input_name) - - # If the name is None then we expect the protocol to fail, which - # we represent by None - if id_name is None: - assert result_name is None - return input_url, DiscoveryFailure - - result = generateSample(result_name, base_url) - headers, content = result.split('\n\n', 1) - header_lines = headers.split('\n') - for header_line in header_lines: - if header_line.startswith('Content-Type:'): - _, ctype = header_line.split(':', 1) - ctype = ctype.strip() - break - else: - ctype = None - - id_url = urlparse.urljoin(base_url, id_name) - - result = DiscoveryResult(input_url) - result.normalized_uri = id_url - if success: - result.xrds_uri = urlparse.urljoin(base_url, result_name) - result.content_type = ctype - result.response_text = content - return input_url, result diff --git a/askbot/deps/openid/test/kvform.py b/askbot/deps/openid/test/kvform.py deleted file mode 100644 index e63d4178..00000000 --- a/askbot/deps/openid/test/kvform.py +++ /dev/null @@ -1,174 +0,0 @@ -from askbot.deps.openid import kvform -from askbot.deps.openid import oidutil -import unittest - -class KVBaseTest(unittest.TestCase): - def shortDescription(self): - return '%s test for %r' % (self.__class__.__name__, self.kvform) - - def log(self, message, unused_priority=None): - self.warnings.append(message) - - def checkWarnings(self, num_warnings): - self.failUnlessEqual(num_warnings, len(self.warnings), repr(self.warnings)) - - def setUp(self): - self.warnings = [] - self.old_log = oidutil.log - self.log_func = oidutil.log = self.log - self.failUnless(self.log_func is oidutil.log, - (oidutil.log, self.log_func)) - - def tearDown(self): - oidutil.log = self.old_log - -class KVDictTest(KVBaseTest): - def __init__(self, kv, dct, warnings): - unittest.TestCase.__init__(self) - self.kvform = kv - self.dict = dct - self.expected_warnings = warnings - - def runTest(self): - # Convert KVForm to dict - d = kvform.kvToDict(self.kvform) - - # make sure it parses to expected dict - self.failUnlessEqual(self.dict, d) - - # Check to make sure we got the expected number of warnings - self.checkWarnings(self.expected_warnings) - - # Convert back to KVForm and round-trip back to dict to make - # sure that *** dict -> kv -> dict is identity. *** - kv = kvform.dictToKV(d) - d2 = kvform.kvToDict(kv) - self.failUnlessEqual(d, d2) - -class KVSeqTest(KVBaseTest): - def __init__(self, seq, kv, expected_warnings): - unittest.TestCase.__init__(self) - self.kvform = kv - self.seq = seq - self.expected_warnings = expected_warnings - - def cleanSeq(self, seq): - """Create a new sequence by stripping whitespace from start - and end of each value of each pair""" - clean = [] - for k, v in self.seq: - if type(k) is str: - k = k.decode('utf8') - if type(v) is str: - v = v.decode('utf8') - clean.append((k.strip(), v.strip())) - return clean - - def runTest(self): - # seq serializes to expected kvform - actual = kvform.seqToKV(self.seq) - self.failUnlessEqual(self.kvform, actual) - self.failUnless(type(actual) is str) - - # Parse back to sequence. Expected to be unchanged, except - # stripping whitespace from start and end of values - # (i. e. ordering, case, and internal whitespace is preserved) - seq = kvform.kvToSeq(actual) - clean_seq = self.cleanSeq(seq) - - self.failUnlessEqual(seq, clean_seq) - self.checkWarnings(self.expected_warnings) - -kvdict_cases = [ - # (kvform, parsed dictionary, expected warnings) - ('', {}, 0), - ('college:harvey mudd\n', {'college':'harvey mudd'}, 0), - ('city:claremont\nstate:CA\n', - {'city':'claremont', 'state':'CA'}, 0), - ('is_valid:true\ninvalidate_handle:{HMAC-SHA1:2398410938412093}\n', - {'is_valid':'true', - 'invalidate_handle':'{HMAC-SHA1:2398410938412093}'}, 0), - - # Warnings from lines with no colon: - ('x\n', {}, 1), - ('x\nx\n', {}, 2), - ('East is least\n', {}, 1), - - # But not from blank lines (because LJ generates them) - ('x\n\n', {}, 1), - - # Warning from empty key - (':\n', {'':''}, 1), - (':missing key\n', {'':'missing key'}, 1), - - # Warnings from leading or trailing whitespace in key or value - (' street:foothill blvd\n', {'street':'foothill blvd'}, 1), - ('major: computer science\n', {'major':'computer science'}, 1), - (' dorm : east \n', {'dorm':'east'}, 2), - - # Warnings from missing trailing newline - ('e^(i*pi)+1:0', {'e^(i*pi)+1':'0'}, 1), - ('east:west\nnorth:south', {'east':'west', 'north':'south'}, 1), - ] - -kvseq_cases = [ - ([], '', 0), - - # Make sure that we handle non-ascii characters (also wider than 8 bits) - ([(u'\u03bbx', u'x')], '\xce\xbbx:x\n', 0), - - # If it's a UTF-8 str, make sure that it's equivalent to the same - # string, decoded. - ([('\xce\xbbx', 'x')], '\xce\xbbx:x\n', 0), - - ([('openid', 'useful'), ('a', 'b')], 'openid:useful\na:b\n', 0), - - # Warnings about leading whitespace - ([(' openid', 'useful'), ('a', 'b')], ' openid:useful\na:b\n', 2), - - # Warnings about leading and trailing whitespace - ([(' openid ', ' useful '), - (' a ', ' b ')], ' openid : useful \n a : b \n', 8), - - # warnings about leading and trailing whitespace, but not about - # internal whitespace. - ([(' open id ', ' use ful '), - (' a ', ' b ')], ' open id : use ful \n a : b \n', 8), - - ([(u'foo', 'bar')], 'foo:bar\n', 0), - ] - -kvexc_cases = [ - [('openid', 'use\nful')], - [('open\nid', 'useful')], - [('open\nid', 'use\nful')], - [('open:id', 'useful')], - [('foo', 'bar'), ('ba\n d', 'seed')], - [('foo', 'bar'), ('bad:', 'seed')], - ] - -class KVExcTest(unittest.TestCase): - def __init__(self, seq): - unittest.TestCase.__init__(self) - self.seq = seq - - def shortDescription(self): - return 'KVExcTest for %r' % (self.seq,) - - def runTest(self): - self.failUnlessRaises(ValueError, kvform.seqToKV, self.seq) - -class GeneralTest(KVBaseTest): - kvform = '<None>' - - def test_convert(self): - result = kvform.seqToKV([(1,1)]) - self.failUnlessEqual(result, '1:1\n') - self.checkWarnings(2) - -def pyUnitTests(): - tests = [KVDictTest(*case) for case in kvdict_cases] - tests.extend([KVSeqTest(*case) for case in kvseq_cases]) - tests.extend([KVExcTest(case) for case in kvexc_cases]) - tests.append(unittest.defaultTestLoader.loadTestsFromTestCase(GeneralTest)) - return unittest.TestSuite(tests) diff --git a/askbot/deps/openid/test/linkparse.py b/askbot/deps/openid/test/linkparse.py deleted file mode 100644 index 28cab023..00000000 --- a/askbot/deps/openid/test/linkparse.py +++ /dev/null @@ -1,109 +0,0 @@ -from askbot.deps.openid.consumer.html_parse import parseLinkAttrs -import os.path -import codecs -import unittest - -def parseLink(line): - parts = line.split() - optional = parts[0] == 'Link*:' - assert optional or parts[0] == 'Link:' - - attrs = {} - for attr in parts[1:]: - k, v = attr.split('=', 1) - if k[-1] == '*': - attr_optional = 1 - k = k[:-1] - else: - attr_optional = 0 - - attrs[k] = (attr_optional, v) - - return (optional, attrs) - -def parseCase(s): - header, markup = s.split('\n\n', 1) - lines = header.split('\n') - name = lines.pop(0) - assert name.startswith('Name: ') - desc = name[6:] - return desc, markup, map(parseLink, lines) - -def parseTests(s): - tests = [] - - cases = s.split('\n\n\n') - header = cases.pop(0) - tests_line, _ = header.split('\n', 1) - k, v = tests_line.split(': ') - assert k == 'Num Tests' - num_tests = int(v) - - for case in cases[:-1]: - desc, markup, links = parseCase(case) - tests.append((desc, markup, links, case)) - - return num_tests, tests - -class _LinkTest(unittest.TestCase): - def __init__(self, desc, case, expected, raw): - unittest.TestCase.__init__(self) - self.desc = desc - self.case = case - self.expected = expected - self.raw = raw - - def shortDescription(self): - return self.desc - - def runTest(self): - actual = parseLinkAttrs(self.case) - i = 0 - for optional, exp_link in self.expected: - if optional: - if i >= len(actual): - continue - - act_link = actual[i] - for k, (o, v) in exp_link.items(): - if o: - act_v = act_link.get(k) - if act_v is None: - continue - else: - act_v = act_link[k] - - if optional and v != act_v: - break - - self.assertEqual(v, act_v) - else: - i += 1 - - assert i == len(actual) - -def pyUnitTests(): - here = os.path.dirname(os.path.abspath(__file__)) - test_data_file_name = os.path.join(here, 'linkparse.txt') - test_data_file = codecs.open(test_data_file_name, 'r', 'utf-8') - test_data = test_data_file.read() - test_data_file.close() - - num_tests, test_cases = parseTests(test_data) - - tests = [_LinkTest(*case) for case in test_cases] - - def test_parseSucceeded(): - assert len(test_cases) == num_tests, (len(test_cases), num_tests) - - check_desc = 'Check that we parsed the correct number of test cases' - check = unittest.FunctionTestCase( - test_parseSucceeded, description=check_desc) - tests.insert(0, check) - - return unittest.TestSuite(tests) - -if __name__ == '__main__': - suite = pyUnitTests() - runner = unittest.TextTestRunner() - runner.run(suite) diff --git a/askbot/deps/openid/test/linkparse.txt b/askbot/deps/openid/test/linkparse.txt deleted file mode 100644 index 74c63ca7..00000000 --- a/askbot/deps/openid/test/linkparse.txt +++ /dev/null @@ -1,584 +0,0 @@ -Num Tests: 72 - -OpenID link parsing test cases -Copyright (C) 2005-2008, JanRain, Inc. -See COPYING for license information. - -File format ------------ - -All text before the first triple-newline (this chunk) should be ignored. - -This file may be interpreted as Latin-1 or UTF-8. - -Test cases separated by three line separators (`\n\n\n'). The test -cases consist of a headers section followed by a data block. These are -separated by a double newline. The headers consist of the header name, -followed by a colon, a space, the value, and a newline. There must be -one, and only one, `Name' header for a test case. There may be zero or -more link headers. The `Link' header consists of whitespace-separated -attribute pairs. A link header with an empty string as a value -indicates an empty but present link tag. The attribute pairs are `=' -separated and not quoted. - -Optional Links and attributes have a trailing `*'. A compilant -implementation may produce this as output or may not. A compliant -implementation will not produce any output that is absent from this -file. - - -Name: No link tag at all - -<html> -<head> -</head> -</html> - - -Name: Link element first - -<link> - - -Name: Link inside HTML, not head - -<html> -<link> - - -Name: Link inside head, not html - -<head> -<link> - - -Name: Link inside html, after head - -<html> -<head> -</head> -<link> - - -Name: Link inside html, before head - -<html> -<link> -<head> - - -Name: Link before html and head - -<link> -<html> -<head> - - -Name: Link after html document with head - -<html> -<head> -</head> -</html> -<link> - - -Name: Link inside html inside head, inside another html - -<html> -<head> -<html> -<link> - - -Name: Link inside html inside head - -<head> -<html> -<link> - - -Name: link inside body inside head inside html - -<html> -<head> -<body> -<link> - - -Name: Link inside head inside head inside html - -<html> -<head> -<head> -<link> - - -Name: Link inside script inside head inside html - -<html> -<head> -<script> -<link> -</script> - - -Name: Link inside comment inside head inside html - -<html> -<head/> -<link> - - -Name: Link inside of head after short head - -<html> -<head/> -<head> -<link> - - -Name: Plain vanilla -Link: - -<html> -<head> -<link> - - -Name: Ignore tags in the <script:... > namespace -Link*: - -<html> -<head> -<script:paddypan> -<link> -</script:paddypan> - - -Name: Short link tag -Link: - -<html> -<head> -<link/> - - -Name: Spaces in the HTML tag -Link: - -<html > -<head> -<link> - - -Name: Spaces in the head tag -Link: - -<html> -<head > -<link> - - -Name: Spaces in the link tag -Link: - -<html> -<head> -<link > - - -Name: No whitespace -Link: - -<html><head><link> - - -Name: Closed head tag -Link: - -<html> -<head> -<link> -</head> - - -Name: One good, one bad (after close head) -Link: - -<html> -<head> -<link> -</head> -<link> - - -Name: One good, one bad (after open body) -Link: - -<html> -<head> -<link> -<body> -<link> - - -Name: ill formed (missing close head) -Link: - -<html> -<head> -<link> -</html> - - -Name: Ill formed (no close head, link after </html>) -Link: - -<html> -<head> -<link> -</html> -<link> - - -Name: Ignore random tags inside of html -Link: - -<html> -<delicata> -<head> -<title> -<link> - - -Name: case-folding -Link*: - -<HtMl> -<hEaD> -<LiNk> - - -Name: unexpected tags -Link: - -<butternut> -<html> -<summer> -<head> -<turban> -<link> - - -Name: un-closed script tags -Link*: - -<html> -<head> -<script> -<link> - - -Name: un-closed script tags (no whitespace) -Link*: - -<html><head><script><link> - - -Name: un-closed comment -Link*: - -<html> -<head> -<!-- -<link> - - -Name: un-closed CDATA -Link*: - -<html> -<head> -<![CDATA[ -<link> - - -Name: cdata-like -Link*: - -<html> -<head> -<![ACORN[ -<link> -]]> - - -Name: comment close only -Link: - -<html> -<head> -<link> ---> - - -Name: Vanilla, two links -Link: -Link: - -<html> -<head> -<link> -<link> - - -Name: extra tag, two links -Link: -Link: - -<html> -<gold nugget> -<head> -<link> -<link> - - -Name: case-fold, body ends, two links -Link: -Link*: - -<html> -<head> -<link> -<LiNk> -<body> -<link> - - -Name: simple, non-quoted rel -Link: rel=openid.server - -<html><head><link rel=openid.server> - - -Name: short tag has rel -Link: rel=openid.server - -<html><head><link rel=openid.server/> - - -Name: short tag w/space has rel -Link: rel=openid.server - -<html><head><link rel=openid.server /> - - -Name: extra non-attribute, has rel -Link: rel=openid.server - -<html><head><link hubbard rel=openid.server> - - -Name: non-attr, has rel, short -Link: rel=openid.server - -<html><head><link hubbard rel=openid.server/> - - -Name: non-attr, has rel, short, space -Link: rel=openid.server - -<html><head><link hubbard rel=openid.server /> - - -Name: misplaced slash has rel -Link: rel=openid.server - -<html><head><link / rel=openid.server> - - -Name: quoted rel -Link: rel=openid.server - -<html><head><link rel="openid.server"> - - -Name: single-quoted rel -Link: rel=openid.server - -<html><head><link rel='openid.server'> - - -Name: two links w/ rel -Link: x=y -Link: a=b - -<html><head><link x=y><link a=b> - - -Name: non-entity -Link: x=&y - -<html><head><link x=&y> - - -Name: quoted non-entity -Link: x=&y - -<html><head><link x="&y"> - - -Name: quoted entity -Link: x=& - -<html><head><link x="&"> - - -Name: entity not processed -Link: x= - -<html><head><link x=""> - - -Name: < -Link: x=< - -<html><head><link x="<"> - - -Name: > -Link: x=> - -<html><head><link x=">"> - - -Name: " -Link: x=" - -<html><head><link x="""> - - -Name: &" -Link: x=&" - -<html><head><link x="&""> - - -Name: mixed entity and non-entity -Link: x=&"…> - -<html><head><link x="&"…>"> - - -Name: mixed entity and non-entity (w/normal chars) -Link: x=x&"…>x - -<html><head><link x="x&"…>x"> - - -Name: broken tags -Link*: x=y - -<html><head><link x=y<> - - -Name: missing close pointy -Link*: x=y -Link: z=y - -<html><head><link x=y<link z=y /> - - -Name: missing attribute value -Link: x=y y*= -Link: x=y - -<html><head><link x=y y=><link x=y /> - - -Name: Missing close pointy (no following) -Link*: x=y - -<html><head><link x=y - - -Name: Should be quoted -Link*: x=< - -<html><head><link x="<"> - - -Name: Should be quoted (2) -Link*: x=> - -<html><head><link x=">"> - - -Name: Repeated attribute -Link: x=y - -<html><head><link x=z x=y> - - -Name: Repeated attribute (2) -Link: x=y - -<html><head><link x=y x=y> - - -Name: Two attributes -Link: x=y y=z - -<html><head><link x=y y=z> - - -Name: Well-formed link rel="openid.server" -Link: rel=openid.server href=http://www.myopenid.com/server - -<html> - <head> - <link rel="openid.server" - href="http://www.myopenid.com/server" /> - </head> -</html> - - -Name: Well-formed link rel="openid.server" and "openid.delegate" -Link: rel=openid.server href=http://www.myopenid.com/server -Link: rel=openid.delegate href=http://example.myopenid.com/ - -<html><head><link rel="openid.server" - href="http://www.myopenid.com/server" /> - <link rel="openid.delegate" href="http://example.myopenid.com/" /> -</head></html> - - -Name: from brian's livejournal page -Link: rel=stylesheet href=http://www.livejournal.com/~serotta/res/319998/stylesheet?1130478711 type=text/css -Link: rel=openid.server href=http://www.livejournal.com/openid/server.bml - -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml"> - <head> - <link rel="stylesheet" - href="http://www.livejournal.com/~serotta/res/319998/stylesheet?1130478711" - type="text/css" /> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> - <meta name="foaf:maker" - content="foaf:mbox_sha1sum '12f8abdacb5b1a806711e23249da592c0d316260'" /> - <meta name="robots" content="noindex, nofollow, noarchive" /> - <meta name="googlebot" content="nosnippet" /> - <link rel="openid.server" - href="http://www.livejournal.com/openid/server.bml" /> - <title>Brian</title> - </head> - - -Name: non-ascii (Latin-1 or UTF8) -Link: x=® - -<html><head><link x="®"> - - diff --git a/askbot/deps/openid/test/n2b64 b/askbot/deps/openid/test/n2b64 deleted file mode 100644 index b12a2460..00000000 --- a/askbot/deps/openid/test/n2b64 +++ /dev/null @@ -1,650 +0,0 @@ -AA== 0 -AQ== 1 -Ag== 2 -Aw== 3 -BA== 4 -BQ== 5 -Bg== 6 -Bw== 7 -CA== 8 -CQ== 9 -Cg== 10 -Cw== 11 -DA== 12 -DQ== 13 -Dg== 14 -Dw== 15 -EA== 16 -EQ== 17 -Eg== 18 -Ew== 19 -FA== 20 -FQ== 21 -Fg== 22 -Fw== 23 -GA== 24 -GQ== 25 -Gg== 26 -Gw== 27 -HA== 28 -HQ== 29 -Hg== 30 -Hw== 31 -IA== 32 -IQ== 33 -Ig== 34 -Iw== 35 -JA== 36 -JQ== 37 -Jg== 38 -Jw== 39 -KA== 40 -KQ== 41 -Kg== 42 -Kw== 43 -LA== 44 -LQ== 45 -Lg== 46 -Lw== 47 -MA== 48 -MQ== 49 -Mg== 50 -Mw== 51 -NA== 52 -NQ== 53 -Ng== 54 -Nw== 55 -OA== 56 -OQ== 57 -Og== 58 -Ow== 59 -PA== 60 -PQ== 61 -Pg== 62 -Pw== 63 -QA== 64 -QQ== 65 -Qg== 66 -Qw== 67 -RA== 68 -RQ== 69 -Rg== 70 -Rw== 71 -SA== 72 -SQ== 73 -Sg== 74 -Sw== 75 -TA== 76 -TQ== 77 -Tg== 78 -Tw== 79 -UA== 80 -UQ== 81 -Ug== 82 -Uw== 83 -VA== 84 -VQ== 85 -Vg== 86 -Vw== 87 -WA== 88 -WQ== 89 -Wg== 90 -Ww== 91 -XA== 92 -XQ== 93 -Xg== 94 -Xw== 95 -YA== 96 -YQ== 97 -Yg== 98 -Yw== 99 -ZA== 100 -ZQ== 101 -Zg== 102 -Zw== 103 -aA== 104 -aQ== 105 -ag== 106 -aw== 107 -bA== 108 -bQ== 109 -bg== 110 -bw== 111 -cA== 112 -cQ== 113 -cg== 114 -cw== 115 -dA== 116 -dQ== 117 -dg== 118 -dw== 119 -eA== 120 -eQ== 121 -eg== 122 -ew== 123 -fA== 124 -fQ== 125 -fg== 126 -fw== 127 -AIA= 128 -AIE= 129 -AII= 130 -AIM= 131 -AIQ= 132 -AIU= 133 -AIY= 134 -AIc= 135 -AIg= 136 -AIk= 137 -AIo= 138 -AIs= 139 -AIw= 140 -AI0= 141 -AI4= 142 -AI8= 143 -AJA= 144 -AJE= 145 -AJI= 146 -AJM= 147 -AJQ= 148 -AJU= 149 -AJY= 150 -AJc= 151 -AJg= 152 -AJk= 153 -AJo= 154 -AJs= 155 -AJw= 156 -AJ0= 157 -AJ4= 158 -AJ8= 159 -AKA= 160 -AKE= 161 -AKI= 162 -AKM= 163 -AKQ= 164 -AKU= 165 -AKY= 166 -AKc= 167 -AKg= 168 -AKk= 169 -AKo= 170 -AKs= 171 -AKw= 172 -AK0= 173 -AK4= 174 -AK8= 175 -ALA= 176 -ALE= 177 -ALI= 178 -ALM= 179 -ALQ= 180 -ALU= 181 -ALY= 182 -ALc= 183 -ALg= 184 -ALk= 185 -ALo= 186 -ALs= 187 -ALw= 188 -AL0= 189 -AL4= 190 -AL8= 191 -AMA= 192 -AME= 193 -AMI= 194 -AMM= 195 -AMQ= 196 -AMU= 197 -AMY= 198 -AMc= 199 -AMg= 200 -AMk= 201 -AMo= 202 -AMs= 203 -AMw= 204 -AM0= 205 -AM4= 206 -AM8= 207 -ANA= 208 -ANE= 209 -ANI= 210 -ANM= 211 -ANQ= 212 -ANU= 213 -ANY= 214 -ANc= 215 -ANg= 216 -ANk= 217 -ANo= 218 -ANs= 219 -ANw= 220 -AN0= 221 -AN4= 222 -AN8= 223 -AOA= 224 -AOE= 225 -AOI= 226 -AOM= 227 -AOQ= 228 -AOU= 229 -AOY= 230 -AOc= 231 -AOg= 232 -AOk= 233 -AOo= 234 -AOs= 235 -AOw= 236 -AO0= 237 -AO4= 238 -AO8= 239 -APA= 240 -APE= 241 -API= 242 -APM= 243 -APQ= 244 -APU= 245 -APY= 246 -APc= 247 -APg= 248 -APk= 249 -APo= 250 -APs= 251 -APw= 252 -AP0= 253 -AP4= 254 -AP8= 255 -AQA= 256 -AQE= 257 -AQI= 258 -AQM= 259 -AQQ= 260 -AQU= 261 -AQY= 262 -AQc= 263 -AQg= 264 -AQk= 265 -AQo= 266 -AQs= 267 -AQw= 268 -AQ0= 269 -AQ4= 270 -AQ8= 271 -ARA= 272 -ARE= 273 -ARI= 274 -ARM= 275 -ARQ= 276 -ARU= 277 -ARY= 278 -ARc= 279 -ARg= 280 -ARk= 281 -ARo= 282 -ARs= 283 -ARw= 284 -AR0= 285 -AR4= 286 -AR8= 287 -ASA= 288 -ASE= 289 -ASI= 290 -ASM= 291 -ASQ= 292 -ASU= 293 -ASY= 294 -ASc= 295 -ASg= 296 -ASk= 297 -ASo= 298 -ASs= 299 -ASw= 300 -AS0= 301 -AS4= 302 -AS8= 303 -ATA= 304 -ATE= 305 -ATI= 306 -ATM= 307 -ATQ= 308 -ATU= 309 -ATY= 310 -ATc= 311 -ATg= 312 -ATk= 313 -ATo= 314 -ATs= 315 -ATw= 316 -AT0= 317 -AT4= 318 -AT8= 319 -AUA= 320 -AUE= 321 -AUI= 322 -AUM= 323 -AUQ= 324 -AUU= 325 -AUY= 326 -AUc= 327 -AUg= 328 -AUk= 329 -AUo= 330 -AUs= 331 -AUw= 332 -AU0= 333 -AU4= 334 -AU8= 335 -AVA= 336 -AVE= 337 -AVI= 338 -AVM= 339 -AVQ= 340 -AVU= 341 -AVY= 342 -AVc= 343 -AVg= 344 -AVk= 345 -AVo= 346 -AVs= 347 -AVw= 348 -AV0= 349 -AV4= 350 -AV8= 351 -AWA= 352 -AWE= 353 -AWI= 354 -AWM= 355 -AWQ= 356 -AWU= 357 -AWY= 358 -AWc= 359 -AWg= 360 -AWk= 361 -AWo= 362 -AWs= 363 -AWw= 364 -AW0= 365 -AW4= 366 -AW8= 367 -AXA= 368 -AXE= 369 -AXI= 370 -AXM= 371 -AXQ= 372 -AXU= 373 -AXY= 374 -AXc= 375 -AXg= 376 -AXk= 377 -AXo= 378 -AXs= 379 -AXw= 380 -AX0= 381 -AX4= 382 -AX8= 383 -AYA= 384 -AYE= 385 -AYI= 386 -AYM= 387 -AYQ= 388 -AYU= 389 -AYY= 390 -AYc= 391 -AYg= 392 -AYk= 393 -AYo= 394 -AYs= 395 -AYw= 396 -AY0= 397 -AY4= 398 -AY8= 399 -AZA= 400 -AZE= 401 -AZI= 402 -AZM= 403 -AZQ= 404 -AZU= 405 -AZY= 406 -AZc= 407 -AZg= 408 -AZk= 409 -AZo= 410 -AZs= 411 -AZw= 412 -AZ0= 413 -AZ4= 414 -AZ8= 415 -AaA= 416 -AaE= 417 -AaI= 418 -AaM= 419 -AaQ= 420 -AaU= 421 -AaY= 422 -Aac= 423 -Aag= 424 -Aak= 425 -Aao= 426 -Aas= 427 -Aaw= 428 -Aa0= 429 -Aa4= 430 -Aa8= 431 -AbA= 432 -AbE= 433 -AbI= 434 -AbM= 435 -AbQ= 436 -AbU= 437 -AbY= 438 -Abc= 439 -Abg= 440 -Abk= 441 -Abo= 442 -Abs= 443 -Abw= 444 -Ab0= 445 -Ab4= 446 -Ab8= 447 -AcA= 448 -AcE= 449 -AcI= 450 -AcM= 451 -AcQ= 452 -AcU= 453 -AcY= 454 -Acc= 455 -Acg= 456 -Ack= 457 -Aco= 458 -Acs= 459 -Acw= 460 -Ac0= 461 -Ac4= 462 -Ac8= 463 -AdA= 464 -AdE= 465 -AdI= 466 -AdM= 467 -AdQ= 468 -AdU= 469 -AdY= 470 -Adc= 471 -Adg= 472 -Adk= 473 -Ado= 474 -Ads= 475 -Adw= 476 -Ad0= 477 -Ad4= 478 -Ad8= 479 -AeA= 480 -AeE= 481 -AeI= 482 -AeM= 483 -AeQ= 484 -AeU= 485 -AeY= 486 -Aec= 487 -Aeg= 488 -Aek= 489 -Aeo= 490 -Aes= 491 -Aew= 492 -Ae0= 493 -Ae4= 494 -Ae8= 495 -AfA= 496 -AfE= 497 -AfI= 498 -AfM= 499 -AfQ= 500 -AfU= 501 -AfY= 502 -Afc= 503 -Afg= 504 -Afk= 505 -Afo= 506 -Afs= 507 -Afw= 508 -Af0= 509 -Af4= 510 -Af8= 511 -AgA= 512 -ALDs7paJl5xPh6ORH61iDA6pONpV0rTjGiTkLEW2JsVsRKaRiS4AGn2PTR1UZXP0vXAmRXwdSegQgWPUp3Hm3RofRcDh1SykZBLif7ulau1hVO+rhwRyKc7F8F+7LcMf/v+s73eOXUDbbI2r52wfr7skZy/IELhsC8EK6HzhACI3 124241322153253947064453752054205174382289463089695815605736438952932114700118408072544073767229325045596832952652232288773280299665950768731398747700657715829631597019676014848183966683866396215048196276450953653433516126074463193382764063985175903718735372053536664711482497859539116009770850968340298474039 -AOzgU1s6Pd2IkrJlvGND8legXTe50nyDCocI5mwT9rW0YsisY5jaaEOcu51BAq9MmXBPeVX0k/jlXwH4Pn3mCpUAU1rEOsTdcmSJp35siKliDdhTZHHdZNMW+igfXGX5OCsA/BaBcGnE6NnrGWXKyTOoVUGQLEkL2T5yhNUaCT83 166340174936369324883416612727439279977041963320514134445183426741643586944819834936989524033374309932122967866930503619179389342537723598234062828695747850043368572301869699886931403612266216965783079972698791813140295203826980649434652168563255385527187360027803388963151668338040517316899628026707657178935 -AO8hrpw+lDiJ13JahLtCb1RenupQcNd0wlTSck9OLL8wB/x6gAoj0PTLV05eZIbz43N3GUSDmmckjlxdHXiBJ9rsoB0P95l1CWIV+4rXblCqxmOdmlm6VZ13bqbI0x7l0cjeMrkmk+yJ067WqUolqQBlUWMTuJVfkxALJYH5xr/C 167923899524385316022824282304301434707626789716026029252173742527362300338760906999615029022863637963070711762128687835779073122264515776657475985362344360699359591353388569856862973447791264902182048648600267737826849280828116753682917256540180401899752566540869918949003470368970029744573140084219550547906 -QxAn7yrdVs5tlHV+Glbqdaj67c6Ni8am3bBLOL8PV5HbdrLf2xIPmNugo6MfUwFSnT+ZPJ51+VTOsItaNwCFju0Eh1cqyP3JWyLRPE7emKuo6xRhf+5ik0pTg77LEF4JXW6ofDqirpR4alFi0G2d9yImQPphsYJwYGF/nNT8u0Q= 47093316905427544098193936500644355852669366083115552072584429220248776817916430034648347490325490701471113667554329499736495877969341478442613611948220957798780043076906836236556612316544460763366275536846463456405604189392790111985912854476264292503164100482712281088955640964034295834935468665872932715332 -AI9PVzrbJUvmCihwSFans1lBKwudGEZpWWu8pkSK2zVgzGhMvUoGgMp6TG2zsUd1tV8zv7KsVD2t6pXmjT1wPUynufq97GVHI06SGpflDTt30WboYRh3DgYxvso1sOjUXpnDezcaqc2Aiz4nV5MSShkBlyBjA8z2worHDE+uXqw0 100635651531872121827765663065728398779771663753008344681972226973080394359405041113312675686974926993279775427390065833081040771269307007695807025882757371805607979134114890454059957194316765342461291139168706134406917264848659448693866813989352429841300235734400772946895458374870482441457514575059390213172 -FiinVicXOqqRLpxcGTorQpSAGeQ/PfDOuzYK9ViFtmPv6D0cYPfhUH4qXEHOejvmX+0b4lfaX8MWPVZxlqpfXiU9BhG76HJxkLF4ysipukeOvhoHzvcxE5bnhSF1i//bOSifATBLBEZInwqSVg5tHHPuuCkwTL91NqhOulp7Lsk= 15560440884463435471963622630292643727112462888414585143143739400703889930416938984547754943252935620248108237258540176511252143752416771350868493435174026287082706690332705481726295797196444796135827460509780634261726494455068460028424141500629527968240913757449787164107068039175831847071025316475940056777 -aYrxyQN/hkBne2ayqo2/iDLF3DZGgk080SOMJfsj9h3Z1OfFZM7TJA+y+/O7niqatosvKrfHrAw+Qs7c6tCZ6NPwYJ4QJLOF9bqH2u2a3fkI954voNUctlUagYUJsZXV8hdhLM6NwUyIZ3ZFkPcpTZp7nKQQ84tr1a8VjDIT5/o= 74114640794666001532816944350975062126079079113921109750255283189037502412929005615388097912507598112836936032143435813588205939470002911374442844578739574773399427907766548612582213272643279263782396527705126350063372192910060171635870872236876399794128383338399728947176692692942605589343038282957050865658 -AMpCUeKUX/vtRslWiUUuXNl1KA9uDAWjMUkTrdsxxRDESI7iZIn3TR9lW+0kV5fzkLF18iYLAwSGBmX1PS/T0UVFmoBPJ9yS7yktNL0lpQ3noyGFn8HHZ6XB3FkH3jegIfGbvwwhnhhFzpHPrXlpO5iU5Y+rexzp2XHWt4yJGuIL 142031143422642739313498629438991149460874309300342349421794421544918823888598660275343727563280565210534243383322796489809683834300630555650331646026843796764549231159336347965502383849513994449309613369541991287590422095953275586374371960367000083487965487661436037637475372929033613295072397262739084075531 -AIMIQVz0JIEKEI+PREu94m3v9XoiU/Q0CpsSuqkwSSje+Wyul5ea9oU5qgtOpdkMUOW91BJo0DW/GMZ8v3C4qyyP29TtjCcAHObJi9hfLSlnTSuzXZnDStooYYKqzfToLToCaAJKCXiXAVW0vWtapLnyqafrf/KgyGZ5u4HfXKY0 92013973253053602863003242446596060337454881568126916916519869242232429836082762281129448384605359749247852792606718908482332975424967542242332487707042773885428473061056052851768940900752317020681189773407893371297668591494665352294885305475871917069040377145530889271334616499701769138948975263435137525300 -ANfP+zPBTR27afneyac1KJhOB5Pq3AXB+SoAXJvQI/GkSoNhw5KdfqoIkLcoJi8wClCm424Gm1AdrdGwDFOM/iKTSPkrvMag93+b2EbQGX66/n2X3YRFNkgq/Gtb+2M8oCcAL054Z/iiMD67aU5RWzjqS64ePHsn01bJ7dqLgpMO 151548639867177154896951257541227014781655576169318283047778755573323724856619156348444192550664853912434681577093459933599575436686424046466113215132845213008587152894642577278656978304699131916299275797578171518984206145555369576872231567191579337901913492071976578289189524123204040497290426960375042970382 -AK0kHtacLGu1NFWMADq2rG8hpzM4UEYyPOL+aMJbnwXcUYptRIxb0YFZg35RN/RiZs4lQsiq+kEJKzMMV71TsJq59vMkIZhZoB3t8g9ZqBZuq0JYcTICDwRpNSttJidVpJ6P9sR3s1xPMYKdlSwt6EEc9htOXfZU+yHKYgn98X60 121583812047864398969816595368193171848971298823388059338224714026742264861090347096116404814514279627148994345584790617974476594451626305761040465570524035369799925437276511604752129817947910677564301623631349399504187314174538914591944778074509068973226322566160587813128746039859381466427380402262866230964 -W3sZlWW1Aev3x/DiH9MzwCAZzBj++x9cknLfGAHwgFqkLH6vimEH/r8hi85hzlCOG5CjwhoZ0D/Hsfr26ZJ3X4chG84byrfDnek1V9mm1++v+clJvlYgcuVgn2Opsba2TILTm1MDB+Ujs9brJ2AAKrE9+ep5nvtQVeG9PUGtdlo= 64240043913835461386212515483198059541440539167395859777194837833769712010594411295323900074066077107346806786205590345517755715510695858065925747020336398305793661773798243627926904542715123849691490667964262778804487343218972081260210371192903128886030021862362141928329650003493687310970684093289133340250 -FTQRk9/BIj21gbLwI22fHJWYj+8Ghdcc613hOtJ+/hQmh73HwTXLpaGK9aCptxVbpjW0r/bxaRjmgxu9u1CCZh5yRd7Z46Wk/LIPXGd3ycQzqRMFB7TISFQGJIcFoxRp3Eb5wa2OyrUg7c/D+kb7oFJq9P7mEwIh8TpLzwmu4SU= 14889529068556301710329043521845510156960298822469914567758538023025100741826628180855835334285179977296740667353391766487166458692144569279381035582718738461626140662441222061900764829681913534146898551570916312642104487829660946024590782808750587095559047648957238487820069966851521487428624726655438348581 -APYXO6uGvs9qWiEAkcWsaCaCrGJJCP2Z1g++XlJ67oZIgEoWITn3T/R2/c4edAfwUUzNHAYZN1h2dSrRoqlrRXrbxFtGOuRCUrXcGLFFcEbTrtm+z5z8xGRfcorx7Cu3FIBPMK5XcGPcbRZdyP1gBkeDMvuBNAo0/To+LP/dhCNM 172810804474418448604443090732221483571611665465870520701624598983692130272337358406272727413570938793741430131635927237320173996217984860203754686741782921346604605228620148450611724714868551781003004492587584071978757421616871762681049508123223983431502852926521520561941051298696758046005573332373854233420 -AIDNxhnDEe1kTJ3XGfTS8zKXeXPRdw5yifm8j8Ibzj/quExy7hFPtKct8hRskPR2qwTlMiW9Ra8Npg2USsqHV0rBoIkX7E3psxq5LBfp/q00l3SEBuLL4K2FGR87bPgU+Duk3RVrNMnColiTcnAR5XkoeWhn/r9MfJMIN9Y0FEh8 90449107125498302548188660544012777357148118984122992664008792590422284061463729084479315745509706793674355738023180454297730948397413371686013210006834869294564190666543874617716180411178090109573192518129248278410216362657350215009192850017507998797754539132540293137589672869131300859207213449571846080636 -AIRWavxYRsGlH0Yr0DudwrpYtbrByf9ZsDawKom7ubiRfepqYzcBlwt4adMMnkYSaXeYtOsD4KBm2ZvLKN3++RkYNmxgkyarORBEg7ERyiThBj7Ksw57pNHCAoHtBEhH7Wp9mHhuZtPvPgCEptmwCu9rYhLt4zZp+Zq8a02dkXvM 92930601962515884925250459851491509622611227724602941760145671636277317511265759558869239180653492283311584982044597979173761619470326096725838197524704577188104121460089235709339932110663536557497112887112782062772810759971739760085128369628777812332518137107605855679096146402427144185104230596200130247628 -AMNJGLcAiJtL5fUfkesWKYJurdYSnvsOZeZcrg7bemkEVjF6S9CcojimUl+ncr/YY5/EXnU0mg84fObtDxWWdJ7z7l0CFcoALTyEatDYKshT0xvdKY3u+LUShxIAyk8EcGnf+KoEaa4Mx3tg2oTBnVegXClOakNTWw8bu2ItagoQ 137134165107366719462230252606689766470445826753581409513106273517221906418464863733870948759313279128624638614534848890858250894834883265387344539280755177217350585564186248554307335197387734431939154077778003706720017441895613190141376534460438929588407764609772857975000507660651583780079804513519571438096 -BmGPZt8XqqI1PuLN4K1/PZMi2rfOYtHEMrcwZdSjKRm5qTkd0Pbb/5zPV07TnM0uLRvIQYTLloEY+GYyn0K5gDTEZpEtQ8ee6Y87zYGDwcf20eqYNxkA7FVV71vqCP/Uw3Oi6B+hMvsWZbvv2vH6MkAeADCrezOtwqVS+irftyc= 4480956865245875120472829476982311611308898564405318773810939350829150182630548948231116574193987272498161864310429976564278532538229396846813874244969927890037756969704618336242255039858182439641759659872128285423988638335967412040624105824571426792562334458751137508116412821914961236269913776304372561703 -APqFgCIYbJWuRyEGuOStPvcprj2PILQ0JpgwQ2jLKn3DvkWSd83qh7PWGKozGavsjh803K+ZzI7P2wP+Nc0r0El3q4nzaHvKaCtVRyMwbXv9wYLFZICeM6J1l9ljUMts4tbDoPzkIy3ScU7pYxarBWqMkcBU8qL6NN1vEdkeu0fW 175922170410080716883576123079908758276229469783745771772401183721225804343343063277676406040455068452258961299511343441961963941297631097736305638850193978800615558067791016294285848963023036905095022181004058235239390870177623185946205281141386416867569004073524130001309977475780893497185890756991672600534 -APA/rCcGeH6A+6ZwaBBDM6mB6tTD8mjkrOWEo/pK3MCZ+rrErMBnFp2S19GhlLOfuY8BHS+D834Fdm8+3wKYkWnXZpGb+e3v8ofOQ34G1HvzULOYtrEiC4ISZRt2SSyz2hU+PBXjVnplsHWTRxZDmBxTJdgli4ItAqxGCxj/aJ9m 168708388929747822981923386197903561880341990893945097067702518857172133291360611402092714329372304718329568897960770488377524912057166920574319430820488930520807742026377043178502591886293565177404635365772829346030773275726024973460121300339258054215286249329967181244588558220467488638468686270735376228198 -AKmwrLP108dCGWOWxE/6woJVLRi/Kra/DvdsPkkrZQmWIlUT7IvwM4gU6bUr4f6wpT08cIQls2cGh7dbSEaO0xLa3mmtKhPiAlzSnz0wuifF3JT9U3uXgUfCZuFtE0z7Oi7WTOrpl3k3GA7JFvXnY0lwblIQALVf6oWyNETnajGl 119160465301384937485959146028591622947513292915838943629387700439301197965652871741710280647524383590817798553034250156068573474278225305190573334054718387045488098320076877626430189054572361967283632592181431701411266656256255758079114072932140551282607247364388070762970060420036793573956057551235306893733 -VTe3rCzAL1Sljo3QAXEkAdBy1ZARHZwtrj6ZNRa5ttqd6/l21g4z3iHCeGo4rnE2F8wYTy+NlugjXw86OS+XojW5y6UzTtx0HX5IJ4POqN64aXWmaklGzroBEYWeuFFKcgQN3NOxkuJoDQ6VElP7Epz69kj5CsKJUwL0SjbNrFY= 59841866347633473702601462509811342285929528424012250265905695635971518533504187799047710303717472950129869674786231155102509311322791323986824635569605105662070745033595366004805920086888891275288347907772640070278731650628917037915863439204501060041944275512863990729926528905725569467329169134226609384534 -AIZt1xGhC/HrvpPASsvVIVdsu//tn0noyJmVYh3FdQ6yIh1uce47iCsrV1yvYqx5ZTbC0vnfnbjFcWqH+HtLX/DelgvhEwzqJ8hwQrfE1ShLG4ZjAVo1Z4GCjrDcEUMlwKcunuSJssuxeQuXwTLS92+q6QeBSS7OmfxPX29CLb4B 94399298271083745508290936113986978382457275531684761701599029877008571741877683365769553170771833981099580359640421358853566501815723434822307977440496208486103754978934472597505865596938563438311337045817621762649604204720249750058676095769230214181772215323235427976398686727606000594646472236822594174465 -NIhTPpWXS82VTA0LTd6TfM+HgLgUcmvnMYtLqPpuqCKZwalAycwl0XFYNyVvaY21J94j92ts/lRYgVtHDhk7/9nLXq5js/lsUnG8rWPHJo11JTxvW+df88aX0pw8u+biOWt87vc1MW1dsMTTsJFJAeBx77qU/Cwto95IVqM7vSE= 36889590210230649939994518345793530042252563793069578097360569338647730438860274349862767107939590441616825589851005429465345268710487649366046960918184701290985280638488938340668212498212581853679035928093386035688597446809895381618260692378376844452061580510108168030682664507293277674052032318576713776417 -KXdi4A2Z7tSiiX9YGtDtxUXIfQvPhcc48rUH+Q2SnXL7fLNmr+F4Rf3RiFBRiHKocPfE94pothop5qQJ5X221/DbEKWK6s+ChfQ636jvRxojoLMab3dPtaAPpDJHrfZMxbT4ZaDJT0tpA2e+zZrzBuDs+UUgCpty9nxtdm1gS7A= 29118662951481660380477444121362422614202367719725087486810943918529894738076273660245405874301505615796632229852040910511025841576465052938308369421493312085081188509808322692130449282585522349552501983296872614029139293444558468751646868108213623606366977549477663987815308260383403466635254115908032940976 -AIOTBZQR2EJJRmoWdRNFLG4fceoS3KnRTHRpPdllhHODqdg+QxTOcOvqIzBqgdD0JgO12SuNAjLQOiz0jhd02qkXw9Y1adGuKvL97ARFtNEuJiNzFAj7KpDLy2zk2rPJp4Lp7cjQs0fe8BQYnTzTsNRGm+4ybln/gse1YWu9w8y5 92394618277596007469808288231093678404089765494062813665106014405059399079199990128824492247005602685377185496959522609467906358619318009231448503013528692450191782140091818984176967246749464502089280153086163239846744554575017530385347720563798041108608545014076448155956762636929707905789978331102411214009 -NzfbJRBF4pqEeborJrjoknJgpfK+DZh2k9cE5dcElMPZ2Zn9im7desWGiBSQnu3KbTO4L/t4+m6nFTNcbIJnqbVSMDHdsfG72rG/t89aOuECQw0HMVVgONNNa6i/mw0jZSWnRLD4fa1YgbUlMd8jeqO9XcBDB4mVtDTxyeGa3vU= 38775530011374537813502898274019389132620116890266344603221997943675706375698597061571989090674289834838060050848545748579361837989319487970580969082824601965845786771062335733318139530316825802589479118956745739691326447349403950997231306042638797277408335778415717988679050762936401945487285814799382535925 -Y4BVPZ6necuLSwaqYEPeZp0lt9tTGFl/WCJJbwg7XpyvuwYKtzagC1NLzY5ymBfwGFw1yRlQuyGsYd9mBfC99DuVCIeh0JNrhJN1bNfoSzy5UO5+dmTr+dm66VGSRS0tFcViDTfCIleTV+zxo/xuZT+Bjxq7kZue8zGkjp42Kmo= 69872189501616471647606976308259279995249122669120675885925763529037695584466011511740991152346215507625265226811128801733353566555339153627478941716586678793853828514394269931890370517258825006937741437480128878717892485074131232336852490940507703859793477547154689914725314529986438108117871674332626168426 -AKCP9Mto4q/a2xNqM4N7PekbKspwt48OGPre+iqVwPrSP/jWKxg3CvvLNZzN5P+/FiUGIklMMFJ8w76OaHIPqKuwckj1gvCLECJEE+UAZWrNKPmpzd/ootN9/kQhNMuloTFCyhXAUUOXJ7Z0WVLb2u6fn4zroszSMBoWQEKC6lcq 112750701794692134675959811050012620191158543234019977304167102486465198271340022889272244811582365901584420008564301920174477182946432553537794834985703732129975734658113610563794129371053853971031300761815004524681756388784922001759202643614966614186697992611399618828963452661554240362943588548146868410154 -APOTAFA2waoAODECaGNgCHa8dNN+cjMnD01M+IeQFytzo9RLMzzzg/gpTUFpyLtFMcfbCkDYQMLXwE4crTimdz5sVvjGQ+5fSFQjoDY6Bw7MO6NAcLzlV/sI/1WyNBKaLQbcl2720n16tdUcdckQNnV+cC2J48CVxYM1c7QQlxA0 171043636512232272455501595416608280460445723238023572475354665686544174728784633443479486247342724860289312593374524429736857970220153680852977711594899595712511352458264354251161579203922747468321999465061463474727943140910084880926005209538535217464825087114791420210981711903880998556269523363208766099508 -AMGpxRlB8WVnsGqyyiy3/mzrPymtJW1o1HcDErK11ZwQV5PwTF3c0THwlnxDmcziLWHSWgPQwfRddVDCXMGW9BffJn+XO6aTcWDPmDAh+1DbWJPE1aqApGbHvQ8HONy90dQMZf1ayuwceWCVTuU1wnHdo9F/sIsRbuu7ic2OJDzY 135994898408425255747055209966103741651849229328236418804928584233229830656742052333413774490626915784901255640138520158698845938184666683995579777154437927013722740366497459963753542029774185193376253885864514386760437194444013834088425088260658670140534670789371556026135595577395047002643901630053097946328 -AJAw4uDYdSYkOrjtwJVWLv3pi1+UxWge4RmkWKqVquTsAVcT2tRZ+MFdHM457Hl7fmFIyxvGZQy4c2v1AbHEfPR8ID2sCRQpdcfrxEUZPMDqxfnHHm0ziny6W4X6ggdBzMp/sBWaVNTBL0e61/pELBGYNRGFMzGws7HQkr/sro1D 101254336834199527040756567675327011562230719161388328289463594628690618298993695452746353237675715087353241661592074446889034411683413957950360025295995263477031608845241728493807755308798509893719674568267846671753070163272328014412744008880395248474446310603301447848026040555910147467745595720879397834051 -AM09TdtXgYL4FI5CGNiVjf0T/AN/pZ5zZsBOi1MAUKMURiXnc1x8VKYTqM9Xb86mqNBBqphynIQG6/3e/YbGJgHlsSdrmKbo+P9daMr02I/7Z76/7Osa8+7Ky6lhVCbU3F0tBH4WvopkCQmuJ267afgvDD5kB+9uNr28deMH00cY 144124056591600568767398029380314564902309327093641173350205276895603332085753288682409279238417493662029954512382520307259348748813767324609446500382301421328754981718014234615523158887865271179104711373675849713359713282937065993613915015084108700238420759344034475478243507306107546245540340758766909867800 -AKDhK+/BKGXbrbBh2vM61OP8LN81YwlJKe68KNwUu4tjXlQg7i49Jis7QKPI/YFPUpSNTu5N2iCgeMnCX4+r3NAfivOao9lw4N3nc9bi839SIWdlokhwBHBYmCIgjehUeBAdkU4jKqlE06pIrpRmSvBtn7O4aWTbT+C++ViYAcGF 112973480670453665543892521898882856059335781900313607790238402438320486344365203510769919022496690291280873287383392088872774202832124927485754495093552572232234532821756395965072330282810574669371524103814871172318519695921477775100282448247625395376072233777533359104085023946019406729587713120941266551173 -ALxDiSxHjfxvP8ETvpE+SyDPTS7q3o3zCK519WTepygC58KSRfvDnIVIyV3toQKzgqD50kF1Ni5D/wuaSs62y3zg3kELX1g+WuBCc8+x50+kDtbHXa1Me3et/OqVS/QeppkcjK1UZMU29fXze6P/w6aQfvKQkE7koeQtZBKkYc0p 132203344567902304830160099595561253300484092355345272411265169562971473393256361094745618829297250316196312398486598077249124198329075791740755862221465178128527292695331061023291345396067863215552021206609309872689233899464919108147533679134727064586730810633196817136739658243232643507412032417747255282985 -VF0YUTvy8Mfi5o6X06DEvLm87r72mAtTdyyLNr0/GXlk0Xj3L2Oi2bVUMtcXQNRXg/mkdGP88pgdaP/eMzqkUU++vJ7t3UgOC1i3SHegpiBhhZh+aZHH/wjFV8Mz2XZB5z8MpMgN+QwALK1TT2Pyt/feQTsOy0imVanB5+OvCeQ= 59242171319056188000481457618922567543461456096441095927600135114274111606802456239311634638536207588762066940095527920532936960549439269891703098017342732142860571277442598349453761561189719823290643146391349978698217357430495238876700400634593256155537598291759795109752990651995982467695091946768443574756 -ezpwBt0N6QhTusiPcKrBvSB6yuk/KShTLUFQHdf5J1u1fgDYrp+aOWuXOFVfOd0bweiG4UxBQNXB2IDFWfYON0fBoaDqNk/41YyqXBSkKbiNWLi1y3zPmwTAiwK0PzYp2EPfk/t/j0HsDbvebu0ygcxb2tPqj4EQ1TXEdD007kU= 86533835313999945727720083706940213467453975054116752898416709637030456504024135513972566184073843025739226187558143854850980654667596935003124034699919861200483994576288766702308068265526535622439762454501169018136389983894783905946543636163866717367545972667876983557989192393479830223914708619684891389509 -U8BT26zT46tTZnkmTNxGUAlXbJhk5cNi4AMSd8fSvZHm55siMFGJ8Jl7mtdzEFR1UFAyEztf2fUhxdtMLe8ei/OJgM0j7myQ9STucEwnsShT7QS/DjBmfvcC42sl1CRpXkb0ZLrEJCPf+crtLKGrG7ExS1oawIAgALBiMQIL6mE= 58812148564290791415180898639607206220554150794356494356250223429674091688305329629529905854147200457536549527135776329004085047145097927266797668252160196098870200925284256433894773392353678965699083286106628662506590268955650280670838340651598082083455821825076016227525614626726458235627297885815646710369 -HfYii3U1SIkBZl09RHaGGA7H3np+qxyNeeCNY07PDl8LwZAaaYk/bHPeBVboan0I2X4o78zCD/gFXFBJ4rxwwUsVjHEioyO2JcpV2/oDOelJBD//78WzBMMSWt7ZKbJV9uYr9ZUM0BUD3fsk1esFCEdnDJdr86U0UMmiig2R+ME= 21039655953870571289679214995029926285040274249531458675115179004718812090027267801012507748013357317597416722235988917212676802092082137617336199787762782958420742299451435320649616271885264333948336627286638368859041172783505464468640994920853000441536629081040963398001710173320125308624362209157720438977 -AICOlee3daFyqTrTdtWjVb5M2rclh9BpIo1CRvKo2bF7NYcjrU0/VvbOnTVXDwdeGMLupbi76f0BrfDxYtkzMXvIZlgoTit4g5ennnklDHFBC5cogaGlri8U28w4/h5oMunZ1O4ezdpRgVJe9nTP/sSEMYiNS5IA7Zshdvm/XccF 90275777798511290102824338787811725003177532250296755103300529948194832904403489332420505850668003332750291879153080212231952155092379375422537931240723308384652734942204313672973885652497290433943089371705605128843469306776615573873479312715317072986990219294942040272550822460408702072075001377245051602693 -L0QUSVIjxvE201b1ztRZyOOxy8vkUz6626TH4tbLwXjjc+AhmrvplaVlavnOgHqve+/L18XNuAYP4BqdxIcWTx+yxBKm4ZS92dRJdcAtccvZpEJtYjdJvI6qbL5Ph6HluaVZwp4dyFyXuZOJGTfYdTb7PUWM0jNT/xsqyjxSQ2U= 33191267986826803728285073844005357792766429917696698533494382218509532051029343127452480789088572904364699220151221680328978554239767633887572649589456766209242252549993823283929686430100804479376247660556781589549613316880150951333982646510273364068770923588389668733632648346075516618646974067295703417701 -APlD9ECKJuACUmQUsbd2GTOpb2PgQVT08C/5hyNEVdA5bWoICX7epmoCKCybdolk+cfEBP6fSz33j+Vn8MbeiHBLdmF6ETbmcyOjldJ902MDvU8dqAa8IgEZN5Uh5x/xzN+3dqk9o0ji7yi291u90rpfIh85PPpDat2B4l5zs9i5 175040148659257809883308984693597046378367187659749953472629929701758633206586720399909808941145946314755491399962797299295431089674294356220216615950668954164397362123668926410543898553191541662075745481299747832013627018846822876386760538344447600390187421938699064459451308870669878673306013635576901916857 -KB7N0tE+A5vFhyrd/m6Qe1wTihkjqmBn+rinfmMAzRlvtxIBSyDLzQsOQs7L4oTG64ABU+YwcWVijvoeZNamaxGl4hatAH1pRqmC/r8FMvC4vqiFTbFHzQhkjM7uoHD1aKnxyBVgjMj0E0KZjrRxydZjIR2p13FXjLP3UQSFtII= 28173452509830313810392326357601136401754938805266458365469366750775669869895498658593356375710132149836430968810246171974040975430205200958564616924399794768861923079158311829444850822144940112488994119845741191519421434257276977333662656888696213514226866147767570046232093727585815615828360199830275208322 -bxFgV7eXwnbQScl4VzS3RTdcMW+NY6pcGkT1UsqHIeDVyBb8DnH/2/Z+DX3zniR1iW6FPdvhJJeQyPIax1ohILa11R27C1TLxGvTrRBGUycxjEcBIxamHveBsXbECWusYLEakeSDg9x4BTWMz1rTQajkorBoeEjYuW+xBxQtXME= 77994515143740690952370766995249847650881300682406161400195705464876513409097078624084133111941171517535435606295232558665316819077765607639545069239931096306624817379462598756505457054433358548941076472902905065316335603665413114267741896000877284610377452471067725794013283338924419969559537339967562669249 -AOH6E2eBzD76QdTJ6QbR/7OeF7AagUif9pEYx7fMqrIsXCJKKpLV/RHIItCDYP2WO4URCaVueoAJe3M/Shj4o6efvH9pf5Q8MLM0rn5MTHWhThivqYQDwjCp1ZsPgq1VFS+gcnmwgHhj2W7XzJxiNPeRXlxI2vL+XTT/wPBYhqEP 158686346608862569574095184731081143351413141116869402750758091813874232272198082464382169470744476593016502816563462778075467588097653320101723165887488327616477297401486647183409348122990505635004320879840358339260797834264972100385692477324858942142372580281421734058008608134075577990829273447077276721423 -ANDDgNXOB/rXwmS4KEjiHj7RCDocVrMv5SU0aw6AJzNTBfseFngqidXx2AJKOEeG7RDDN2gzn4K4qJktF0AIPG2JbELlLUu0MFlpOLxamp586qyp67Cl9OuPq3UZTyQhIsSIE3VQkvxuQkGsaV1owDV3BKIWQbQEqMQI3yT4ELHm 146598844784260148346676185962272439320781765598895126402049215152385925250917998794921584290777625240122575975327405909800121511343265147922400813488099624745229653124857224399973509428158163452130086943873214460600035260925149630502192183407327427517292065083168010281295559088633086659209316582810260124134 -Vprr6oBnWuxIzyTZjuxlKSdZhBc0upeNBHVIlXpQEnN1Q+XURKzp4/6Vg/koITftr3SMSgGpE7LkrERMGFgYaqM5XZ1RXYFKT9dRJnz9VRDITVZtdkDrU04bqo2Ur+jvZhvg/oHBDTgQ4nPLJfHO3+GEmUtck+g/wOVozMMgufY= 60816213163057201559480662231646403262735082707152897397414589876256824040344252799972529759737904461369360580708093117244392116003622336721789703580184437841209963565058475060017600871779929808204093448248984201640754565635410002090180110910120481044515630478472999135146756643143415057403006410330361346550 -do4LGsm0afQLHl9alWF2RVyEKPxLIErsf4pTPgScRE7ZiTSVErbCDeyzd/KHzhBLQs/DhHHcw+OXj541cIRm6jaLVKiT8EwLW/dVG0AkVli83sFh2f56Kk+bCGSKvfGEQcGLY2k7nQ06zoMlYR/xbZCka6Q6kSq4YBDQgigQ1lU= 83252051731120517035090523892596419800592471447735288551342681962005778435125655090199060145942826521644585427683714084736143440310518046334877897672493531918539106001203807757254797471481884534543367685912500572052457610702790097953420236852480969038388056545966568595395722585797418296411673622376893961813 -OL2Qoj4xkqRrQmuuLwrABG3BMMBNGjfBtVBNTdBf7g027Ghkk/z3aK3jKT1EPpdiOdn8zXYBSO1mTRGyK3n7Jo8ICOcnlBOF6cZtDsb9bvSVE26MOD2wzl6irU7vzS+s3vGBkN3AazrxPD4czk3xezA9y13DJVnNzgAgIQHEols= 39844525812817530522650122383059885756573694015271773938493414420875846359054562126060762455794481186614035892021706051863945033061233991184379580556219478200155757966121832613842937722944431875100059046588723473670448006803481527981834627086055642349130254917244469014754132003347635357123155857820000494171 -Ljgn+3Hcg5DOf6usRumk7P+ZrdTBRmo968HdZU1mS7LwLW3Hii2KNkwMV7J77zA0P1pnvhMSEEeh1RbCUjLtSIbt3RIcOEoc+aO0eINF8r99l83xF57CBI3MDA3AAbtaYATy/NUXSC2h4W5kdsQuR88139MFi5y8E5njqxHu3UI= 32456338403763561215581247445990611953939298888251578685087656354454727113846722731945605696397627662593375001096230320486703167389461057538581895745078593206660798580358701927596287363374862536765135996838944212622199018632046955402325290145163082309469649329852148345837780541107029165352782710901375425858 -AMt5/u+ZUNm+Xsucr4RQPUu6ExAOq/Jbcjm/Kb2YIAaEQ1czIL82wsu6YmpHcfMaxLjY+EnaaF+eCWQPeGd1av919+QFbQPeh5DT7ZT9klK7BFyVsN0nEDJQ3AMMJqq6lm4sUeVxDVTmMypYnkzRl7jqzyCRY1MHA+o2LyMECdOg 142886089970163885609957244378225169093559131065687633458877059657380607541767850701139140472705242750285722732461954100519608059127637509286558848391554697942686619832870045594188204522385787253648018847569919409782188708374165437385572046835539379151066214153911415525465041951116179326632238059135825466272 -AMvXeHCaa+zk5VdB27KoS8XpjSUngaw7Gwlq6e2RrkEOxBhU2rGWGJ3fhq1HBNRxDf0quqfYTMd1speisaEr3cIyx9BhYwB6A+Nex/Sf9DSixezhcgEz6c5CfwUYP0QTTOiZDqzz+GcjKikjN7DKJTO0WSXMRG8qX8FBbH0rlc9l 143142496664357119491819741364830737485524654099662921673419335301323845847085335210884201567922636945282124120681371777665458057821603161276185071778040317947168899788341482064834489328957963447735297898161379277478278414388733161844053774747425459239004132791029364174047523473372650441001639174571312926565 -AMxoMXHfE2i4khsAkv/lPtLQhbWUjP3kxYmlJkpacpicBB6z/TmG5zjmTC/sqzBvBn3J4UvMzKYFyk9/l7Wnuc480500a3S4HRVtMtirPueV8v/SPktL67eN2zoj1VZA/Rex0aRGjW2CzEKGwEn3G2bZSgdT8hKv7AypF69ppjz6 143539479941314279463880342636704987025205547180882175105616955926182352311179043850344463145750154442573797875223178075233807385237935671604701513551125937539235111702655902037518920150424691586943553275517626347557879039695678271564616114192941679606063184290901862703975921261779714258077775731727612132602 -ODvOKg7l9RCn5CePG1FfMitkR5l9+7JK67eU+WeA5p1YXCcKS8GbYAKCtXPD2QfxmQcrNYfAc6Yb/kksaq29oW7MzZuTDzK0HXY5xBc/fJzEuvU51gaI0PR3cuU1qRlLqwmIlyt16gto+2E64BgPgIKJcAjx+TfH/EqNeJ77/W4= 39488587053253042573878502921384752550143716864908041972426777545317969264945056510991363961916339225192727727267483337259701961148978214005913510275048195308792987888118270387288989623193626554910652030960235845935461155296845475356011099372367616732243132816329531758943935324760665826550992788664237161838 -AKkznyQtB+PGvbVroM5nUIzhJUjiNj7q4fC9sSFbmDgvehnwPElVlie6PimH2FKonGV4GSaxZ+osil+9omfkb4rO3pq8fy5KcFSw/gs09X/U2eEEcUt/4oSbjs2NaMIxQftM2CauULiwfkWdkMFTBkHnh7Bbyocc8dtmrBDdoI8a 118817437232756222334188081193205110010964766506378146125932730686679941224328135190204402802650523704343176483564284220367074983943319572348376466341132480772885833789613392397284313483009178508647973749522358005819092779831781339778163122774381387989185969990310049504391258988402795259963134610905036263194 -AJfwWA7XnYbTjlJt+9hO/Q/OubHkUkyMYrN6Jd0cN5MG9Rg8W3i8U6oJxT18p4XozkiOgPlF1lE7hIAW9KRKJKGTue+iw0okLq5UNMu2Ha6l5/wzKi0QzRVTBnQm2zjPlQpgUorBBty5mcbt/B/Y3vOE4I3iVXklVtjQ7zIBHaNK 106695084438708194568048926154027115609888551145480521213711726807296356271397749432698558860759334362315257102647885062353922543502466463770991058956633500180245599467233361812610650830611712448187310827443315947425061886163301613989593906515923245020641415290300558869209909418659128196109640872398602216266 -aCXItk5XhuNrbrqJr1Qm04U4y4AzSKDMms11PgVcdf5fCGdizibh6/oZqx5OitM26nRz2vob8F+ZIP0CIyIJU0T1M50dVTbbpwuVNdv/XI6gHekQt0d2g34x1TQJIcsT1VWwGWTPNMtht1hezBAYxwv105AGKnqdLiz04YAdEk0= 73134927546833985031652237686088635686032103401394612286045377544136784429757461671691980910279873140130943470029643791712859175007885735170485461366406852784845528918253441791024065848540598601036357817496637108534035807393364939272891745520961269029038360205258229770737579266643408540634722493263322616397 -APNeoaWlyNa554OtHP8F7GAY5V9F7LMoF2ssg5wBmsgGFktrRH1C4FdyD0COrzIb0Vcko1/HiTnA9JXlfGKc3gTHEnO0gxBSDjK41L+EIgUfR0EhAD9iftTaCoBM7qZN3R1MYrSz3sevQZNMFOOnRrzwWEXnJaPKAZXvsqPzOIF9 170899982929163229592439208307232242235219591108657660041403142612622997092685093132858257827585941687488772925553142105567685213341947938835403410054637382864108739466539574004149772568683507025358331323655651148107044968424043673850583150424463706583215452211942132017052425497789362680979074312857823248765 -ALhwBfBYpOk1pfJcNut0C2fEAd4hhYU03/ZQBqVe/7MgpEDjro7oMvSdba5kjH/VBssmZVqpvuZ5lG+vI9lXLukhwRKJg7m67HG8lZXvjDmjU/PCjxBPNt5r8/DziETYmMa+fhaMTw4hedZcwDe37t1VPIflvM94sBKu6be9yJAn 129516480651398210587505113546142851617282590236388547627336279692965778911450075230961856270046942312918567973875005814982283590898552829322178788678196583244198944578081007477482775130405341039067711963061287597331433268366003672643052056973656674139309732186091974604170508497340243515339072325943686631463 -c9vpoiZvtnj71b8XguD67WayOF57QgOX4V4L++nG2u/RY9VT2+0tJ/C4NIawVa7ScQZAPVLuhV4J50HJX7FZgtY5n+lwMzNo0av7i0IqTS+1BBO8eNJy2wkCbWWBxNybuNnF6OK7eXdPb2Mmwm2OmhN2/j7HAr0cD7rK/Hnif7I= 81358980280155473712258342299472964374474635149963153129588784719499494479288254287754874893180126149146558961101860327826747785201363745989346818037655063262173536227595206355647880155693272153902647256175878517626925488264893732295267833614283963802283320574654949992393798458265266551024756663538388467634 -APArEXNLzDydcHrieLDReJryWxFzcsN1dxjpJIVGeJp6itsJOrUtnmXVnETtaZhWsmN3/Zh0R7TgJ253f7PZ/Z2xCEdqF0hs2MmnERSywdWZQ0a0McbDUUaDjBNYFht1wvS6djbI1b8RfayrnEZ0miYdzrrP1ntU+5cM1QBAvj6T 168651870043094856205824264282870999215855903395882323164614939540734011037112413507417141209480771157672307388419164831992909066097194364645695794831939514470650008210390333649278806163193463937050083854756730458780288720541495880958909249273048328511615821480782977316719631334570687241232556472064072892051 -RhGyx6xibf0OvY1XjnmX5na3G7emG8PWbvEa1kIjR6pK6K1MrMZnxFefXpHWInFS7ADESNI9LHjZB8VW5QrjRVPMksgdEAlkhY7MyQxaclUlShFl2AfKYBfIIro+vg7mUMzMctD+07BLk+jejRHtPVIxHmNnZrZYds80ve5z3Xw= 49204219353786910100605282012781696579642953908541693903348594981245301165936599174304121350092894937817100350990938057159324959104937469442065996667276651025661016077514839755853073999975805394464570132481314896694678249282338429544941873047382467276103868995474424700207571657816852575364781281563515280764 -AKbFfU3GL6NILVyONPVD/X0tffk5HS//7FBp7n6JKMXu3VXvWnfTl32R0WyVHk2yP0iIyi6SUusSicOH9ncO8KJHmaoMGN9Fn+Zq94FTFqZne5NxHmCtwRAbFNDVGg4FeemGXEe1S5Kk1VcvWqnp+QgY0uwa7RtT8C7/T+1pZlwq 117110890075563714812929271250884717870581483065920538069845585667296154465072587148155060755111295509684258790280104272121160614620669593483929827848744548171793187278583947500205314283462739235860439216105116687015890394925743036369717346234391524403038196640934551590543386844279091801685432977718405127210 -AJ0xZ9dfRc6P4W31bMHBymgOq+38ETEIMvMtr+wB5WTcsquZY1IUB4IVkrHaOo3W2SIr479IfJOOQhmvyRS4iB05yDI88Z/fJfXarkH53gDivECuo+5+JmV7e0S6gCvOuVamwoQjlK3G32bCV2946ry4EyIsVZ6Alk9xk7X5HfGU 110384671994603894282707302829898242894456931176497230904862171369974466400767175784681299142670706023468915238955836087425993929524341269289746060546848852729416925808186253355106621584826213979718185296723694190658548757311188764342751280681935289121682174507629679900374674992438818324999211250580434317716 -fjzmb1D+YBU5Wn1GlwhxjiJS07k+fXxjeNRbOv5SjktzxOXmautO8xZ5ACOlYrTt5G2gzW2PU6sYNfByQ0xoUSyutOuQlD2r+8MnDrxCo6RxT3P0dUSX7q0IVj+oLK4GPbscnKLfe6KqUcYLMgKnDYnc+ztFD+csL6BQnM9WMLk= 88647261832601702291191332432291274285041869480562430895152086741320122435409959711452438332192792226899741738806447713240934608106883094466050154088410020909933636902495700779087737304255058561688767369900548260278700135161077055869478387490726087630962098228537973426295306997128615315548440548541717688505 -YDg99aHkQSh9RjytWknbXzcgLD8MrWUEHF46yQLHYANKXaQYyf3yGM9TYPCDUqWbOapqQe+XfOCoACLyRg7vVDsnOPRDI9ZFUgCQBNG06ZOxzktEhnNJoRC99da8jyodFqqk2f9UD1lVa8tsQdatjUDocwgJaDAOpYEyGnUlbXo= 67567767932654827067250684965667741848878457020992905661955722020937161710030993261011062929936964216357930453809610708591260182295097124272956485574313839759737390934220465669626974544253750900911093325004172643146669082793591441922014060981070503803266774197958528843445580649512373693546027107823355522426 -ANdsfO+cNtWsbT/QJHGkYAL2WCHWVPrX6oEz78pO8lUwiigVEow5roLI5Tm7GP7XffjF95z5WDxzpoam+Bfp4za75D6ZEHQmuFnpWQAmNLUHdKUE6UcsWN1rbV1uY+x+Nr5Vni/M7PfQi1yRTTJTYav40tFPb9rY48FsUotivoxd 151275723772668372472508916060743043308364940375633847663054782759325087560768667906829087958412643723335046123025802453213225972572697773468957759328009026531148112732519692142632237595562259864125679649273054426879080697360204352423668940795473103047320116317252295126635024518179060076282921965794883439709 -D2Z8YA0G/vzEVVQ6itLPUC92r9n9FKRpf6lDPWIgpZOOfIkukPp7zzTlo9Ej5IsBrZBbtGz/eYmlHeZ8Y9pQj8HFW24HeKYqjmR0ujbNxI0QgoE+VUwPVg0HhoQsOGmq47zpXpkDwpOAZbMh/t1Bafq6r2zM0qmiwOacJ8KFUas= 10814483230552506566705634583020057064935800294861277580077052473134972003523900930560478187758928889017740705417070994563709463926267126567504805864719383185267204810142444719634360655595490833208838383875687102074846353850310954150927702228780599083427768247170427544730791038729428517279760042619935478187 -XoZpSMHqlOyPYJS7dWSRNDJHCkjbo6+DECzu0FpB9O8bftcxan/06Twbo5d1lEqPlLx3w0XeWtrmCSCaeVcXVtlY3QuPjdKPv8LBnnhslPOVcbGyflaTPXU+ITWE6rwnIF+yWQl3NIwCV4EBtCT+3U//Dt/Ebif9gzfKpKltD6U= 66377743237695515693282032069691369056215169443985727092982918806809030742478033317158686828712146024066618073633406428345129492010236994055590530566431286733776441810601990431112187030942086686719669823512292071202675269428014136307286941704297995292544712278047959299939833088742083527714893795660235870117 -QUbbkyJQ0Nru9c/nPbphM6VxHp5DWlai6407KIDbTGvUReVYI7de1gO/BFphL9GA7gDareYoMuej3/SVp8lEujXywtXzjiI+j2TzR3YYiMBAMhsJO1wU9pxy69Cj5xeFFlrOycjE9sPS9nrqnEEEFNPiK/GDDTHj0KuNbWSCLrI= 45838919357034925862751142472777409057791233610959872523563363744902783251621354580995921495295078179996083468819097423327554678806691589090814275138081407920379810144694354354954459732280968086760894209634364189264517251735804373673532012530665557440070501687207620525228416650281363557992436992284712644274 -F+uI7ARCeAlnPLO1YR7RJj8LyhtE/EJMcY45lsNMff0YeENe8KOITZVxNA55FcxDYpg9sKi1UV3/ASqkqpH8MOxWpBdT2UwSX3oBkp6ETfJKqiag0C4MS8cQVsfcKF39BJ6KUE7X6KUEj11j2YIIRREmLPyZ0LatG7dN7Rmv2iI= 16797235966984072293396362937533957334369977688369659112225970370748312376722010874726300554329794854683394163379447263409228872034356195791733533528404245739693397078461712458035888813157166614479153484688995068722288153129390850561042173295997770817893349738328312152341860704179681230323810266038959856162 -ALkEoXznA7BJlBIfA3Avl9kygQcxexEMApwduVRiXeYG0uEXMQU4rgMJBlPqs+ly8LTIcLFaLnJAG2KFQn2GXz2TNa7w4xkegkrslIJEtBWX/lc7VzRtcLbhaXEs0Ci1ValnW9Up7dYOj3Qw9eNo/9M9b1fD9TI+0QXFtp1ge728 129924120553920201168632484268654219915712271781591182777925696006023100660478316445751842982460082888615429513674356810187315558964251402722465707617058251479494744427428152566665405423424700027316505872162698141109433045594670140335040479559124757490095995568556894332243767736124299898808796118800328801724 -Ki3FNTEE870E9GaNtbT418CLSmf++s6Di3hzAy8NgiDOFo+uuicJa54V3JNRxOBc99sl/chfZuaBQt14BFOQ0i+9rm2KD82okNABd+SNfXOb0Ow2taZX8CpkVJYDyphFPyHbPIKmzwMShNx9X2z9w4++tJgzBzGcFTPv1nhAlxc= 29618953883711174042338818332957726953262658484143534778541769862244883781157097499904047532839425875312731531093860721544220959674634750905085721866390609141599426547378130082409488797303960018348798930232014390380383063108812922828160584483043190739354817699497573863286563890071313017508437166939160221463 -AJq8tcSnAq6M32ViO4hVGiHY7Tb08cLVyxpl/v0Y5adYblvjrbsFcCmsNDi5PnBOBl5awR7KZdQ1xgq6jIs+SQbccEMvJvGUZW5MgcHrXBj9XVd+8oB0z0eahqXpgYBqLDeHLU6238xR3dJYFf+Xrcrzjg8swx66OmQKkAQVJtdq 108660120968150664552423780971948386965268856900017812123107864829782135741514930439461240950044759098603910762272795612101834680870627850178371693837566833495418727543557712057554231215186486008080050486837716071537742708913279026303380104388546316647349432118287628353129105425052237438199445863950767806314 -AI3mfrgcRwtE3mA12gSoQV1xyIGy/YA4pCCvja4mTjvzQOAfiZL0efadxZH5awohCC1SpZDCFsE9yYp4LugHKu/A8zMcp4k5ena8sTPDkSod1yucjybgmVJ5h17Pru28AzHQ/YUmCnojQv55aV2+AUhxzIfojY+NT2PKRqr+vuf+ 99645829268436288676280252226747461064597487404802430565833102291706103139410465131373666856042539909746769688396958963177805479987372681967013633920910376342526433530508868114301205524789149997372160919406352823342811006288909548557622230243808373083272214426118230701324879006645047374853535922112549545982 -TmXQ+D8XFKSclXwnTIH8d+sb1IV0gfm7GagJahaFL6A9rvYaZ0NTizkG5DQ0RmXyo0wPmLork/296whsdNdUxVAwnGFlWWvMV0ftR1fOvN9KoT0WtVZ4Rmu6Fuc7q1PskAZzIp7MkOAxILO4iX5dNuVC+GLZYIbpTel3Ga8fXuU= 55052751096768041533898435453266875315629605001878362193939750978427494147944918632414581744895066623527980497732722163665712245580312596487741856071020477624754815927936394948233480228964159047139170955663289543349257377302556035170334384320502468579367401821986660515827461352578142560630318492817238744805 -EF6KIBWQiQoHOnBdJs1p+WIcAv9ILt0cnQVo+o/2niOtI0C+eFBSiNgeddhotkQFgHvGUjq8BPYgtLC8A5IFKGzXu4SYj5ziagka0hqfhVs9zVHKNx2NUoMhPDG5R7+giwEGGPOayGHVNbsBf1FBYG91+mwy8hnNbhcHSnvLGk4= 11494909948912248031301686864833544028186348338729984264372557659364976118965740281229664413031002362633393381744365783802034700038490736736266032000546393704814403638058993380993275865674190555703046732456017652317200288968188655019374159412919163798248766655991273308390043613040731449231289437754791500366 -AL7wCh8tkFe07qChFAzRkrnNehvda/Teroj65X1Bmcr14+/zeJlZDObYRYBOm8YYSYNgJekcL3o9lLFE34sCMbSJgm4dGwpEVexiLVi+zc8ndnqBDSAnRqtC+3jbInm/v8l6cUvuzrUNtzXIQ/H4FrmPMiVy0EMerkMtkfw5GBsd 134080980697158076909534078193319899756347955848461100874771253577754225619652121295523443912922220564492468474647193062555347746840044705102003079330399499915801536721237211615317000955332058281901995149084303143543150689010335818219129745452688372571010816270728441637278434982752674030696337642893239393053 -APunLhlblRi3bbRBwSV8dsw8h5SvT8ncAmXPnca+e1dLzrQZzL7P2OhFope0mW1MCDl2kJPiGTdK3SiYJVsAFeR3r/0z96g3oq+8uS66T6VaJym0QToMsqQF4/fUMaTo9HsukyPyOgjVIU+6TiFd3SxQKIu1/GpQWVQIP2pkHFKM 176716779397275986910036615967409090183531310366246043951791503601618945774743601662530806467045971394247287367421508126613573039423674729894091424105133906122821596079925540513892022311039293333114333317886304014722168786051080135090242879622144693440448171583324154550086458411590240882982297314605229953676 -MM6B5AgdJKe5OLlPzcXwi9WhqQjx5KsnBYxxa3kWdGNTdk/IN6TVd4Ptn8lWkLm78mw3DXP4Ol1sQbIfkHRoKFUN6TaWg5aDCJBDXyHSTZI2FDc1di0Te1SwziYn0sIOe+R+rfuLuHlcT1xaZBgL6+dDLAZaZza36UEjn5i/pTs= 34273208848307582992498656582721015257885595139328466874135636009184357438445251703533153492315835793684794951576799764181908090765379592683793969576893243386892292517067596035059342970830813419330530731370385186653239446376170533147020072285887964430731437765184844167400169982662183791828762458682426369339 -AJK1dx77ZA4F0sYCgRL1LKSTvjGTKBHd4QBeVnE6FKJxIow82puqtsVZ7TBxbECex+LkLQPrEbuQaVr3giUDjg0aJCE0D9ZVXCUS06qulqcCCdWgGFHXDOQzTWDn6TlJCGxtTEMbMxSlUq1q0iKZ19kwMHiT3GydBn8/G7tIYd23 103022457217861194294329435482792508957642944252832971366936865663608381648431732294396977429863681671686490913575377744795372643599438468695483808375208871881849232129651519218503507811863794426234594709451104684234156597418383183271923307418704786548452806494411689822939919114966188329657999811363991575991 -fPZNsqUYBbVGA2FAiglnByxGJOZkVSpj8Y4QNW5wq6o/1e/PRwp0TLYJXIoCJRs82pAj0QDpQbHl5lCZmNxEIQP8o8xI//HCPxPIdgBJmSfm3VGetrOpqEGU0KJJqK4IsjoVpAfPFMUMOpGNz9CSvCHGk1AKrtYvrTJEKmETuig= 87751387019308584846595931543798879607048239290774788042055795835726250309378365187899578817976976035304304847968410200168743967600896348021636654074952051821111673620467434295067182213181329543946368332581250062140819766061014427755090798550122401239987766844126425179573454145697756278292448630509686471208 -EmT6DUd0bxcdprYhAnycQaxm89kltJOlIOGFFRmEK90H3RhzBGr5PRVTJVqemFVpVliO1gy1nPHgqDGVNIE1GXhrhyFJU6m+HJeNcduippRe38xPCiuraRkXao79X7WAiVYUq6RIH+UIRnfTvHBgzTwjrOvKJ5853hYmGaanjh0= 12917015385266582065020051081997430892582163827812227349569911846746592973268746845211126663077128575098045461893559476227689488349263954564361736197688317585888118974603264677576027836032271531903881104937422976121352854003385726888601980526287956222142458858211589791399646989299770657341412683499692330525 -APtOYyWzdY1A/YU0SGrtjPdMZA5E50Y3hJVXppwuuSk04TjXzcbu2Sqp7sMnKYbToRW4nB5p2UnaLPhTRy0yszOd1auLngW+0ttCybD6nTcVoP65gYOwXGfSEQysqKLb1OfV8kYq5Ba92Efn+CcWWWuS0wEr97W5M/Hccx9bGu0r 176473215292413922394356058789571494026727424839036665031567966488209592078148711908841964690807374236235612412767651029865069639786447019874344449598703213025389428836803984245755885691094364960118900160737925054803955567361126391353868279642836569627177281508980029006921064654964339077608785831304875404587 -Vs6bjpYfFA1R/QTeCfhMuZLZ+Zxo6wxq1jFZpi5SBR1LaUwAtOAj38OJC8L7zmxSOj/RGEmJHkulI3E1MH7P7xlWbY468/azfot5fX9BgHrtptV6Q0dkBUg7H91+tcxdbm4/V0HGQGa2rZp+XK1rO+U/d0ki6iNbsCsCR+OeyvI= 60957991334776853645581868230398759578123373154273044785333939425321390401088800849629483265841435899835570419798325123273632247193463641611211088549152950252041797959644227170492417662363676228611376046334386877555777556575818860902071813120592757466883038430756577949025778080997296219236534786815367760626 -GiauT9A+wmwJsFbS2OPIM6ultIbU+kT2NgACn1jFAy+vNBahdfHMCH0jJdCs5TbmKTCeiEf3ITc5TV1OSvIejJ0GRkTf80nY47TAhiP1aehZvMAv59NQHHTDUE1U4TPVYKIyFpm1V1A+JBHKJzuGrB4lvqB2ed7k4m/ZD5lFLMM= 18363925023885496669420377869542744504974590667921570026763131637088916425434675950812384919000566852243714758512996458727914094904422651029609645299422563453163291342992902510788457007623888307499601267675322986672697397389663297565071582648674012080122614260400848960757021864980761735684874056409664531651 -AL/9KOZLtZu4+ZQYQsmOgbST8F4RV4N/Z+l8qsbCFlHdXHqTTkcN0chsccE/3KkVTZsAnAyJqogbAvB/RZqttaK5a8iKlOEoerUS92FVQw/42WhsVaFggR9cHVuvCD6QqclZjSBQKQzUMy0YWPWlycAZDIv96tooA+V+Fk0jbcFs 134819194171226950171930028888667967094069342154233489571728632904658607624703819928943642011918061760802468868660586005724399808048609316802502143143910585363214684061242274402109137825176291816945489430125510625857564490981683683589784133305376252294774711594646923226452625156299996630452243345104727556460 -AK5x2N/4+PKlsW/fNrw76CnE+nS76Rd7Ugo3IKhMTB/IuCc5xG4MQHo5MlWE0oVkZ+Gs4CxUpvD/WCCjHHFlSxKG4mC6ehz3NVLglBt+f1RWfPkF28JPd0UaIOG3um8kG4J3JDN48PXOPP86A0H8ZYbE5+ImmXsGAcwvScUQRInU 122499245103202714319465533564374494931278163571999934877854825659720649344163774228004853964635693562785966889622928722984134944784141208867445419597834322541679973956606275877526560988151196822256754309120410807075405427166696093800381410682490767468563176131997424692783482903880902119461752084196789357012 -ALZ12i0hqFhwRAikcoahYzH/BUolhgZ9Jz6adLvvTO4wk6LLOpNC/zCz+LjM7HazZomT1SqeYJ2X+WeGFLADHuWo+Gp/I3S0UEneYHKJxoU7OoOtE0mB0BCncLckHao/LmbpnQpS+Lx5bRsr0yE6oWNea6gbyRm/R0to74MI3/KK 128128022342420083856194424802390993133863171077961467523372211039771843125192435716337829530528063182315478279257832480290950255315151577221042903861075751839976362752440630888566422581799720709574650482021111126414843635330535518992034746102956214991673417580508389225948159518319625680855827280146399752842 -APXxvLifWgehdwdTRAJP5KrchRzgbUsyMWKcPGm2ZkwGDJjoTl2LIOOGVFiL4CyPBxahkEHf0nMxBN5oNGX/Y4W4PuOAC8gMgHzdLkPWkpnTcyoe5DD+fQsqNuKVw9nvyB15fx8k0d6b056nfFjnnRqgybby7MSllAWSKRYRdxVm 172707950911363219032118650562553641123743396229371815589867086054370029540557395298194067635069298952836929253340374819975848769009260895874615676938511747311585257140973518651959463416682165208985512233703837931718385346209362040743041262031997793519095342415901373534535662377972036003546589624834285049190 -O+9ohtZ9SzGLJoZM8IRQAjhc/GPt2X5G+M22ZidYjx9WgOTrZDXorSyxLuHxay6djsJSgjxYMj8MuanYSn/DzPWBB1Gn4cDmIsfeYuzO+vUJ4l6d0nIvBg9Iqs61/PGFd46YxhnDiVQ9HEznyTjzESnNqc0+/OkQVJcwNHAcZBg= 42087920806448980363073662127262313840530298932643042322138035915324224188032438119079107631420338701086802583985117830416851550991102672642532160807467909040086448764318690465254898516502941122327185894900817634110254371864896139724173087625913998657136384357741816102965779105122269429701537815263708996632 -VJOZmvqrqsIUTQSSJpZPhbQIYN2tsfBhAciWnfAYpwjK9/ts7OP4Qgdp6T/V2EsSRPnfZ0VKdLg1CnEWDhfcODo+/BZcUrJ0AviFAEtdeUhoMSWXtjel9Ln2guHY4s33z2cN70+e8gfjes65lCzrxUIXEF4nKxzKBnScoooQP5k= 59391682001673484862915842850714742391303140646889359425353339320546979084250010101273851580028171449840778038774656177449549941659895629203970455580974953864068394275066532699748911169800076515776388213090834432354601344176559839798153004796057709798368011673585434643656820656931921831615507416411999846297 -FRyJCOgPziO6RDHX1JgYGZRcSAuoQFIZM4niD/B0twK3l+TRpmVigKZAJnZZFtmX+0JQkDwQn3lcBGQIL6mgy+j0hD58U2/Wd6xebuHSzf4OHVGo1cYoqZLplszA+hVCoDVTHi2YAZ+GtfQEggumcNVxqfEZd6D9Nu//hm0t21M= 14824975573460749317081504809641216868382341402512168178334301409725840669112911061147252565570697788806398498723577368905065980113760265945344671897779830912242224090954834750057278285419880820811348943398148063418809729356397202526234113316098584002071850758705282845646489058224513019380757604894853946195 -dUk5LyS7mduFJlvh5o8R73kJIeeTh0Zli/y3XjtIXfCaNRf+wDlD/pX91JEwsQ5Mvj8yq/Uq13QyWhoNwsPpXVcJtJ+02wtIn5darsBDfzcD/LbWhl7zTRUeMjZ72gAWi1djx94SWjrZJS2oWZU92Og1yOyKRG+ua0AhHfYYh6g= 82361050315899968537319599868832189063658136463903643442673674137187842597528653416212822014359684261704550279153006971937114135373937934986951573613797195556144113400128502946618028800530164890707031379614952207482505803377774320259789692177752930767589642007257364960987343146063216186985472686575891023784 -AI6rejwEznR35rIPuIz0CP2aWyhRUR3unJ90YfxyuVYxrqOJQGSDTSf6SGDDw5MqpZXa9pWuwpyrb6smOq4ZtC3Er7lipJfXDjhy+0k1qcfMjmqbATUscwXGpgW+MO71cttccEz6vhbjndi8gvG5M/vfL2l1jA8nXuBd4e254dbz 100186164434910864539376019601151338080943067893748898987236087770762310617199833479771711726248130012472861788210345311298499515751355424063761182369333224929721733015910055321263016834247318907562652286587380604998130368845939290804442878127169587599285040969551065995197981341260363722618429042861484922611 -AJ5vLZX0fSs8dUSBqd5hki48T9cYuR0atxR+qv7cRu9nD1vP8uNVR8dLitg3XH0RARt3ZmOgi/AuggZt6tTxuIBg+9JhBY9WW+BLL5CnYWHC3AKMi7MQBWciLtmBpyF152bDaEcV1PXxtml2KxX0Ba0C+hGVDmJSdi8Kjd4AkfU6 111256341508463539324514225759801553679558662737345522765042612717818066374840372549356543720386819501973783940451033901079765311790026584654529398345993992144903839534037331533660672892487693477412528974248713261092693018326068480417183236210881306241164169849090833681510163753605662526243408192127670285626 -ZhXtSzn1GiFfHHnSKUYZiTcEWqlI8owyCKFjCQ+VEvkdk50m8uN7RCQ6ZhI545tN7Uy0WdLstJhgJETBYLHHIoWsJn07mgPxuyO0XsqNroICMQEOO/YWQFk1c0VqZifcohQAwJj7fONzM7hTcA22/7gVigJ3iLq178jZOJsEPQs= 71686982768953132894579286530164112027530221141251507987469672039995314435159469907420372652392376452531392493658576814100773556880394271726970628960571077839124343525055625420896355363707908511865700866168843075071778015504724409171911254647909938237551680861008772396291072284353858575645679153885560978699 -Vc8Cw5m5yI+bJ5sUJYm/F2wyZ5x3D4ydyL0uU/3eVF2ZJu55OOlC9pUyyv7WGExClHvWpR9mhMnsqCLyseLfM2Q/YXJ7cjGPKp2xd+fvwHa4hRi1FdOxs96rJnb+HUt9hTwQByXgzpnUfs7AqrqaNf4WSlBNMu0IOOqDdB4iVHU= 60256873326783629723455608618518793848697944184579877638436234491615392142659293975260290798403892159720925893207048153291000664050780029732557737984085196691225472664027706406879051455184548871511448456651238810812870905640934953489289909009741493031472382758586341375517766302753448531830002512912250459253 -QmeUn6cbpE8YrDfMETz/+KVFaK+d4NHHzcdj/MnjcmqQSLpP/XwCW/aeudlN3SfKd6rNo1XZefunZO/ek+PHEIy899WzjiJaajhf2X05fl9WuPEaMES3Yrr+ClogFNQ+9jL8+7L+J8lDuqQzvchT0U0RPay5HSNZw+ZouVCiQ18= 46630904037845609335515965570673490721137364238213103678233212262384415738654627185220187275286458759154841820256007930773120637898228224906635911124921895934056288121005350040349882413280772888907627838315559544636626856478316691755270725623680935763476199888127096014398699432042227882284223578563208692575 -ALUBYIShA4w5kRUa6iNF8S33DqaprdOWjVBnO+j9CCGtUh+NNwfpKR8AKf536MtuFFtwaQvRIlkLpaTYXuRxzyU/YG2+UfRQF3pEmXQhcMxJqFzqZ5nWCIWlJ/KtYS4lcC/B7hD2UGAktnIdjVUTSxX60VzA+zxeunV2iBZXQlEs 127106299687401374061881872616647348819431126560557369258073443762502337592227172639640997680536372567116568811258505773087926491911004324918919511363985868314578663758269650473780772688462266790559846182685481907703974916356209771821075179827563487466641669110315430790405454641953880582274165368514679034156 -ANyAdMnVCVjmUZGiVdyvGE5mUQpKoJOJINqMAfzVUGvvxXFmGdoAx+xsDRNAP4KoijpXk6E3yPBPBZEWyhiHnyjEkktK/gX6gnb745afS0QIlsjhKCk/W/BHXkzC862Llnc1ZGAIsERnGceEoZHdICfDUh/7nMFp5WuSMzPB7nEO 154841617115465511611746667401422322067517612306328612547616471923266281876818466022676728696273611923942543658633762267658490816264271663863494188027433799849037906883352478212451733963905925106470599843045599411842850386623187980045961158399934160107237440980574028985561404965317132715808604373199725949198 -AJ4nfhDe+HojR2YrprDHW9FVUxsZvoIekwlNL2iKFRFcTB9IcEdh6QnGcaRinev7yEYUsL6saSxUj39uWlqo8udJFdszuuQUmnloIi34L5uj0m1OpLy2dawpFQr8pqyA7go4ugMMj6XCtiVnISUcK8wjHgY3Jed/EKK8k5ce0Jxt 111059703393618496515021583605572584329116596402705082562306930876194742195701060137568030171429700588269665205795898835699633817098262654446852249498668467827435829513531633390969638488553144849154126899372953755511962841193763362947708260103832329116485114451074371844037650417731807385491783373627950406765 -AL+heSTflb2MkRYFTKghfzqlVQ1oE5vcx0eCIsy9NJ2NGFXCRRvoGDVoB8UEsUWIRnaA+MIpwDKGpbOS8kRQrvBvPe/xM/t3jrGkaS6pN064+bCBx8Y/Jq31ZXNG8oUol+y1Eo6fkUKNl4EOetmZWK8VmhVwol5YngDffj4Q8ned 134567692290185631768518572983694048149859804864902017394351513816079806629664302312927579302025923096596995134868068794900003728293470554490807959649153000914807604036531509869958441069678002226922395630284261949256022972967357884468325217602330254290548618134453007903724438628204981673400911693835033278365 -AI272d2sbYIi637kHZC+6lievgcDvT5VKaCnus3fHwm2vfao7oYu31P4st9DlqPWJ635X6QtLkU5HgvVSy66MDj2fcOfwVL09ffkZYnoGNdhMADVgOq62Ro5cCpOdw8Ko0cCyVpVIaSysPuqY7kiClf9GTdyZz/uYHDgwWeNrc4R 99528854246023003959943182132914587584844397870416002887630245681136432049666385367430032197518895755482367603560037194955739661569172773017279832774100155646116233705958563163070414171045438199561777058338188494271322834524386565519620661180246416329082614115142485663975718653564590519408413408765689056785 -AN9S8vPzo4SkyKsk07nfyD0um1riJzRqqWF9KCL+kWMHajurgPACikYzu61tL7l1mNEaIU16Ndz541o+y76DgsTLYszu4KXUOEt1Gu3eHy05Fq18zCDlNesSVjkZjPmuJr2ku+p0cP0TLLMn7/KuVOm4GlEVc6OvBNZuEzRriSYZ 156823459768092337875922818543729136404805918580285507923139232733465414368775678369646914249412830351437211620056021568154043505276475345347569200977945836210758870414054407438380975491139001471954448623922841964684437333066353208837709613982022690623722155151315252634380695513434502419141555410441456920089 -AMc5H8kywLgiT4zz5xgoI90jejsHorbqUGtBeX9wke7zyvEKyWxRKScZwzRbinjDZzN48eg/30qTZOV2Rw97JFg+EA63iZ0vqfF8jErIt3hODniKX8zayCuNmiSb5kiZL0UDU1SNh8ER4m6o5vshBKkmqs0PeozfCGQtR3bZXlx4 139899247405256530335276706333424670310599977544642091674186635734421385499036688803073040921114325725234673132788498809189814711681909865484671959982394306416477300458309408833281654917008031099378445580498219376391819745965887864647387211647794422908411100892195529730435423964537342228510107659017578765432 -AKv+3H/TruTX3wdMWnLzD05em8u/QMl6lCHT4VkK+uZwBXoLeji54Tcs/hZIhj0Bdj0URrRt+7JdGSTy4Sr986AtVFxBJZA3lT+JT4JSrq3oY1Tv+tX/yg8ZodQmbpQyyfaFg3BgeHNmsUoCrdqhj4IwBqEXoOBRIXnzaTuqqSEw 120779384043726135670909127168686589868907326577918074234323699599475436892003731971700278391108690400460261929381703781833059801757700386671579819341589048987186473249926590758009001670959004477454905417357202448886738669226760846888369186457452643053236389556969071303251275912453385963613554945645058007344 -ANXIB+HxOyJd3YYsscMpqZpi/eYjZi5q6A0MohU4BiWEJK/E4uIObLJDH5yd4ng+hn7UMhc+R/AxG88hIdOc5NyG/QyFs95ZLUC26F9rkRifu2CBkgqR5EQi2cgwC8jGxQOkC62YND6cAn/ILsKTYaH0iavtO9Tz04vQp9Ypc82H 150122383481070201614242107655752525590609186454390549085509458064289390813495886095936526832230958746095739308601699615024239939948911472291507190108935262129646691795733786714291498653838550751365834947465294261687773081563139416397262227609481906371677917295227469553787085145970923979142676551778927103367 -ZQLFoW+dJ7vrHdMlcLRGKY6T6PZKnE2L3NjXymS/55my2CDBLdDf3oXwLlRjVt9KnEiXyQzLhyY2PrFA4k3N/3P5lVDLHero5c36TMshbHgbIKRGN2CGWPEFeQ4j040IwVbQCPJeuF3jL5ikCxWZFXfeEnTL6TqumLfD9yLQfKA= 70932215714423143395949105745758445705072524008235214324766464113352968998429901322485575506330607802260244612268338586532462314021433435523464635419846126736185176246740838082062856583684393425704173881940108783636582561707441482446854068022535943408999200681879161519209676205165680598258447492092651404448 -LzzvPw0FdtM2G/RRiqoajJiIH+Lw3jpL4H+08yOpp1bNITR2Aq0beu2nP0H4o2Z1/FNr2hzuGakkAhVbmmRXc8keoOkeaAQAP/8OYxHpjrqou3WPWaKx+vUCTSqVYYf8gnVKpAAC2cD+3lW+/ZJ538o+c0ovbUKNu1u1j1OBtA0= 33171669664542509840621265032202455391098253465550501094201777336478104142847268103467889435377685359857979277521589539506627375165485879405453566052091202280471235979376217319335800766353336252760793484157724210008639813552207624049019149744883918494762511376489708611103181576211531366514802868659603747853 -APrGj1lIIlxA57DNh+bTEAFbJK2Y2P3MxLShb4fPx2aY6j88k3umoe07ISQLf9PzNPeml4/0I3w0KNd2x4s9KHbj7NsIT64lhO6eQSEteqZXZGXUYUyNzhrTbAjt+Q9LVKItQhsTkTW2HTQ5RQZfGrkL118b/I18J4P+T8CGZdDz 176100632478477421621142147788721746818712752858710594712903769452749028606541677227413333567013253138397373757811889654342173021761934591400685421771460440213093509170325205622261487145789848227404883040799927313402244625239515162996390018403365063394514244196976794479529075569412676472840544017222373593331 -Fvcl/LemWk29I5LCjU1QedTjGlkvFF/kZXNkRJv+vNZ7qgq6pX8WB9yVkk6AoclDYAhCRfKTKuEpR23iafVuHpprPfNXcqBH8n01kq3U27xqIy2hS+D6BRBK67PQaekq31EB0aOcEb/DuNaXakS9+mtTMx6BKt+WoEY+NkzHK6c= 16126868736093163702771491576570380743773057522016869811780571865928979861357811080042796140032050364543242385458140594532945509386155523162799601656485075247603490060565663264947465987286983338572455184901756399862440455644131755848583379822279676555143231305246033911608913609591095831135803702269767527335 -AKW8tvaB8YZ7J5W2lmquBniJzUhRfqFdPZPqvBoMzR4cRh1CMNdSFsYsnsaF3KolNzogdsxFpHAaEMG6zSvpNJAoi4nixCqb5SETXrSLASXvNjI9MvCoE2JCRq7kMbjPL7cem+mBPWZITGUI6KVlJPLxQngHYSFxukqlx7jznwJH 116384596458828069344020651216200368975621068920641012055593076864629080375946542748377736186556382088448816531408136815533164209947323588157210859294774679831647934533061547276394884474877353537242203645373945111105805934070657589374883764420038511061919092743520704686962593876316976299391579463759429567047 -D5N2P4FrqDf7/2Z2BJsqah4SjUtolic/yNqdNzvNEogDKZKAJyGq4zhnHvkYXkEm2ueU/FDPJRqisszG0oULdU6c7p8acirEwsGLVh4RamnFRgmQSK1vbiYB3bR+P+iFX/bZ+TWjN2Y3YMa5UB//I6Zb5kEIjmTpjY2LEPI1e6s= 10937855369372570149476727082965401421189236366492771695094788039313362971972373068736123833330006002198346944149230147444718818161877123407713821100752433128205189334393732633989950841577315682292180735057952587083688644195300641998709155269462601925653013312848413290208844194513502358901613104779186502571 -V/A1ktS0xrcwlI8xrYqvlLCFYrdVp8tEzZaZ9iNNpPH/pzVsA0WbnnUeHbdilkje+4OdoX9C4U2xaOuWOfvqLR0c7GeCkSffCqyf4ZsBmjy/BQL6rCpxMF0gIHXO5O8aJ1h17hy9LTuNzWm4zVh4pNFuHC9L6nAcf92udMiIQzk= 61752386563628388546439207444896778638632243226541303179646524864765343154194512297447627825411023405896612559648434895675553567405277169056807223959390559391191382555701580549902639604424290133917402316755076644943742815711432111554988540913643347167948778404861099845961151998728662878854088239266688156473 -APoPgEKA0/r1FYmt/Iso6ChYK6dDU62Y+vH5h/LVE00biBYG1f7aL3GdllUTN+XQSHpqlDw8CD+9xojwZIMfgpgjOwLbbe7Aso460zLrg3R8aHBpbVt8iZUgjACwPYr5UyKbFzIAWaXcnYYQ+tCO9aDIuOz+/7eIF62C81zXFJVZ 175598490446477604563905754135475294999639698464908622773037381109011373179895295130424828038708319325919451724985361900259676699137657615076219968061941008972496322083528922054390781811699677037439989404270415929836486610353098273115864435328533577114470407444852521009919911888840405368858409835197558461785 -cL54ymLJhRx3U20Y9aUTIsXy9Ags+XHy4qk3F7uJyO46eiXSL7VrrR9vTQXAbETbu1YiVWfslsPht810eUDUVaVir6yLnXkywn46Ci42FEvVoTEFjO22uYcCh8nqB8H589w/+lVSlNrcILugwfdfCvK1iZzVimOO6l3qzfXToOU= 79171550718114578361958369278761819285111811576818442980166457146638966315793211967882077899426611721874954146020093740153495693185472340728106727284441726113022873005252623222594060645383105757498856463065370975867121188445567981809371870213273555432308279508351518168027875538720367440153667708369625129189 -QdQN4qW2QZq8/fmSaqlRiPSoDbhmF0oYjaY29HcKYGHdlOH0AMJb+RUIq1aszvVtjh7AYay2TNhaZMWQ6Qi3c42SNk3A1MVknT6zqiRCGjNFfxf/matbRLbTFQF832MAId708vrFLF/o2HpekMkc5hcHB6bkUUhEI1NLcMXwGck= 46226230186280253581676626651942823886592433541360244612432763620730826574920825070086312767146345247802570752482654580909236388357139147786783758670999083804670979821212991224400629053427330483809790366665043598754931511997925850227997764381723288657884346974360232490075739442406431704368767588177525348809 -cxHvCK/dyVDvaqCCQyLeaiBGA36mV5el+1lc2eUTkHGUzX5gU0QQCEp+iSXNJhIOON8VFpKOFsziuV0Z+3cegWRw/VnxnjXcBh6IDKdupzOPB+Yl8MA1ti/GrQjLC6ikcNYNjQT0ZThL7KTqEvvZJH68WYmD0IuK26swjNGIGaI= 80804939616399473443737611589382762718815989847332356984276911837267997590368701684135326680567847542004499684038240485603420973682522792156533112356849436451918522884749244246467852622918805139990256619014116276456718693703261686778030658826952213058982142604346352178078750879100976710761147710018148637090 -AIQ3OIZevkYoRGBmsFaXJobSfLeInuKKReVYNjP5VEPoMq0mXTltY6l09/rQ3d1JjsMD1PfA7emhxex+H9t3leBIfCi6Ux34GQEjXWpQc4awuiy9tbR077HaJyecvb8Qy1FTnOHoH5C043QJzrKYT/sFXjgB60piI8Y0R/hwxO4r 92845026347218330987427785323244729176754623818531419911990153715676845614711324345879159989637824921793015074978358052562420379797956750450245721653716740651389924718711940869162230097839047895842495414221110468446944827052871968998907462191349838598297775847512250220907563815783358238473966349820476321323 -LoG6ib5lUh57rdmSkZSWzBoudytFohS4uoU/uly6OaQDOi34GeNVxu/yr6RszJyL9JWkGNgFaBIv/HirH5zA9VQAL/6kpL93a0/GQ/nuHkHy3GWZPF/2+yJ0PfazQ40fWhHZfRxBngWslbguFPjj1XaJ37YzpQAYb/+QcUai9ic= 32658152290878644668906121702816147999633088014476055330179597550087921141413344679134407016170035735846077181424615228657687216737432274043674411132745299610950657139041836412322040866250189120286839287690983293111362228893996267791120043532014262644480689231457941173330523718758287779526551822788227954215 -AKu2jgOQCCfYZ3CLkXEH44aO4TtwMPeK/eq4FtNj9HZ9FxT0LLNJh0ZXPOaPJjgznvIw5C7/hNm7rUs1JeV8I8dj3nbS3EVERQz1gc/ckYB3H1bViWREOD5+TScDusi86YO/z4ar3dauKkg5kT1kKDuU/OP5kNMWvtJjHc4Vd3L3 120581042599355202025471829872601846477331097842315143148145881424071317426176264583672725691485724160094190478865850305422057632110749683552966861219554215519032344086824849470294473808177223497912069335635933312949412445851201918768630656712413082629164792850095444166888072453190903931430551124946191872759 -ANLs7OsR7oBM5jSjVADrk+Mx9d0TeieTIkxwWiJ5STKNQmW2EzPOjgbfcLhbYEhzzDFJveXO2dzz6/c8V5oW2yqg7VMx88DzEbpQnQpk/rOQRw9jbI4fxXNJHkNZCeysEVvFfLJb4ecsGA0xJ3Aylny/jP10ahPv2z5K99edGZSU 148116916208650944522110872759145096907599612943009577897396622287067669897712748449324334650112672914917664881091633448764667172850435775162090891556266912697811031318228334453406561952979778127173704706529448647577013482442758465809198730066784986763500579667100246958959793527011919373534159474250508506260 -AL+Er3n1qj+SBsZVtOMJYg4m0CN+DE6gRnC1F7nPvd2XnBe+QE0+LKfcpUDHVNxoydW4BDzNVwnUNbyjXZ+iuddPtO9hchVEI36UiuL0ydeldFpOZ9mtHJaAF6abd0MlHw4vXRf8CbOvXb5N4s76ggijlZBjRtU563sSmBcyq6Zt 134488725667189507159811764480908602790838430340670328479145818969651133017546803581865897303917708192047926432630297993507146075655594931523561067937580218599890162311074002344315818494246433967228889645359283635389151927472221799543158424012020308449895562192866672439712148770104592027035768027605661099629 -AK/04XOBSjjPpuFXTDF82RNWnKqZz9mJQbS2B5bn0ehFnBa6j+B+MazX+AxXTL/d5+hPLT1uexcnSMl3DcGGwKipOXg7Dtuj3pfJXHTrCqXAUYrIXI+8vKVQO55yQPGfzIg9SVgetwW1sDk+a28ZhJ5a9OddqNoi5C+dLce7ZtNb 123560902006294001923570614486104726169564351074482936927091682096999779538353161007361361829586988452098646362280351148131540524964916445100589671458589346440250329883789099771417949746709217272531950438336245613419967556433467843237384555807236658182067742367748737224684334525934210197178231424396818830171 -PzOEGHlihiveoWFAALY+LOfkRJfm0NUF/uR6cSU/tbpGAq4onNpr+iZIzEP5o3JBLOtDC595/NBPI0fzaXl0vQvgJs6KG8iKANjsLKQjIpZBkoKhdbG9MzTVQuAeuDW0w3sn2iMZ/v2dgAzRwfqmQYXJr3I2BbcwWraIJuZXw5A= 44381416070253681813077725822442106641846565789204187691647505370231831464947935035197059366680327425453811558282831465960889061956588244308214943856009686127871667376028831540813257349779756631357122923723235595360268572998278795110672666089470210929411514949652537714634611421849780859192966935514197771152 -APnuduN01GS9dO2m2uCLs400AR2lX7elOnIPC5U6e17qbukxWYzNhilZlM4kdGXAIeYpzFdSIW/gxRMZe6TXq9krFWRaaPyT2QwRfGHYnazS9F1QNYmW1zXdt+qVp0JGxmh5PyDstbP8Z3x50/E8Mb0gLLPhNAvzY2Jnr9A8Q1Hy 175507868985304663005133968393406051624825489142498103948374797086106732382869120248515993626061853699363294022457032257026588816021007648668265488426495800459085474654859258116280251546902009156490112550154951965894022789029787886785376415437170872937201839249103828294508088966180386198213606090453461193202 -QHEhL4iVzNdUsfG0izTEepwTOvxka8t/9MwuF1Ey6kxsI+ry4g4sJPgR2xMnbtOmvQn2NitAkfvA8JPCiL7a8+gmf+DVRDjKDfpfrtgAVmo+3rH+uJYTrKhAp8R7ggU2xIrvbIrgeUj7ieThPI3Rtap+IdkPCL853JC/+oKtytM= 45252649968839515171157821292772647085425694172492111870169593872127007254353374581972876464918186509502070064028725519394859148593053614163356612260257013360168930649423732336969778875205250872728821432415158634190866775855521719727700464116412886964736859295086745723651735554245035077902615220578218265299 -APeaekK4mVhEShCfM0mkRebcg1Iq5CgrFIEGOoh1nHzgebr5A9Wrhm9yD1Vd3e+fFD9urDRB4y5MHPJHX1U2NFToC+H8nQkFXL8bfd/9Wl2c7y8m0Mxwi53pLIdzETLbbfeOOtJvuSYYT3n8+/PeMnJ46UD8OfqtnFuS0/bVpFLS 173873040145444066957050580959132871919216036714423404143335635770937773583761934638398867981658394368476005882852706046614562314432695052874974848076542261910660410561876043187368112065303981001507235893831108658530338308496461162623683138693880482650786841100027392293758260448606244283355655751440485602002 -FqC/wgZDPTUoObPFSH5w4QR79zj/O+ZiHGTEnsBMwNZD3Gl/ClRDIsFMDDupNLgwgXsqCQbpwSOHOtAvUuAFwRpzt5B7lwIgtP5ism/AZRno5p+9WVSmUAM3glHsNtvYydz2MkXtnXzSMIR1ZVoLrdwMnckE4pbMzggqz+JZqxw= 15889870005716350976759704672045310928616256175405784574141006779373730686049218680335525720670897894546334915362899913262232170795516176419192840427996647372619000239408311568577050460995518058850793096827271653902583271225799114408537346367483775593212272587811309978019791973449354003275559762102731778844 -AJXNbv2AMWadF5h99ZAUy5gLnVK/hMaakFo0ZedtPNRJobxPmwj+h52G+Czd0U48G0V0wpdeUJC9v/4BhjzhCvNhNsdAT1+vQXDuteYQ1aspsEKLQ6b+NknO88QSbRJw53+KeOY2xe7PKOa4V89XnFFBF7wljRnIYrM8vvcqVQDk 105194875227030598769888785590198577650278341586165110611689226597424766274486797264032300493674927704016605741286512271390703088626381669060095573361828932336327125438452066548897528158329044309005232090053420259033538936293519762277428283316506398965916381374819450858053512398634116052299066189424983605476 -AIDRnUpBHepjBqYAlU4MG/8JxzX1mPxVNHpWvnEVgvqTQx/bisFPpXrYs3jAKIR/lzevYwhH0K/8Vvw4NK9iTMFqgSnU44AZztKsoxUXsEsl1UU56UscY5C7ciKU6vjjWI7nm/uHNOXdE82TQXkk2WX8ferNqZU5DaLFCb+zxb7w 90459642084794142567976043425270153270545560059973413835786695756473295513758287577749768786155290305189883600338986370836806413936196854410098516254596146039255388020628703824195128439558127783534033672712705194483515442668075394018677699876614329419492391568463215822656901183478205197671375262145069825776 -AIdvVNzJqWPgAShvi3GhbhMQft+SLigKGrhoqas2Saz/bA9u9Td6fAxa2LjrAqshW6cnm2aalc3Yv6RW/Y8vg7Ho31NSaRjT4zMUenykcC0/Y88UNxREi85wdnHwGytms6Lq49H8/7EFGJIyL1PLRWPmZn6XFkegscI/HUq/hiKm 95105613103051650721863964216778532448106311156426028879315612217763044797186635476805213120469258258125661666950525364331551671653846368977016286153840829836509696804585927581668281228810410814602664419962214359687545209312836366693384158782798559255789953908588601637765910472073600954502095647132310971046 -DdchOPjXrI6lpV84IdKCisPmdqZan8AARXRLADEhixsfXCYuO+WhNatI/fM1vgv+/TxwwIQjIfG1vOZcB36JUfjHYdItYQ70vUXaVFdpqvoBGyfOTU50Ds/11iGPCF8mWiQwR30/XAXytqDZtaVJVWsgHD3RigBSnSHhnvZAWYg= 9719024770319024562623340689338530708271347986326272393419504304391837979619189392867902307307106771234732135400958362219711925045600118964223238147375808749507928768896918369395426933218443166133187066167663170936604731896932630589251946733237697936733924510107175304126061649311812536190882160340308613512 -I+Z6rdTOt26/v3dtUP1plITb15fjb6aMDvqFS3AD1+nxBqnnk7ISGE9j6dv762EIWQpMzcCG5NCCq35KOHEwRXP28zup6olOMt3CBFgYVcBE2pWOpGiO19G/iFweYZXZPY5HgIkex7HBbb7l6HhomPc2sLL/IRhh2oogyHx2JMM= 25210054612455888156900839678249806510561198051210010474517915819801056434402727631042894881559517808906460418029149538469607239850657781476308872923928122553395468026744382526167194202058040459679991391557937527079948356545086684521068912222036707113005006607012596093923970784177288565193670152033981048003 -ALbBoyelCs4UkfnPjMT3S67ujhBHBEE0uxLx6kSGZq2IOMU/QdWYPFElRgYC/y++334FSEycjS6NAJJo2ITpZCO5AjNJ93J3WYgbDLiwu1VzKHX6ItfFNEk45km+QTi07+pDKcKNd1k0mxqpLd/PuZd5hRpPDDoKBb6i+mrCb2yF 128335905497646745013379107761994003743181143126608677203818152878840562628631384684712779135591095534911406031545494164782375276574093777950840330452805743803067864740000758175436633463846967335728314347497013853264454015790847388463800323796888198433722196292529074568758149650782323407298620158495364705413 -ANwlxEkeqmqYTxw1ZwMi1v2wo4ntPaEYZYoTLTJQfa+kuIksnHW9va243HAiOixd+rviVdm1dEwzESBbX0wiJNtRBpP+bnRxy4xOBjNoOB0c/tfka5JVwu5eeskyHx4V3inLviUaj86Yck42n5NaJFMfBvhzVftZ/YF9WBITI8g6 154592850289860621115358362871905683265658659789986179554827712019629689749439795961607030363152337159590319622241556795951071651584979664762468782303706550885785493534656062553770262954861884613383561063525714923031691298088562054236178003658891902606245782350998076658704876516153027797371814038658244397114 diff --git a/askbot/deps/openid/test/oidutil.py b/askbot/deps/openid/test/oidutil.py deleted file mode 100644 index ec84b1b6..00000000 --- a/askbot/deps/openid/test/oidutil.py +++ /dev/null @@ -1,176 +0,0 @@ -import unittest -import codecs -import string -import random -from askbot.deps.openid import oidutil - -def test_base64(): - allowed_s = string.ascii_letters + string.digits + '+/=' - allowed_d = {} - for c in allowed_s: - allowed_d[c] = None - isAllowed = allowed_d.has_key - - def checkEncoded(s): - for c in s: - assert isAllowed(c), s - - cases = [ - '', - 'x', - '\x00', - '\x01', - '\x00' * 100, - ''.join(map(chr, range(256))), - ] - - for s in cases: - b64 = oidutil.toBase64(s) - checkEncoded(b64) - s_prime = oidutil.fromBase64(b64) - assert s_prime == s, (s, b64, s_prime) - - # Randomized test - for _ in xrange(50): - n = random.randrange(2048) - s = ''.join(map(chr, map(lambda _: random.randrange(256), range(n)))) - b64 = oidutil.toBase64(s) - checkEncoded(b64) - s_prime = oidutil.fromBase64(b64) - assert s_prime == s, (s, b64, s_prime) - -class AppendArgsTest(unittest.TestCase): - def __init__(self, desc, args, expected): - unittest.TestCase.__init__(self) - self.desc = desc - self.args = args - self.expected = expected - - def runTest(self): - result = oidutil.appendArgs(*self.args) - self.assertEqual(self.expected, result, self.args) - - def shortDescription(self): - return self.desc - - - -class TestSymbol(unittest.TestCase): - def testCopyHash(self): - import copy - s = oidutil.Symbol("Foo") - d = {s: 1} - d_prime = copy.deepcopy(d) - self.failUnless(s in d_prime, "%r isn't in %r" % (s, d_prime)) - - t = oidutil.Symbol("Bar") - self.failIfEqual(hash(s), hash(t)) - - -def buildAppendTests(): - simple = 'http://www.example.com/' - cases = [ - ('empty list', - (simple, []), - simple), - - ('empty dict', - (simple, {}), - simple), - - ('one list', - (simple, [('a', 'b')]), - simple + '?a=b'), - - ('one dict', - (simple, {'a':'b'}), - simple + '?a=b'), - - ('two list (same)', - (simple, [('a', 'b'), ('a', 'c')]), - simple + '?a=b&a=c'), - - ('two list', - (simple, [('a', 'b'), ('b', 'c')]), - simple + '?a=b&b=c'), - - ('two list (order)', - (simple, [('b', 'c'), ('a', 'b')]), - simple + '?b=c&a=b'), - - ('two dict (order)', - (simple, {'b':'c', 'a':'b'}), - simple + '?a=b&b=c'), - - ('escape', - (simple, [('=', '=')]), - simple + '?%3D=%3D'), - - ('escape (URL)', - (simple, [('this_url', simple)]), - simple + '?this_url=http%3A%2F%2Fwww.example.com%2F'), - - ('use dots', - (simple, [('openid.stuff', 'bother')]), - simple + '?openid.stuff=bother'), - - ('args exist (empty)', - (simple + '?stuff=bother', []), - simple + '?stuff=bother'), - - ('args exist', - (simple + '?stuff=bother', [('ack', 'ack')]), - simple + '?stuff=bother&ack=ack'), - - ('args exist', - (simple + '?stuff=bother', [('ack', 'ack')]), - simple + '?stuff=bother&ack=ack'), - - ('args exist (dict)', - (simple + '?stuff=bother', {'ack': 'ack'}), - simple + '?stuff=bother&ack=ack'), - - ('args exist (dict 2)', - (simple + '?stuff=bother', {'ack': 'ack', 'zebra':'lion'}), - simple + '?stuff=bother&ack=ack&zebra=lion'), - - ('three args (dict)', - (simple, {'stuff': 'bother', 'ack': 'ack', 'zebra':'lion'}), - simple + '?ack=ack&stuff=bother&zebra=lion'), - - ('three args (list)', - (simple, [('stuff', 'bother'), ('ack', 'ack'), ('zebra', 'lion')]), - simple + '?stuff=bother&ack=ack&zebra=lion'), - ] - - tests = [] - - for name, args, expected in cases: - test = AppendArgsTest(name, args, expected) - tests.append(test) - - return unittest.TestSuite(tests) - -def pyUnitTests(): - some = buildAppendTests() - some.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestSymbol)) - return some - -def test_appendArgs(): - suite = buildAppendTests() - suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestSymbol)) - runner = unittest.TextTestRunner() - result = runner.run(suite) - assert result.wasSuccessful() - -# XXX: there are more functions that could benefit from being better -# specified and tested in oidutil.py These include, but are not -# limited to appendArgs - -def test(skipPyUnit=True): - test_base64() - if not skipPyUnit: - test_appendArgs() - -if __name__ == '__main__': - test(skipPyUnit=False) diff --git a/askbot/deps/openid/test/storetest.py b/askbot/deps/openid/test/storetest.py deleted file mode 100644 index 0aa834e0..00000000 --- a/askbot/deps/openid/test/storetest.py +++ /dev/null @@ -1,397 +0,0 @@ -from askbot.deps.openid.association import Association -from askbot.deps.openid.cryptutil import randomString -from askbot.deps.openid.store.nonce import mkNonce, split - -import unittest -import string -import time -import socket -import random -import os - -db_host = 'dbtest' - -allowed_handle = [] -for c in string.printable: - if c not in string.whitespace: - allowed_handle.append(c) -allowed_handle = ''.join(allowed_handle) - -def generateHandle(n): - return randomString(n, allowed_handle) - -generateSecret = randomString - -def getTmpDbName(): - hostname = socket.gethostname() - hostname = hostname.replace('.', '_') - hostname = hostname.replace('-', '_') - return "%s_%d_%s_openid_test" % \ - (hostname, os.getpid(), \ - random.randrange(1, int(time.time()))) - -def testStore(store): - """Make sure a given store has a minimum of API compliance. Call - this function with an empty store. - - Raises AssertionError if the store does not work as expected. - - OpenIDStore -> NoneType - """ - ### Association functions - now = int(time.time()) - - server_url = 'http://www.myopenid.com/openid' - def genAssoc(issued, lifetime=600): - sec = generateSecret(20) - hdl = generateHandle(128) - return Association(hdl, sec, now + issued, lifetime, 'HMAC-SHA1') - - def checkRetrieve(url, handle=None, expected=None): - retrieved_assoc = store.getAssociation(url, handle) - assert retrieved_assoc == expected, (retrieved_assoc, expected) - if expected is not None: - if retrieved_assoc is expected: - print ('Unexpected: retrieved a reference to the expected ' - 'value instead of a new object') - assert retrieved_assoc.handle == expected.handle - assert retrieved_assoc.secret == expected.secret - - def checkRemove(url, handle, expected): - present = store.removeAssociation(url, handle) - assert bool(expected) == bool(present) - - assoc = genAssoc(issued=0) - - # Make sure that a missing association returns no result - checkRetrieve(server_url) - - # Check that after storage, getting returns the same result - store.storeAssociation(server_url, assoc) - checkRetrieve(server_url, None, assoc) - - # more than once - checkRetrieve(server_url, None, assoc) - - # Storing more than once has no ill effect - store.storeAssociation(server_url, assoc) - checkRetrieve(server_url, None, assoc) - - # Removing an association that does not exist returns not present - checkRemove(server_url, assoc.handle + 'x', False) - - # Removing an association that does not exist returns not present - checkRemove(server_url + 'x', assoc.handle, False) - - # Removing an association that is present returns present - checkRemove(server_url, assoc.handle, True) - - # but not present on subsequent calls - checkRemove(server_url, assoc.handle, False) - - # Put assoc back in the store - store.storeAssociation(server_url, assoc) - - # More recent and expires after assoc - assoc2 = genAssoc(issued=1) - store.storeAssociation(server_url, assoc2) - - # After storing an association with a different handle, but the - # same server_url, the handle with the later issue date is returned. - checkRetrieve(server_url, None, assoc2) - - # We can still retrieve the older association - checkRetrieve(server_url, assoc.handle, assoc) - - # Plus we can retrieve the association with the later issue date - # explicitly - checkRetrieve(server_url, assoc2.handle, assoc2) - - # More recent, and expires earlier than assoc2 or assoc. Make sure - # that we're picking the one with the latest issued date and not - # taking into account the expiration. - assoc3 = genAssoc(issued=2, lifetime=100) - store.storeAssociation(server_url, assoc3) - - checkRetrieve(server_url, None, assoc3) - checkRetrieve(server_url, assoc.handle, assoc) - checkRetrieve(server_url, assoc2.handle, assoc2) - checkRetrieve(server_url, assoc3.handle, assoc3) - - checkRemove(server_url, assoc2.handle, True) - - checkRetrieve(server_url, None, assoc3) - checkRetrieve(server_url, assoc.handle, assoc) - checkRetrieve(server_url, assoc2.handle, None) - checkRetrieve(server_url, assoc3.handle, assoc3) - - checkRemove(server_url, assoc2.handle, False) - checkRemove(server_url, assoc3.handle, True) - - checkRetrieve(server_url, None, assoc) - checkRetrieve(server_url, assoc.handle, assoc) - checkRetrieve(server_url, assoc2.handle, None) - checkRetrieve(server_url, assoc3.handle, None) - - checkRemove(server_url, assoc2.handle, False) - checkRemove(server_url, assoc.handle, True) - checkRemove(server_url, assoc3.handle, False) - - checkRetrieve(server_url, None, None) - checkRetrieve(server_url, assoc.handle, None) - checkRetrieve(server_url, assoc2.handle, None) - checkRetrieve(server_url, assoc3.handle, None) - - checkRemove(server_url, assoc2.handle, False) - checkRemove(server_url, assoc.handle, False) - checkRemove(server_url, assoc3.handle, False) - - ### test expired associations - # assoc 1: server 1, valid - # assoc 2: server 1, expired - # assoc 3: server 2, expired - # assoc 4: server 3, valid - assocValid1 = genAssoc(issued=-3600,lifetime=7200) - assocValid2 = genAssoc(issued=-5) - assocExpired1 = genAssoc(issued=-7200,lifetime=3600) - assocExpired2 = genAssoc(issued=-7200,lifetime=3600) - - store.cleanupAssociations() - store.storeAssociation(server_url + '1', assocValid1) - store.storeAssociation(server_url + '1', assocExpired1) - store.storeAssociation(server_url + '2', assocExpired2) - store.storeAssociation(server_url + '3', assocValid2) - - cleaned = store.cleanupAssociations() - assert cleaned == 2, cleaned - - ### Nonce functions - - def checkUseNonce(nonce, expected, server_url, msg=''): - stamp, salt = split(nonce) - actual = store.useNonce(server_url, stamp, salt) - assert bool(actual) == bool(expected), "%r != %r: %s" % (actual, expected, - msg) - - for url in [server_url, '']: - # Random nonce (not in store) - nonce1 = mkNonce() - - # A nonce is allowed by default - checkUseNonce(nonce1, True, url) - - # Storing once causes useNonce to return True the first, and only - # the first, time it is called after the store. - checkUseNonce(nonce1, False, url) - checkUseNonce(nonce1, False, url) - - # Nonces from when the universe was an hour old should not pass these days. - old_nonce = mkNonce(3600) - checkUseNonce(old_nonce, False, url, "Old nonce (%r) passed." % (old_nonce,)) - - - old_nonce1 = mkNonce(now - 20000) - old_nonce2 = mkNonce(now - 10000) - recent_nonce = mkNonce(now - 600) - - from askbot.deps.openid.store import nonce as nonceModule - orig_skew = nonceModule.SKEW - try: - nonceModule.SKEW = 0 - store.cleanupNonces() - # Set SKEW high so stores will keep our nonces. - nonceModule.SKEW = 100000 - assert store.useNonce(server_url, *split(old_nonce1)) - assert store.useNonce(server_url, *split(old_nonce2)) - assert store.useNonce(server_url, *split(recent_nonce)) - - nonceModule.SKEW = 3600 - cleaned = store.cleanupNonces() - assert cleaned == 2, "Cleaned %r nonces." % (cleaned,) - - nonceModule.SKEW = 100000 - # A roundabout method of checking that the old nonces were cleaned is - # to see if we're allowed to add them again. - assert store.useNonce(server_url, *split(old_nonce1)) - assert store.useNonce(server_url, *split(old_nonce2)) - # The recent nonce wasn't cleaned, so it should still fail. - assert not store.useNonce(server_url, *split(recent_nonce)) - finally: - nonceModule.SKEW = orig_skew - - -def test_filestore(): - from askbot.deps.openid.store import filestore - import tempfile - import shutil - try: - temp_dir = tempfile.mkdtemp() - except AttributeError: - import os - temp_dir = os.tmpnam() - os.mkdir(temp_dir) - - store = filestore.FileOpenIDStore(temp_dir) - try: - testStore(store) - store.cleanup() - except: - raise - else: - shutil.rmtree(temp_dir) - -def test_sqlite(): - from askbot.deps.openid.store import sqlstore - try: - from pysqlite2 import dbapi2 as sqlite - except ImportError: - pass - else: - conn = sqlite.connect(':memory:') - store = sqlstore.SQLiteStore(conn) - store.createTables() - testStore(store) - -def test_mysql(): - from askbot.deps.openid.store import sqlstore - try: - import MySQLdb - except ImportError: - pass - else: - db_user = 'openid_test' - db_passwd = '' - db_name = getTmpDbName() - - from MySQLdb.constants import ER - - # Change this connect line to use the right user and password - try: - conn = MySQLdb.connect(user=db_user, passwd=db_passwd, host = db_host) - except MySQLdb.OperationalError, why: - if why[0] == 2005: - print ('Skipping MySQL store test (cannot connect ' - 'to test server on host %r)' % (db_host,)) - return - else: - raise - - conn.query('CREATE DATABASE %s;' % db_name) - try: - conn.query('USE %s;' % db_name) - - # OK, we're in the right environment. Create store and - # create the tables. - store = sqlstore.MySQLStore(conn) - store.createTables() - - # At last, we get to run the test. - testStore(store) - finally: - # Remove the database. If you want to do post-mortem on a - # failing test, comment out this line. - conn.query('DROP DATABASE %s;' % db_name) - -def test_postgresql(): - """ - Tests the PostgreSQLStore on a locally-hosted PostgreSQL database - cluster, version 7.4 or later. To run this test, you must have: - - - The 'psycopg' python module (version 1.1) installed - - - PostgreSQL running locally - - - An 'openid_test' user account in your database cluster, which - you can create by running 'createuser -Ad openid_test' as the - 'postgres' user - - - Trust auth for the 'openid_test' account, which you can activate - by adding the following line to your pg_hba.conf file: - - local all openid_test trust - - This test connects to the database cluster three times: - - - To the 'template1' database, to create the test database - - - To the test database, to run the store tests - - - To the 'template1' database once more, to drop the test database - """ - from askbot.deps.openid.store import sqlstore - try: - import psycopg - except ImportError: - pass - else: - db_name = getTmpDbName() - db_user = 'openid_test' - - # Connect once to create the database; reconnect to access the - # new database. - conn_create = psycopg.connect(database = 'template1', user = db_user, - host = db_host) - conn_create.autocommit() - - # Create the test database. - cursor = conn_create.cursor() - cursor.execute('CREATE DATABASE %s;' % (db_name,)) - conn_create.close() - - # Connect to the test database. - conn_test = psycopg.connect(database = db_name, user = db_user, - host = db_host) - - # OK, we're in the right environment. Create the store - # instance and create the tables. - store = sqlstore.PostgreSQLStore(conn_test) - store.createTables() - - # At last, we get to run the test. - testStore(store) - - # Disconnect. - conn_test.close() - - # It takes a little time for the close() call above to take - # effect, so we'll wait for a second before trying to remove - # the database. (Maybe this is because we're using a UNIX - # socket to connect to postgres rather than TCP?) - import time - time.sleep(1) - - # Remove the database now that the test is over. - conn_remove = psycopg.connect(database = 'template1', user = db_user, - host = db_host) - conn_remove.autocommit() - - cursor = conn_remove.cursor() - cursor.execute('DROP DATABASE %s;' % (db_name,)) - conn_remove.close() - -def test_memstore(): - from askbot.deps.openid.store import memstore - testStore(memstore.MemoryStore()) - -test_functions = [ - test_filestore, - test_sqlite, - test_mysql, - test_postgresql, - test_memstore, - ] - -def pyUnitTests(): - tests = map(unittest.FunctionTestCase, test_functions) - load = unittest.defaultTestLoader.loadTestsFromTestCase - return unittest.TestSuite(tests) - -if __name__ == '__main__': - import sys - suite = pyUnitTests() - runner = unittest.TextTestRunner() - result = runner.run(suite) - if result.wasSuccessful(): - sys.exit(0) - else: - sys.exit(1) diff --git a/askbot/deps/openid/test/support.py b/askbot/deps/openid/test/support.py deleted file mode 100644 index 1573edbd..00000000 --- a/askbot/deps/openid/test/support.py +++ /dev/null @@ -1,51 +0,0 @@ -from askbot.deps.openid import message -from askbot.deps.openid import oidutil - -class OpenIDTestMixin(object): - def failUnlessOpenIDValueEquals(self, msg, key, expected, ns=None): - if ns is None: - ns = message.OPENID_NS - - actual = msg.getArg(ns, key) - error_format = 'Wrong value for openid.%s: expected=%s, actual=%s' - error_message = error_format % (key, expected, actual) - self.failUnlessEqual(expected, actual, error_message) - - def failIfOpenIDKeyExists(self, msg, key, ns=None): - if ns is None: - ns = message.OPENID_NS - - actual = msg.getArg(ns, key) - error_message = 'openid.%s unexpectedly present: %s' % (key, actual) - self.failIf(actual is not None, error_message) - -class CatchLogs(object): - def setUp(self): - self.old_logger = oidutil.log - oidutil.log = self.gotLogMessage - self.messages = [] - - def gotLogMessage(self, message): - self.messages.append(message) - - def tearDown(self): - oidutil.log = self.old_logger - - def failUnlessLogMatches(self, *prefixes): - """ - Check that the log messages contained in self.messages have - prefixes in *prefixes. Raise AssertionError if not, or if the - number of prefixes is different than the number of log - messages. - """ - assert len(prefixes) == len(self.messages), \ - "Expected log prefixes %r, got %r" % (prefixes, - self.messages) - - for prefix, message in zip(prefixes, self.messages): - assert message.startswith(prefix), \ - "Expected log prefixes %r, got %r" % (prefixes, - self.messages) - - def failUnlessLogEmpty(self): - self.failUnlessLogMatches() diff --git a/askbot/deps/openid/test/test_accept.py b/askbot/deps/openid/test/test_accept.py deleted file mode 100644 index 0bb491eb..00000000 --- a/askbot/deps/openid/test/test_accept.py +++ /dev/null @@ -1,127 +0,0 @@ -import unittest -import os.path -from askbot.deps.openid.yadis import accept - -def getTestData(): - """Read the test data off of disk - - () -> [(int, str)] - """ - filename = os.path.join(os.path.dirname(__file__), 'data', 'accept.txt') - i = 1 - lines = [] - for line in file(filename): - lines.append((i, line)) - i += 1 - return lines - -def chunk(lines): - """Return groups of lines separated by whitespace or comments - - [(int, str)] -> [[(int, str)]] - """ - chunks = [] - chunk = [] - for lineno, line in lines: - stripped = line.strip() - if not stripped or stripped[0] == '#': - if chunk: - chunks.append(chunk) - chunk = [] - else: - chunk.append((lineno, stripped)) - - if chunk: - chunks.append(chunk) - - return chunks - -def parseLines(chunk): - """Take the given chunk of lines and turn it into a test data dictionary - - [(int, str)] -> {str:(int, str)} - """ - items = {} - for (lineno, line) in chunk: - header, data = line.split(':', 1) - header = header.lower() - items[header] = (lineno, data.strip()) - - return items - -def parseAvailable(available_text): - """Parse an Available: line's data - - str -> [str] - """ - return [s.strip() for s in available_text.split(',')] - -def parseExpected(expected_text): - """Parse an Expected: line's data - - str -> [(str, float)] - """ - expected = [] - if expected_text: - for chunk in expected_text.split(','): - chunk = chunk.strip() - mtype, qstuff = chunk.split(';') - mtype = mtype.strip() - assert '/' in mtype - qstuff = qstuff.strip() - q, qstr = qstuff.split('=') - assert q == 'q' - qval = float(qstr) - expected.append((mtype, qval)) - - return expected - -class MatchAcceptTest(unittest.TestCase): - def __init__(self, descr, accept_header, available, expected): - unittest.TestCase.__init__(self) - self.accept_header = accept_header - self.available = available - self.expected = expected - self.descr = descr - - def shortDescription(self): - return self.descr - - def runTest(self): - accepted = accept.parseAcceptHeader(self.accept_header) - actual = accept.matchTypes(accepted, self.available) - self.failUnlessEqual(self.expected, actual) - -def pyUnitTests(): - lines = getTestData() - chunks = chunk(lines) - data_sets = map(parseLines, chunks) - cases = [] - for data in data_sets: - lnos = [] - lno, header = data['accept'] - lnos.append(lno) - lno, avail_data = data['available'] - lnos.append(lno) - try: - available = parseAvailable(avail_data) - except: - print 'On line', lno - raise - - lno, exp_data = data['expected'] - lnos.append(lno) - try: - expected = parseExpected(exp_data) - except: - print 'On line', lno - raise - - descr = 'MatchAcceptTest for lines %r' % (lnos,) - case = MatchAcceptTest(descr, header, available, expected) - cases.append(case) - return unittest.TestSuite(cases) - -if __name__ == '__main__': - runner = unittest.TextTestRunner() - runner.run(loadTests()) diff --git a/askbot/deps/openid/test/test_association.py b/askbot/deps/openid/test/test_association.py deleted file mode 100644 index 486be87b..00000000 --- a/askbot/deps/openid/test/test_association.py +++ /dev/null @@ -1,183 +0,0 @@ -from askbot.deps.openid.test import datadriven - -import unittest - -from askbot.deps.openid.message import Message, BARE_NS, OPENID_NS, OPENID2_NS -from askbot.deps.openid import association -import time -from askbot.deps.openid import cryptutil -import warnings - -class AssociationSerializationTest(unittest.TestCase): - def test_roundTrip(self): - issued = int(time.time()) - lifetime = 600 - assoc = association.Association( - 'handle', 'secret', issued, lifetime, 'HMAC-SHA1') - s = assoc.serialize() - assoc2 = association.Association.deserialize(s) - self.failUnlessEqual(assoc.handle, assoc2.handle) - self.failUnlessEqual(assoc.issued, assoc2.issued) - self.failUnlessEqual(assoc.secret, assoc2.secret) - self.failUnlessEqual(assoc.lifetime, assoc2.lifetime) - self.failUnlessEqual(assoc.assoc_type, assoc2.assoc_type) - -from askbot.deps.openid.server.server import \ - DiffieHellmanSHA1ServerSession, \ - DiffieHellmanSHA256ServerSession, \ - PlainTextServerSession - -from askbot.deps.openid.consumer.consumer import \ - DiffieHellmanSHA1ConsumerSession, \ - DiffieHellmanSHA256ConsumerSession, \ - PlainTextConsumerSession - -from askbot.deps.openid.dh import DiffieHellman - -def createNonstandardConsumerDH(): - nonstandard_dh = DiffieHellman(1315291, 2) - return DiffieHellmanSHA1ConsumerSession(nonstandard_dh) - -class DiffieHellmanSessionTest(datadriven.DataDrivenTestCase): - secrets = [ - '\x00' * 20, - '\xff' * 20, - ' ' * 20, - 'This is a secret....', - ] - - session_factories = [ - (DiffieHellmanSHA1ConsumerSession, DiffieHellmanSHA1ServerSession), - (createNonstandardConsumerDH, DiffieHellmanSHA1ServerSession), - (PlainTextConsumerSession, PlainTextServerSession), - ] - - def generateCases(cls): - return [(c, s, sec) - for c, s in cls.session_factories - for sec in cls.secrets] - - generateCases = classmethod(generateCases) - - def __init__(self, csess_fact, ssess_fact, secret): - datadriven.DataDrivenTestCase.__init__(self, csess_fact.__name__) - self.secret = secret - self.csess_fact = csess_fact - self.ssess_fact = ssess_fact - - def runOneTest(self): - csess = self.csess_fact() - msg = Message.fromOpenIDArgs(csess.getRequest()) - ssess = self.ssess_fact.fromMessage(msg) - check_secret = csess.extractSecret( - Message.fromOpenIDArgs(ssess.answer(self.secret))) - self.failUnlessEqual(self.secret, check_secret) - - - -class TestMakePairs(unittest.TestCase): - """Check the key-value formatting methods of associations. - """ - - def setUp(self): - self.message = m = Message(OPENID2_NS) - m.updateArgs(OPENID2_NS, { - 'mode': 'id_res', - 'identifier': '=example', - 'signed': 'identifier,mode', - 'sig': 'cephalopod', - }) - m.updateArgs(BARE_NS, {'xey': 'value'}) - self.assoc = association.Association.fromExpiresIn( - 3600, '{sha1}', 'very_secret', "HMAC-SHA1") - - - def testMakePairs(self): - """Make pairs using the OpenID 1.x type signed list.""" - pairs = self.assoc._makePairs(self.message) - expected = [ - ('identifier', '=example'), - ('mode', 'id_res'), - ] - self.failUnlessEqual(pairs, expected) - - - -class TestMac(unittest.TestCase): - def setUp(self): - self.pairs = [('key1', 'value1'), - ('key2', 'value2')] - - - def test_sha1(self): - assoc = association.Association.fromExpiresIn( - 3600, '{sha1}', 'very_secret', "HMAC-SHA1") - expected = ('\xe0\x1bv\x04\xf1G\xc0\xbb\x7f\x9a\x8b' - '\xe9\xbc\xee}\\\xe5\xbb7*') - sig = assoc.sign(self.pairs) - self.failUnlessEqual(sig, expected) - - if cryptutil.SHA256_AVAILABLE: - def test_sha256(self): - assoc = association.Association.fromExpiresIn( - 3600, '{sha256SA}', 'very_secret', "HMAC-SHA256") - expected = ('\xfd\xaa\xfe;\xac\xfc*\x988\xad\x05d6-\xeaVy' - '\xd5\xa5Z.<\xa9\xed\x18\x82\\$\x95x\x1c&') - sig = assoc.sign(self.pairs) - self.failUnlessEqual(sig, expected) - - - -class TestMessageSigning(unittest.TestCase): - def setUp(self): - self.message = m = Message(OPENID2_NS) - m.updateArgs(OPENID2_NS, {'mode': 'id_res', - 'identifier': '=example'}) - m.updateArgs(BARE_NS, {'xey': 'value'}) - self.args = {'openid.mode': 'id_res', - 'openid.identifier': '=example', - 'xey': 'value'} - - - def test_signSHA1(self): - assoc = association.Association.fromExpiresIn( - 3600, '{sha1}', 'very_secret', "HMAC-SHA1") - signed = assoc.signMessage(self.message) - self.failUnless(signed.getArg(OPENID_NS, "sig")) - self.failUnlessEqual(signed.getArg(OPENID_NS, "signed"), - "assoc_handle,identifier,mode,ns,signed") - self.failUnlessEqual(signed.getArg(BARE_NS, "xey"), "value", - signed) - - if cryptutil.SHA256_AVAILABLE: - def test_signSHA256(self): - assoc = association.Association.fromExpiresIn( - 3600, '{sha1}', 'very_secret', "HMAC-SHA256") - signed = assoc.signMessage(self.message) - self.failUnless(signed.getArg(OPENID_NS, "sig")) - self.failUnlessEqual(signed.getArg(OPENID_NS, "signed"), - "assoc_handle,identifier,mode,ns,signed") - self.failUnlessEqual(signed.getArg(BARE_NS, "xey"), "value", - signed) - - -class TestCheckMessageSignature(unittest.TestCase): - def test_aintGotSignedList(self): - m = Message(OPENID2_NS) - m.updateArgs(OPENID2_NS, {'mode': 'id_res', - 'identifier': '=example', - 'sig': 'coyote', - }) - m.updateArgs(BARE_NS, {'xey': 'value'}) - assoc = association.Association.fromExpiresIn( - 3600, '{sha1}', 'very_secret', "HMAC-SHA1") - self.failUnlessRaises(ValueError, assoc.checkMessageSignature, m) - - -def pyUnitTests(): - return datadriven.loadTests(__name__) - -if __name__ == '__main__': - suite = pyUnitTests() - runner = unittest.TextTestRunner() - runner.run(suite) diff --git a/askbot/deps/openid/test/test_association_response.py b/askbot/deps/openid/test/test_association_response.py deleted file mode 100644 index 34b2145f..00000000 --- a/askbot/deps/openid/test/test_association_response.py +++ /dev/null @@ -1,340 +0,0 @@ -"""Tests for consumer handling of association responses - -This duplicates some things that are covered by test_consumer, but -this works for now. -""" -from askbot.deps.openid import oidutil -from askbot.deps.openid.test.test_consumer import CatchLogs -from askbot.deps.openid.message import Message, OPENID2_NS, OPENID_NS, no_default -from askbot.deps.openid.server.server import DiffieHellmanSHA1ServerSession -from askbot.deps.openid.consumer.consumer import GenericConsumer, \ - DiffieHellmanSHA1ConsumerSession, ProtocolError -from askbot.deps.openid.consumer.discover import OpenIDServiceEndpoint, OPENID_1_1_TYPE, OPENID_2_0_TYPE -from askbot.deps.openid.store import memstore -import unittest - -# Some values we can use for convenience (see mkAssocResponse) -association_response_values = { - 'expires_in': '1000', - 'assoc_handle':'a handle', - 'assoc_type':'a type', - 'session_type':'a session type', - 'ns':OPENID2_NS, - } - -def mkAssocResponse(*keys): - """Build an association response message that contains the - specified subset of keys. The values come from - `association_response_values`. - - This is useful for testing for missing keys and other times that - we don't care what the values are.""" - args = dict([(key, association_response_values[key]) for key in keys]) - return Message.fromOpenIDArgs(args) - -class BaseAssocTest(CatchLogs, unittest.TestCase): - def setUp(self): - CatchLogs.setUp(self) - self.store = memstore.MemoryStore() - self.consumer = GenericConsumer(self.store) - self.endpoint = OpenIDServiceEndpoint() - - def failUnlessProtocolError(self, str_prefix, func, *args, **kwargs): - try: - result = func(*args, **kwargs) - except ProtocolError, e: - message = 'Expected prefix %r, got %r' % (str_prefix, e[0]) - self.failUnless(e[0].startswith(str_prefix), message) - else: - self.fail('Expected ProtocolError, got %r' % (result,)) - -def mkExtractAssocMissingTest(keys): - """Factory function for creating test methods for generating - missing field tests. - - Make a test that ensures that an association response that - is missing required fields will short-circuit return None. - - According to 'Association Session Response' subsection 'Common - Response Parameters', the following fields are required for OpenID - 2.0: - - * ns - * session_type - * assoc_handle - * assoc_type - * expires_in - - If 'ns' is missing, it will fall back to OpenID 1 checking. In - OpenID 1, everything except 'session_type' and 'ns' are required. - """ - - def test(self): - msg = mkAssocResponse(*keys) - - self.failUnlessRaises(KeyError, - self.consumer._extractAssociation, msg, None) - - return test - -class TestExtractAssociationMissingFieldsOpenID2(BaseAssocTest): - """Test for returning an error upon missing fields in association - responses for OpenID 2""" - - test_noFields_openid2 = mkExtractAssocMissingTest(['ns']) - - test_missingExpires_openid2 = mkExtractAssocMissingTest( - ['assoc_handle', 'assoc_type', 'session_type', 'ns']) - - test_missingHandle_openid2 = mkExtractAssocMissingTest( - ['expires_in', 'assoc_type', 'session_type', 'ns']) - - test_missingAssocType_openid2 = mkExtractAssocMissingTest( - ['expires_in', 'assoc_handle', 'session_type', 'ns']) - - test_missingSessionType_openid2 = mkExtractAssocMissingTest( - ['expires_in', 'assoc_handle', 'assoc_type', 'ns']) - -class TestExtractAssociationMissingFieldsOpenID1(BaseAssocTest): - """Test for returning an error upon missing fields in association - responses for OpenID 2""" - - test_noFields_openid1 = mkExtractAssocMissingTest([]) - - test_missingExpires_openid1 = mkExtractAssocMissingTest( - ['assoc_handle', 'assoc_type']) - - test_missingHandle_openid1 = mkExtractAssocMissingTest( - ['expires_in', 'assoc_type']) - - test_missingAssocType_openid1 = mkExtractAssocMissingTest( - ['expires_in', 'assoc_handle']) - -class DummyAssocationSession(object): - def __init__(self, session_type, allowed_assoc_types=()): - self.session_type = session_type - self.allowed_assoc_types = allowed_assoc_types - -class ExtractAssociationSessionTypeMismatch(BaseAssocTest): - def mkTest(requested_session_type, response_session_type, openid1=False): - def test(self): - assoc_session = DummyAssocationSession(requested_session_type) - keys = association_response_values.keys() - if openid1: - keys.remove('ns') - msg = mkAssocResponse(*keys) - msg.setArg(OPENID_NS, 'session_type', response_session_type) - self.failUnlessProtocolError('Session type mismatch', - self.consumer._extractAssociation, msg, assoc_session) - - return test - - test_typeMismatchNoEncBlank_openid2 = mkTest( - requested_session_type='no-encryption', - response_session_type='', - ) - - test_typeMismatchDHSHA1NoEnc_openid2 = mkTest( - requested_session_type='DH-SHA1', - response_session_type='no-encryption', - ) - - test_typeMismatchDHSHA256NoEnc_openid2 = mkTest( - requested_session_type='DH-SHA256', - response_session_type='no-encryption', - ) - - test_typeMismatchNoEncDHSHA1_openid2 = mkTest( - requested_session_type='no-encryption', - response_session_type='DH-SHA1', - ) - - test_typeMismatchDHSHA1NoEnc_openid1 = mkTest( - requested_session_type='DH-SHA1', - response_session_type='DH-SHA256', - openid1=True, - ) - - test_typeMismatchDHSHA256NoEnc_openid1 = mkTest( - requested_session_type='DH-SHA256', - response_session_type='DH-SHA1', - openid1=True, - ) - - test_typeMismatchNoEncDHSHA1_openid1 = mkTest( - requested_session_type='no-encryption', - response_session_type='DH-SHA1', - openid1=True, - ) - - -class TestOpenID1AssociationResponseSessionType(BaseAssocTest): - def mkTest(expected_session_type, session_type_value): - """Return a test method that will check what session type will - be used if the OpenID 1 response to an associate call sets the - 'session_type' field to `session_type_value` - """ - def test(self): - self._doTest(expected_session_type, session_type_value) - self.failUnlessEqual(0, len(self.messages)) - - return test - - def _doTest(self, expected_session_type, session_type_value): - # Create a Message with just 'session_type' in it, since - # that's all this function will use. 'session_type' may be - # absent if it's set to None. - args = {} - if session_type_value is not None: - args['session_type'] = session_type_value - message = Message.fromOpenIDArgs(args) - self.failUnless(message.isOpenID1()) - - actual_session_type = self.consumer._getOpenID1SessionType(message) - error_message = ('Returned sesion type parameter %r was expected ' - 'to yield session type %r, but yielded %r' % - (session_type_value, expected_session_type, - actual_session_type)) - self.failUnlessEqual( - expected_session_type, actual_session_type, error_message) - - test_none = mkTest( - session_type_value=None, - expected_session_type='no-encryption', - ) - - test_empty = mkTest( - session_type_value='', - expected_session_type='no-encryption', - ) - - # This one's different because it expects log messages - def test_explicitNoEncryption(self): - self._doTest( - session_type_value='no-encryption', - expected_session_type='no-encryption', - ) - self.failUnlessEqual(1, len(self.messages)) - self.failUnless(self.messages[0].startswith( - 'WARNING: OpenID server sent "no-encryption"')) - - test_dhSHA1 = mkTest( - session_type_value='DH-SHA1', - expected_session_type='DH-SHA1', - ) - - # DH-SHA256 is not a valid session type for OpenID1, but this - # function does not test that. This is mostly just to make sure - # that it will pass-through stuff that is not explicitly handled, - # so it will get handled the same way as it is handled for OpenID - # 2 - test_dhSHA256 = mkTest( - session_type_value='DH-SHA256', - expected_session_type='DH-SHA256', - ) - -class DummyAssociationSession(object): - secret = "shh! don't tell!" - extract_secret_called = False - - session_type = None - - allowed_assoc_types = None - - def extractSecret(self, message): - self.extract_secret_called = True - return self.secret - -class TestInvalidFields(BaseAssocTest): - def setUp(self): - BaseAssocTest.setUp(self) - self.session_type = 'testing-session' - - # This must something that works for Association.fromExpiresIn - self.assoc_type = 'HMAC-SHA1' - - self.assoc_handle = 'testing-assoc-handle' - - # These arguments should all be valid - self.assoc_response = Message.fromOpenIDArgs({ - 'expires_in': '1000', - 'assoc_handle':self.assoc_handle, - 'assoc_type':self.assoc_type, - 'session_type':self.session_type, - 'ns':OPENID2_NS, - }) - - self.assoc_session = DummyAssociationSession() - - # Make the session for the response's session type - self.assoc_session.session_type = self.session_type - self.assoc_session.allowed_assoc_types = [self.assoc_type] - - def test_worksWithGoodFields(self): - """Handle a full successful association response""" - assoc = self.consumer._extractAssociation( - self.assoc_response, self.assoc_session) - self.failUnless(self.assoc_session.extract_secret_called) - self.failUnlessEqual(self.assoc_session.secret, assoc.secret) - self.failUnlessEqual(1000, assoc.lifetime) - self.failUnlessEqual(self.assoc_handle, assoc.handle) - self.failUnlessEqual(self.assoc_type, assoc.assoc_type) - - def test_badAssocType(self): - # Make sure that the assoc type in the response is not valid - # for the given session. - self.assoc_session.allowed_assoc_types = [] - self.failUnlessProtocolError('Unsupported assoc_type for session', - self.consumer._extractAssociation, - self.assoc_response, self.assoc_session) - - def test_badExpiresIn(self): - # Invalid value for expires_in should cause failure - self.assoc_response.setArg(OPENID_NS, 'expires_in', 'forever') - self.failUnlessProtocolError('Invalid expires_in', - self.consumer._extractAssociation, - self.assoc_response, self.assoc_session) - - -# XXX: This is what causes most of the imports in this file. It is -# sort of a unit test and sort of a functional test. I'm not terribly -# fond of it. -class TestExtractAssociationDiffieHellman(BaseAssocTest): - secret = 'x' * 20 - - def _setUpDH(self): - sess, message = self.consumer._createAssociateRequest( - self.endpoint, 'HMAC-SHA1', 'DH-SHA1') - - # XXX: this is testing _createAssociateRequest - self.failUnlessEqual(self.endpoint.compatibilityMode(), - message.isOpenID1()) - - server_sess = DiffieHellmanSHA1ServerSession.fromMessage(message) - server_resp = server_sess.answer(self.secret) - server_resp['assoc_type'] = 'HMAC-SHA1' - server_resp['assoc_handle'] = 'handle' - server_resp['expires_in'] = '1000' - server_resp['session_type'] = 'DH-SHA1' - return sess, Message.fromOpenIDArgs(server_resp) - - def test_success(self): - sess, server_resp = self._setUpDH() - ret = self.consumer._extractAssociation(server_resp, sess) - self.failIf(ret is None) - self.failUnlessEqual(ret.assoc_type, 'HMAC-SHA1') - self.failUnlessEqual(ret.secret, self.secret) - self.failUnlessEqual(ret.handle, 'handle') - self.failUnlessEqual(ret.lifetime, 1000) - - def test_openid2success(self): - # Use openid 2 type in endpoint so _setUpDH checks - # compatibility mode state properly - self.endpoint.type_uris = [OPENID_2_0_TYPE, OPENID_1_1_TYPE] - self.test_success() - - def test_badDHValues(self): - sess, server_resp = self._setUpDH() - server_resp.setArg(OPENID_NS, 'enc_mac_key', '\x00\x00\x00') - self.failUnlessProtocolError('Malformed response for', - self.consumer._extractAssociation, server_resp, sess) diff --git a/askbot/deps/openid/test/test_auth_request.py b/askbot/deps/openid/test/test_auth_request.py deleted file mode 100644 index 412b77a4..00000000 --- a/askbot/deps/openid/test/test_auth_request.py +++ /dev/null @@ -1,206 +0,0 @@ -import cgi -import unittest - -from askbot.deps.openid.consumer import consumer -from askbot.deps.openid import message -from askbot.deps.openid.test import support - -class DummyEndpoint(object): - preferred_namespace = None - local_id = None - server_url = None - is_op_identifier = False - - def preferredNamespace(self): - return self.preferred_namespace - - def getLocalID(self): - return self.local_id - - def isOPIdentifier(self): - return self.is_op_identifier - -class DummyAssoc(object): - handle = "assoc-handle" - -class TestAuthRequestMixin(support.OpenIDTestMixin): - """Mixin for AuthRequest tests for OpenID 1 and 2; DON'T add - unittest.TestCase as a base class here.""" - - preferred_namespace = None - immediate = False - expected_mode = 'checkid_setup' - - def setUp(self): - self.endpoint = DummyEndpoint() - self.endpoint.local_id = 'http://server.unittest/joe' - self.endpoint.claimed_id = 'http://joe.vanity.example/' - self.endpoint.server_url = 'http://server.unittest/' - self.endpoint.preferred_namespace = self.preferred_namespace - self.realm = 'http://example/' - self.return_to = 'http://example/return/' - self.assoc = DummyAssoc() - self.authreq = consumer.AuthRequest(self.endpoint, self.assoc) - - def failUnlessAnonymous(self, msg): - for key in ['claimed_id', 'identity']: - self.failIfOpenIDKeyExists(msg, key) - - def failUnlessHasRequiredFields(self, msg): - self.failUnlessEqual(self.preferred_namespace, - self.authreq.message.getOpenIDNamespace()) - - self.failUnlessEqual(self.preferred_namespace, - msg.getOpenIDNamespace()) - - self.failUnlessOpenIDValueEquals(msg, 'mode', - self.expected_mode) - - # Implement these in subclasses because they depend on - # protocol differences! - self.failUnlessHasRealm(msg) - self.failUnlessIdentifiersPresent(msg) - - # TESTS - - def test_checkNoAssocHandle(self): - self.authreq.assoc = None - msg = self.authreq.getMessage(self.realm, self.return_to, - self.immediate) - - self.failIfOpenIDKeyExists(msg, 'assoc_handle') - - def test_checkWithAssocHandle(self): - msg = self.authreq.getMessage(self.realm, self.return_to, - self.immediate) - - self.failUnlessOpenIDValueEquals(msg, 'assoc_handle', - self.assoc.handle) - - def test_addExtensionArg(self): - self.authreq.addExtensionArg('bag:', 'color', 'brown') - self.authreq.addExtensionArg('bag:', 'material', 'paper') - self.failUnless('bag:' in self.authreq.message.namespaces) - self.failUnlessEqual(self.authreq.message.getArgs('bag:'), - {'color': 'brown', - 'material': 'paper'}) - msg = self.authreq.getMessage(self.realm, self.return_to, - self.immediate) - - # XXX: this depends on the way that Message assigns - # namespaces. Really it doesn't care that it has alias "0", - # but that is tested anyway - post_args = msg.toPostArgs() - self.failUnlessEqual('brown', post_args['openid.ext0.color']) - self.failUnlessEqual('paper', post_args['openid.ext0.material']) - - def test_standard(self): - msg = self.authreq.getMessage(self.realm, self.return_to, - self.immediate) - - self.failUnlessHasIdentifiers( - msg, self.endpoint.local_id, self.endpoint.claimed_id) - -class TestAuthRequestOpenID2(TestAuthRequestMixin, unittest.TestCase): - preferred_namespace = message.OPENID2_NS - - def failUnlessHasRealm(self, msg): - # check presence of proper realm key and absence of the wrong - # one. - self.failUnlessOpenIDValueEquals(msg, 'realm', self.realm) - self.failIfOpenIDKeyExists(msg, 'trust_root') - - def failUnlessIdentifiersPresent(self, msg): - identity_present = msg.hasKey(message.OPENID_NS, 'identity') - claimed_present = msg.hasKey(message.OPENID_NS, 'claimed_id') - - self.failUnlessEqual(claimed_present, identity_present) - - def failUnlessHasIdentifiers(self, msg, op_specific_id, claimed_id): - self.failUnlessOpenIDValueEquals(msg, 'identity', op_specific_id) - self.failUnlessOpenIDValueEquals(msg, 'claimed_id', claimed_id) - - # TESTS - - def test_setAnonymousWorksForOpenID2(self): - """OpenID AuthRequests should be able to set 'anonymous' to true.""" - self.failUnless(self.authreq.message.isOpenID2()) - self.authreq.setAnonymous(True) - self.authreq.setAnonymous(False) - - def test_userAnonymousIgnoresIdentfier(self): - self.authreq.setAnonymous(True) - msg = self.authreq.getMessage(self.realm, self.return_to, - self.immediate) - self.failUnlessHasRequiredFields(msg) - self.failUnlessAnonymous(msg) - - def test_opAnonymousIgnoresIdentifier(self): - self.endpoint.is_op_identifier = True - self.authreq.setAnonymous(True) - msg = self.authreq.getMessage(self.realm, self.return_to, - self.immediate) - self.failUnlessHasRequiredFields(msg) - self.failUnlessAnonymous(msg) - - def test_opIdentifierSendsIdentifierSelect(self): - self.endpoint.is_op_identifier = True - msg = self.authreq.getMessage(self.realm, self.return_to, - self.immediate) - self.failUnlessHasRequiredFields(msg) - self.failUnlessHasIdentifiers( - msg, message.IDENTIFIER_SELECT, message.IDENTIFIER_SELECT) - -class TestAuthRequestOpenID1(TestAuthRequestMixin, unittest.TestCase): - preferred_namespace = message.OPENID1_NS - - def setUpEndpoint(self): - TestAuthRequestBase.setUpEndpoint(self) - self.endpoint.preferred_namespace = message.OPENID1_NS - - def failUnlessHasIdentifiers(self, msg, op_specific_id, claimed_id): - """Make sure claimed_is is *absent* in request.""" - self.failUnlessOpenIDValueEquals(msg, 'identity', op_specific_id) - self.failIfOpenIDKeyExists(msg, 'claimed_id') - - def failUnlessIdentifiersPresent(self, msg): - self.failIfOpenIDKeyExists(msg, 'claimed_id') - self.failUnless(msg.hasKey(message.OPENID_NS, 'identity')) - - def failUnlessHasRealm(self, msg): - # check presence of proper realm key and absence of the wrong - # one. - self.failUnlessOpenIDValueEquals(msg, 'trust_root', self.realm) - self.failIfOpenIDKeyExists(msg, 'realm') - - # TESTS - - def test_setAnonymousFailsForOpenID1(self): - """OpenID 1 requests MUST NOT be able to set anonymous to True""" - self.failUnless(self.authreq.message.isOpenID1()) - self.failUnlessRaises(ValueError, self.authreq.setAnonymous, True) - self.authreq.setAnonymous(False) - - def test_identifierSelect(self): - """Identfier select SHOULD NOT be sent, but this pathway is in - here in case some special discovery stuff is done to trigger - it with OpenID 1. If it is triggered, it will send - identifier_select just like OpenID 2. - """ - self.endpoint.is_op_identifier = True - msg = self.authreq.getMessage(self.realm, self.return_to, - self.immediate) - self.failUnlessHasRequiredFields(msg) - self.failUnlessEqual(message.IDENTIFIER_SELECT, - msg.getArg(message.OPENID1_NS, 'identity')) - -class TestAuthRequestOpenID1Immediate(TestAuthRequestOpenID1): - immediate = True - expected_mode = 'checkid_immediate' - -class TestAuthRequestOpenID2Immediate(TestAuthRequestOpenID2): - immediate = True - expected_mode = 'checkid_immediate' - -if __name__ == '__main__': - unittest.main() diff --git a/askbot/deps/openid/test/test_ax.py b/askbot/deps/openid/test/test_ax.py deleted file mode 100644 index 6d4aeac5..00000000 --- a/askbot/deps/openid/test/test_ax.py +++ /dev/null @@ -1,626 +0,0 @@ -"""Tests for the attribute exchange extension module -""" - -import unittest -from askbot.deps.openid.extensions import ax -from askbot.deps.openid.message import NamespaceMap, Message, OPENID2_NS -from askbot.deps.openid.consumer.consumer import SuccessResponse - -class BogusAXMessage(ax.AXMessage): - mode = 'bogus' - - getExtensionArgs = ax.AXMessage._newArgs - -class DummyRequest(object): - def __init__(self, message): - self.message = message - -class AXMessageTest(unittest.TestCase): - def setUp(self): - self.bax = BogusAXMessage() - - def test_checkMode(self): - check = self.bax._checkMode - self.failUnlessRaises(ax.NotAXMessage, check, {}) - self.failUnlessRaises(ax.AXError, check, {'mode':'fetch_request'}) - - # does not raise an exception when the mode is right - check({'mode':self.bax.mode}) - - def test_checkMode_newArgs(self): - """_newArgs generates something that has the correct mode""" - # This would raise AXError if it didn't like the mode newArgs made. - self.bax._checkMode(self.bax._newArgs()) - - -class AttrInfoTest(unittest.TestCase): - def test_construct(self): - self.failUnlessRaises(TypeError, ax.AttrInfo) - type_uri = 'a uri' - ainfo = ax.AttrInfo(type_uri) - - self.failUnlessEqual(type_uri, ainfo.type_uri) - self.failUnlessEqual(1, ainfo.count) - self.failIf(ainfo.required) - self.failUnless(ainfo.alias is None) - - -class ToTypeURIsTest(unittest.TestCase): - def setUp(self): - self.aliases = NamespaceMap() - - def test_empty(self): - for empty in [None, '']: - uris = ax.toTypeURIs(self.aliases, empty) - self.failUnlessEqual([], uris) - - def test_undefined(self): - self.failUnlessRaises( - KeyError, - ax.toTypeURIs, self.aliases, 'http://janrain.com/') - - def test_one(self): - uri = 'http://janrain.com/' - alias = 'openid_hackers' - self.aliases.addAlias(uri, alias) - uris = ax.toTypeURIs(self.aliases, alias) - self.failUnlessEqual([uri], uris) - - def test_two(self): - uri1 = 'http://janrain.com/' - alias1 = 'openid_hackers' - self.aliases.addAlias(uri1, alias1) - - uri2 = 'http://jyte.com/' - alias2 = 'openid_hack' - self.aliases.addAlias(uri2, alias2) - - uris = ax.toTypeURIs(self.aliases, ','.join([alias1, alias2])) - self.failUnlessEqual([uri1, uri2], uris) - -class ParseAXValuesTest(unittest.TestCase): - """Testing AXKeyValueMessage.parseExtensionArgs.""" - - def failUnlessAXKeyError(self, ax_args): - msg = ax.AXKeyValueMessage() - self.failUnlessRaises(KeyError, msg.parseExtensionArgs, ax_args) - - def failUnlessAXValues(self, ax_args, expected_args): - """Fail unless parseExtensionArgs(ax_args) == expected_args.""" - msg = ax.AXKeyValueMessage() - msg.parseExtensionArgs(ax_args) - self.failUnlessEqual(expected_args, msg.data) - - def test_emptyIsValid(self): - self.failUnlessAXValues({}, {}) - - def test_missingValueForAliasExplodes(self): - self.failUnlessAXKeyError({'type.foo':'urn:foo'}) - - def test_countPresentButNotValue(self): - self.failUnlessAXKeyError({'type.foo':'urn:foo', - 'count.foo':'1'}) - - def test_invalidCountValue(self): - msg = ax.FetchRequest() - self.failUnlessRaises(ax.AXError, - msg.parseExtensionArgs, - {'type.foo':'urn:foo', - 'count.foo':'bogus'}) - - def test_requestUnlimitedValues(self): - msg = ax.FetchRequest() - - msg.parseExtensionArgs( - {'mode':'fetch_request', - 'required':'foo', - 'type.foo':'urn:foo', - 'count.foo':ax.UNLIMITED_VALUES}) - - attrs = list(msg.iterAttrs()) - foo = attrs[0] - - self.failUnless(foo.count == ax.UNLIMITED_VALUES) - self.failUnless(foo.wantsUnlimitedValues()) - - def test_longAlias(self): - # Spec minimum length is 32 characters. This is a silly test - # for this library, but it's here for completeness. - alias = 'x' * ax.MINIMUM_SUPPORTED_ALIAS_LENGTH - - msg = ax.AXKeyValueMessage() - msg.parseExtensionArgs( - {'type.%s' % (alias,): 'urn:foo', - 'count.%s' % (alias,): '1', - 'value.%s.1' % (alias,): 'first'} - ) - - def test_invalidAlias(self): - types = [ - ax.AXKeyValueMessage, - ax.FetchRequest - ] - - inputs = [ - {'type.a.b':'urn:foo', - 'count.a.b':'1'}, - {'type.a,b':'urn:foo', - 'count.a,b':'1'}, - ] - - for typ in types: - for input in inputs: - msg = typ() - self.failUnlessRaises(ax.AXError, msg.parseExtensionArgs, - input) - - def test_countPresentAndIsZero(self): - self.failUnlessAXValues( - {'type.foo':'urn:foo', - 'count.foo':'0', - }, {'urn:foo':[]}) - - def test_singletonEmpty(self): - self.failUnlessAXValues( - {'type.foo':'urn:foo', - 'value.foo':'', - }, {'urn:foo':[]}) - - def test_doubleAlias(self): - self.failUnlessAXKeyError( - {'type.foo':'urn:foo', - 'value.foo':'', - 'type.bar':'urn:foo', - 'value.bar':'', - }) - - def test_doubleSingleton(self): - self.failUnlessAXValues( - {'type.foo':'urn:foo', - 'value.foo':'', - 'type.bar':'urn:bar', - 'value.bar':'', - }, {'urn:foo':[], 'urn:bar':[]}) - - def test_singletonValue(self): - self.failUnlessAXValues( - {'type.foo':'urn:foo', - 'value.foo':'Westfall', - }, {'urn:foo':['Westfall']}) - - -class FetchRequestTest(unittest.TestCase): - def setUp(self): - self.msg = ax.FetchRequest() - self.type_a = 'http://janrain.example.com/a' - self.alias_a = 'a' - - - def test_mode(self): - self.failUnlessEqual(self.msg.mode, 'fetch_request') - - def test_construct(self): - self.failUnlessEqual({}, self.msg.requested_attributes) - self.failUnlessEqual(None, self.msg.update_url) - - msg = ax.FetchRequest('hailstorm') - self.failUnlessEqual({}, msg.requested_attributes) - self.failUnlessEqual('hailstorm', msg.update_url) - - def test_add(self): - uri = 'mud://puddle' - - # Not yet added: - self.failIf(uri in self.msg) - - attr = ax.AttrInfo(uri) - self.msg.add(attr) - - # Present after adding - self.failUnless(uri in self.msg) - - def test_addTwice(self): - uri = 'lightning://storm' - - attr = ax.AttrInfo(uri) - self.msg.add(attr) - self.failUnlessRaises(KeyError, self.msg.add, attr) - - def test_getExtensionArgs_empty(self): - expected_args = { - 'mode':'fetch_request', - } - self.failUnlessEqual(expected_args, self.msg.getExtensionArgs()) - - def test_getExtensionArgs_noAlias(self): - attr = ax.AttrInfo( - type_uri = 'type://of.transportation', - ) - self.msg.add(attr) - ax_args = self.msg.getExtensionArgs() - for k, v in ax_args.iteritems(): - if v == attr.type_uri and k.startswith('type.'): - alias = k[5:] - break - else: - self.fail("Didn't find the type definition") - - self.failUnlessExtensionArgs({ - 'type.' + alias:attr.type_uri, - 'if_available':alias, - }) - - def test_getExtensionArgs_alias_if_available(self): - attr = ax.AttrInfo( - type_uri = 'type://of.transportation', - alias = 'transport', - ) - self.msg.add(attr) - self.failUnlessExtensionArgs({ - 'type.' + attr.alias:attr.type_uri, - 'if_available':attr.alias, - }) - - def test_getExtensionArgs_alias_req(self): - attr = ax.AttrInfo( - type_uri = 'type://of.transportation', - alias = 'transport', - required = True, - ) - self.msg.add(attr) - self.failUnlessExtensionArgs({ - 'type.' + attr.alias:attr.type_uri, - 'required':attr.alias, - }) - - def failUnlessExtensionArgs(self, expected_args): - """Make sure that getExtensionArgs has the expected result - - This method will fill in the mode. - """ - expected_args = dict(expected_args) - expected_args['mode'] = self.msg.mode - self.failUnlessEqual(expected_args, self.msg.getExtensionArgs()) - - def test_isIterable(self): - self.failUnlessEqual([], list(self.msg)) - self.failUnlessEqual([], list(self.msg.iterAttrs())) - - def test_getRequiredAttrs_empty(self): - self.failUnlessEqual([], self.msg.getRequiredAttrs()) - - def test_parseExtensionArgs_extraType(self): - extension_args = { - 'mode':'fetch_request', - 'type.' + self.alias_a:self.type_a, - } - self.failUnlessRaises(ValueError, - self.msg.parseExtensionArgs, extension_args) - - def test_parseExtensionArgs(self): - extension_args = { - 'mode':'fetch_request', - 'type.' + self.alias_a:self.type_a, - 'if_available':self.alias_a - } - self.msg.parseExtensionArgs(extension_args) - self.failUnless(self.type_a in self.msg) - self.failUnlessEqual([self.type_a], list(self.msg)) - attr_info = self.msg.requested_attributes.get(self.type_a) - self.failUnless(attr_info) - self.failIf(attr_info.required) - self.failUnlessEqual(self.type_a, attr_info.type_uri) - self.failUnlessEqual(self.alias_a, attr_info.alias) - self.failUnlessEqual([attr_info], list(self.msg.iterAttrs())) - - def test_extensionArgs_idempotent(self): - extension_args = { - 'mode':'fetch_request', - 'type.' + self.alias_a:self.type_a, - 'if_available':self.alias_a - } - self.msg.parseExtensionArgs(extension_args) - self.failUnlessEqual(extension_args, self.msg.getExtensionArgs()) - self.failIf(self.msg.requested_attributes[self.type_a].required) - - def test_extensionArgs_idempotent_count_required(self): - extension_args = { - 'mode':'fetch_request', - 'type.' + self.alias_a:self.type_a, - 'count.' + self.alias_a:'2', - 'required':self.alias_a - } - self.msg.parseExtensionArgs(extension_args) - self.failUnlessEqual(extension_args, self.msg.getExtensionArgs()) - self.failUnless(self.msg.requested_attributes[self.type_a].required) - - def test_extensionArgs_count1(self): - extension_args = { - 'mode':'fetch_request', - 'type.' + self.alias_a:self.type_a, - 'count.' + self.alias_a:'1', - 'if_available':self.alias_a, - } - extension_args_norm = { - 'mode':'fetch_request', - 'type.' + self.alias_a:self.type_a, - 'if_available':self.alias_a, - } - self.msg.parseExtensionArgs(extension_args) - self.failUnlessEqual(extension_args_norm, self.msg.getExtensionArgs()) - - def test_openidNoRealm(self): - openid_req_msg = Message.fromOpenIDArgs({ - 'mode': 'checkid_setup', - 'ns': OPENID2_NS, - 'ns.ax': ax.AXMessage.ns_uri, - 'ax.update_url': 'http://different.site/path', - 'ax.mode': 'fetch_request', - }) - self.failUnlessRaises(ax.AXError, - ax.FetchRequest.fromOpenIDRequest, - DummyRequest(openid_req_msg)) - - def test_openidUpdateURLVerificationError(self): - openid_req_msg = Message.fromOpenIDArgs({ - 'mode': 'checkid_setup', - 'ns': OPENID2_NS, - 'realm': 'http://example.com/realm', - 'ns.ax': ax.AXMessage.ns_uri, - 'ax.update_url': 'http://different.site/path', - 'ax.mode': 'fetch_request', - }) - - self.failUnlessRaises(ax.AXError, - ax.FetchRequest.fromOpenIDRequest, - DummyRequest(openid_req_msg)) - - def test_openidUpdateURLVerificationSuccess(self): - openid_req_msg = Message.fromOpenIDArgs({ - 'mode': 'checkid_setup', - 'ns': OPENID2_NS, - 'realm': 'http://example.com/realm', - 'ns.ax': ax.AXMessage.ns_uri, - 'ax.update_url': 'http://example.com/realm/update_path', - 'ax.mode': 'fetch_request', - }) - - fr = ax.FetchRequest.fromOpenIDRequest(DummyRequest(openid_req_msg)) - - def test_openidUpdateURLVerificationSuccessReturnTo(self): - openid_req_msg = Message.fromOpenIDArgs({ - 'mode': 'checkid_setup', - 'ns': OPENID2_NS, - 'return_to': 'http://example.com/realm', - 'ns.ax': ax.AXMessage.ns_uri, - 'ax.update_url': 'http://example.com/realm/update_path', - 'ax.mode': 'fetch_request', - }) - - fr = ax.FetchRequest.fromOpenIDRequest(DummyRequest(openid_req_msg)) - - def test_fromOpenIDRequestWithoutExtension(self): - """return None for an OpenIDRequest without AX paramaters.""" - openid_req_msg = Message.fromOpenIDArgs({ - 'mode': 'checkid_setup', - 'ns': OPENID2_NS, - }) - oreq = DummyRequest(openid_req_msg) - r = ax.FetchRequest.fromOpenIDRequest(oreq) - self.failUnless(r is None, "%s is not None" % (r,)) - - def test_fromOpenIDRequestWithoutData(self): - """return something for SuccessResponse with AX paramaters, - even if it is the empty set.""" - openid_req_msg = Message.fromOpenIDArgs({ - 'mode': 'checkid_setup', - 'realm': 'http://example.com/realm', - 'ns': OPENID2_NS, - 'ns.ax': ax.AXMessage.ns_uri, - 'ax.mode': 'fetch_request', - }) - oreq = DummyRequest(openid_req_msg) - r = ax.FetchRequest.fromOpenIDRequest(oreq) - self.failUnless(r is not None) - - -class FetchResponseTest(unittest.TestCase): - def setUp(self): - self.msg = ax.FetchResponse() - self.value_a = 'monkeys' - self.type_a = 'http://phone.home/' - self.alias_a = 'robocop' - self.request_update_url = 'http://update.bogus/' - - def test_construct(self): - self.failUnless(self.msg.update_url is None) - self.failUnlessEqual({}, self.msg.data) - - def test_getExtensionArgs_empty(self): - expected_args = { - 'mode':'fetch_response', - } - self.failUnlessEqual(expected_args, self.msg.getExtensionArgs()) - - def test_getExtensionArgs_empty_request(self): - expected_args = { - 'mode':'fetch_response', - } - req = ax.FetchRequest() - msg = ax.FetchResponse(request=req) - self.failUnlessEqual(expected_args, msg.getExtensionArgs()) - - def test_getExtensionArgs_empty_request_some(self): - uri = 'http://not.found/' - alias = 'ext0' - - expected_args = { - 'mode':'fetch_response', - 'type.%s' % (alias,): uri, - 'count.%s' % (alias,): '0' - } - req = ax.FetchRequest() - req.add(ax.AttrInfo(uri)) - msg = ax.FetchResponse(request=req) - self.failUnlessEqual(expected_args, msg.getExtensionArgs()) - - def test_updateUrlInResponse(self): - uri = 'http://not.found/' - alias = 'ext0' - - expected_args = { - 'mode':'fetch_response', - 'update_url': self.request_update_url, - 'type.%s' % (alias,): uri, - 'count.%s' % (alias,): '0' - } - req = ax.FetchRequest(update_url=self.request_update_url) - req.add(ax.AttrInfo(uri)) - msg = ax.FetchResponse(request=req) - self.failUnlessEqual(expected_args, msg.getExtensionArgs()) - - def test_getExtensionArgs_some_request(self): - expected_args = { - 'mode':'fetch_response', - 'type.' + self.alias_a:self.type_a, - 'value.' + self.alias_a + '.1':self.value_a, - 'count.' + self.alias_a: '1' - } - req = ax.FetchRequest() - req.add(ax.AttrInfo(self.type_a, alias=self.alias_a)) - msg = ax.FetchResponse(request=req) - msg.addValue(self.type_a, self.value_a) - self.failUnlessEqual(expected_args, msg.getExtensionArgs()) - - def test_getExtensionArgs_some_not_request(self): - req = ax.FetchRequest() - msg = ax.FetchResponse(request=req) - msg.addValue(self.type_a, self.value_a) - self.failUnlessRaises(KeyError, msg.getExtensionArgs) - - def test_getSingle_success(self): - req = ax.FetchRequest() - self.msg.addValue(self.type_a, self.value_a) - self.failUnlessEqual(self.value_a, self.msg.getSingle(self.type_a)) - - def test_getSingle_none(self): - self.failUnlessEqual(None, self.msg.getSingle(self.type_a)) - - def test_getSingle_extra(self): - self.msg.setValues(self.type_a, ['x', 'y']) - self.failUnlessRaises(ax.AXError, self.msg.getSingle, self.type_a) - - def test_get(self): - self.failUnlessRaises(KeyError, self.msg.get, self.type_a) - - def test_fromSuccessResponseWithoutExtension(self): - """return None for SuccessResponse with no AX paramaters.""" - args = { - 'mode': 'id_res', - 'ns': OPENID2_NS, - } - sf = ['openid.' + i for i in args.keys()] - msg = Message.fromOpenIDArgs(args) - class Endpoint: - claimed_id = 'http://invalid.' - - oreq = SuccessResponse(Endpoint(), msg, signed_fields=sf) - r = ax.FetchResponse.fromSuccessResponse(oreq) - self.failUnless(r is None, "%s is not None" % (r,)) - - def test_fromSuccessResponseWithoutData(self): - """return something for SuccessResponse with AX paramaters, - even if it is the empty set.""" - args = { - 'mode': 'id_res', - 'ns': OPENID2_NS, - 'ns.ax': ax.AXMessage.ns_uri, - 'ax.mode': 'fetch_response', - } - sf = ['openid.' + i for i in args.keys()] - msg = Message.fromOpenIDArgs(args) - class Endpoint: - claimed_id = 'http://invalid.' - - oreq = SuccessResponse(Endpoint(), msg, signed_fields=sf) - r = ax.FetchResponse.fromSuccessResponse(oreq) - self.failUnless(r is not None) - - def test_fromSuccessResponseWithData(self): - name = 'ext0' - value = 'snozzberry' - uri = "http://willy.wonka.name/" - args = { - 'mode': 'id_res', - 'ns': OPENID2_NS, - 'ns.ax': ax.AXMessage.ns_uri, - 'ax.update_url': 'http://example.com/realm/update_path', - 'ax.mode': 'fetch_response', - 'ax.type.'+name: uri, - 'ax.count.'+name: '1', - 'ax.value.%s.1'%name: value, - } - sf = ['openid.' + i for i in args.keys()] - msg = Message.fromOpenIDArgs(args) - class Endpoint: - claimed_id = 'http://invalid.' - - resp = SuccessResponse(Endpoint(), msg, signed_fields=sf) - ax_resp = ax.FetchResponse.fromSuccessResponse(resp) - values = ax_resp.get(uri) - self.failUnlessEqual([value], values) - - -class StoreRequestTest(unittest.TestCase): - def setUp(self): - self.msg = ax.StoreRequest() - self.type_a = 'http://three.count/' - self.alias_a = 'juggling' - - def test_construct(self): - self.failUnlessEqual({}, self.msg.data) - - def test_getExtensionArgs_empty(self): - args = self.msg.getExtensionArgs() - expected_args = { - 'mode':'store_request', - } - self.failUnlessEqual(expected_args, args) - - def test_getExtensionArgs_nonempty(self): - aliases = NamespaceMap() - aliases.addAlias(self.type_a, self.alias_a) - msg = ax.StoreRequest(aliases=aliases) - msg.setValues(self.type_a, ['foo', 'bar']) - args = msg.getExtensionArgs() - expected_args = { - 'mode':'store_request', - 'type.' + self.alias_a: self.type_a, - 'count.' + self.alias_a: '2', - 'value.%s.1' % (self.alias_a,):'foo', - 'value.%s.2' % (self.alias_a,):'bar', - } - self.failUnlessEqual(expected_args, args) - -class StoreResponseTest(unittest.TestCase): - def test_success(self): - msg = ax.StoreResponse() - self.failUnless(msg.succeeded()) - self.failIf(msg.error_message) - self.failUnlessEqual({'mode':'store_response_success'}, - msg.getExtensionArgs()) - - def test_fail_nomsg(self): - msg = ax.StoreResponse(False) - self.failIf(msg.succeeded()) - self.failIf(msg.error_message) - self.failUnlessEqual({'mode':'store_response_failure'}, - msg.getExtensionArgs()) - - def test_fail_msg(self): - reason = 'no reason, really' - msg = ax.StoreResponse(False, reason) - self.failIf(msg.succeeded()) - self.failUnlessEqual(reason, msg.error_message) - self.failUnlessEqual({'mode':'store_response_failure', - 'error':reason}, msg.getExtensionArgs()) diff --git a/askbot/deps/openid/test/test_consumer.py b/askbot/deps/openid/test/test_consumer.py deleted file mode 100644 index e0bf48c5..00000000 --- a/askbot/deps/openid/test/test_consumer.py +++ /dev/null @@ -1,2097 +0,0 @@ -import urlparse -import cgi -import time -import warnings - -from askbot.deps.openid.message import Message, OPENID_NS, OPENID2_NS, IDENTIFIER_SELECT, \ - OPENID1_NS, BARE_NS -from askbot.deps.openid import cryptutil, dh, oidutil, kvform -from askbot.deps.openid.store.nonce import mkNonce, split as splitNonce -from askbot.deps.openid.consumer.discover import OpenIDServiceEndpoint, OPENID_2_0_TYPE, \ - OPENID_1_1_TYPE -from askbot.deps.openid.consumer.consumer import \ - AuthRequest, GenericConsumer, SUCCESS, FAILURE, CANCEL, SETUP_NEEDED, \ - SuccessResponse, FailureResponse, SetupNeededResponse, CancelResponse, \ - DiffieHellmanSHA1ConsumerSession, Consumer, PlainTextConsumerSession, \ - SetupNeededError, DiffieHellmanSHA256ConsumerSession, ServerError, \ - ProtocolError, _httpResponseToMessage -from askbot.deps.openid import association -from askbot.deps.openid.server.server import \ - PlainTextServerSession, DiffieHellmanSHA1ServerSession -from askbot.deps.openid.yadis.manager import Discovery -from askbot.deps.openid.yadis.discover import DiscoveryFailure -from askbot.deps.openid.dh import DiffieHellman - -from askbot.deps.openid.fetchers import HTTPResponse, HTTPFetchingError -from askbot.deps.openid import fetchers -from askbot.deps.openid.store import memstore - -from support import CatchLogs - -assocs = [ - ('another 20-byte key.', 'Snarky'), - ('\x00' * 20, 'Zeros'), - ] - -def mkSuccess(endpoint, q): - """Convenience function to create a SuccessResponse with the given - arguments, all signed.""" - signed_list = ['openid.' + k for k in q.keys()] - return SuccessResponse(endpoint, Message.fromOpenIDArgs(q), signed_list) - -def parseQuery(qs): - q = {} - for (k, v) in cgi.parse_qsl(qs): - assert not q.has_key(k) - q[k] = v - return q - -def associate(qs, assoc_secret, assoc_handle): - """Do the server's half of the associate call, using the given - secret and handle.""" - q = parseQuery(qs) - assert q['openid.mode'] == 'associate' - assert q['openid.assoc_type'] == 'HMAC-SHA1' - reply_dict = { - 'assoc_type':'HMAC-SHA1', - 'assoc_handle':assoc_handle, - 'expires_in':'600', - } - - if q.get('openid.session_type') == 'DH-SHA1': - assert len(q) == 6 or len(q) == 4 - message = Message.fromPostArgs(q) - session = DiffieHellmanSHA1ServerSession.fromMessage(message) - reply_dict['session_type'] = 'DH-SHA1' - else: - assert len(q) == 2 - session = PlainTextServerSession.fromQuery(q) - - reply_dict.update(session.answer(assoc_secret)) - return kvform.dictToKV(reply_dict) - - -GOODSIG = "[A Good Signature]" - - -class GoodAssociation: - expiresIn = 3600 - handle = "-blah-" - - def getExpiresIn(self): - return self.expiresIn - - def checkMessageSignature(self, message): - return message.getArg(OPENID_NS, 'sig') == GOODSIG - - -class GoodAssocStore(memstore.MemoryStore): - def getAssociation(self, server_url, handle=None): - return GoodAssociation() - - -class TestFetcher(object): - def __init__(self, user_url, user_page, (assoc_secret, assoc_handle)): - self.get_responses = {user_url:self.response(user_url, 200, user_page)} - self.assoc_secret = assoc_secret - self.assoc_handle = assoc_handle - self.num_assocs = 0 - - def response(self, url, status, body): - return HTTPResponse( - final_url=url, status=status, headers={}, body=body) - - def fetch(self, url, body=None, headers=None): - if body is None: - if url in self.get_responses: - return self.get_responses[url] - else: - try: - body.index('openid.mode=associate') - except ValueError: - pass # fall through - else: - assert body.find('DH-SHA1') != -1 - response = associate( - body, self.assoc_secret, self.assoc_handle) - self.num_assocs += 1 - return self.response(url, 200, response) - - return self.response(url, 404, 'Not found') - -def makeFastConsumerSession(): - """ - Create custom DH object so tests run quickly. - """ - dh = DiffieHellman(100389557, 2) - return DiffieHellmanSHA1ConsumerSession(dh) - -def setConsumerSession(con): - con.session_types = {'DH-SHA1': makeFastConsumerSession} - -def _test_success(server_url, user_url, delegate_url, links, immediate=False): - store = memstore.MemoryStore() - if immediate: - mode = 'checkid_immediate' - else: - mode = 'checkid_setup' - - endpoint = OpenIDServiceEndpoint() - endpoint.claimed_id = user_url - endpoint.server_url = server_url - endpoint.local_id = delegate_url - endpoint.type_uris = [OPENID_1_1_TYPE] - - fetcher = TestFetcher(None, None, assocs[0]) - fetchers.setDefaultFetcher(fetcher, wrap_exceptions=False) - - def run(): - trust_root = consumer_url - - consumer = GenericConsumer(store) - setConsumerSession(consumer) - - request = consumer.begin(endpoint) - return_to = consumer_url - - m = request.getMessage(trust_root, return_to, immediate) - - redirect_url = request.redirectURL(trust_root, return_to, immediate) - - parsed = urlparse.urlparse(redirect_url) - qs = parsed[4] - q = parseQuery(qs) - new_return_to = q['openid.return_to'] - del q['openid.return_to'] - assert q == { - 'openid.mode':mode, - 'openid.identity':delegate_url, - 'openid.trust_root':trust_root, - 'openid.assoc_handle':fetcher.assoc_handle, - }, (q, user_url, delegate_url, mode) - - assert new_return_to.startswith(return_to) - assert redirect_url.startswith(server_url) - - parsed = urlparse.urlparse(new_return_to) - query = parseQuery(parsed[4]) - query.update({ - 'openid.mode':'id_res', - 'openid.return_to':new_return_to, - 'openid.identity':delegate_url, - 'openid.assoc_handle':fetcher.assoc_handle, - }) - - assoc = store.getAssociation(server_url, fetcher.assoc_handle) - - message = Message.fromPostArgs(query) - message = assoc.signMessage(message) - info = consumer.complete(message, request.endpoint, new_return_to) - assert info.status == SUCCESS, info.message - assert info.identity_url == user_url - - assert fetcher.num_assocs == 0 - run() - assert fetcher.num_assocs == 1 - - # Test that doing it again uses the existing association - run() - assert fetcher.num_assocs == 1 - - # Another association is created if we remove the existing one - store.removeAssociation(server_url, fetcher.assoc_handle) - run() - assert fetcher.num_assocs == 2 - - # Test that doing it again uses the existing association - run() - assert fetcher.num_assocs == 2 - -import unittest - -http_server_url = 'http://server.example.com/' -consumer_url = 'http://consumer.example.com/' -https_server_url = 'https://server.example.com/' - -class TestSuccess(unittest.TestCase, CatchLogs): - server_url = http_server_url - user_url = 'http://www.example.com/user.html' - delegate_url = 'http://consumer.example.com/user' - - def setUp(self): - CatchLogs.setUp(self) - self.links = '<link rel="openid.server" href="%s" />' % ( - self.server_url,) - - self.delegate_links = ('<link rel="openid.server" href="%s" />' - '<link rel="openid.delegate" href="%s" />') % ( - self.server_url, self.delegate_url) - - def tearDown(self): - CatchLogs.tearDown(self) - - def test_nodelegate(self): - _test_success(self.server_url, self.user_url, - self.user_url, self.links) - - def test_nodelegateImmediate(self): - _test_success(self.server_url, self.user_url, - self.user_url, self.links, True) - - def test_delegate(self): - _test_success(self.server_url, self.user_url, - self.delegate_url, self.delegate_links) - - def test_delegateImmediate(self): - _test_success(self.server_url, self.user_url, - self.delegate_url, self.delegate_links, True) - - -class TestSuccessHTTPS(TestSuccess): - server_url = https_server_url - - -class TestConstruct(unittest.TestCase): - def setUp(self): - self.store_sentinel = object() - - def test_construct(self): - oidc = GenericConsumer(self.store_sentinel) - self.failUnless(oidc.store is self.store_sentinel) - - def test_nostore(self): - self.failUnlessRaises(TypeError, GenericConsumer) - - -class TestIdRes(unittest.TestCase, CatchLogs): - consumer_class = GenericConsumer - - def setUp(self): - CatchLogs.setUp(self) - - self.store = memstore.MemoryStore() - self.consumer = self.consumer_class(self.store) - self.return_to = "nonny" - self.endpoint = OpenIDServiceEndpoint() - self.endpoint.claimed_id = self.consumer_id = "consu" - self.endpoint.server_url = self.server_url = "serlie" - self.endpoint.local_id = self.server_id = "sirod" - self.endpoint.type_uris = [OPENID_1_1_TYPE] - - def disableDiscoveryVerification(self): - """Set the discovery verification to a no-op for test cases in - which we don't care.""" - def dummyVerifyDiscover(_, endpoint): - return endpoint - self.consumer._verifyDiscoveryResults = dummyVerifyDiscover - - def disableReturnToChecking(self): - def checkReturnTo(unused1, unused2): - return True - self.consumer._checkReturnTo = checkReturnTo - complete = self.consumer.complete - def callCompleteWithoutReturnTo(message, endpoint): - return complete(message, endpoint, None) - self.consumer.complete = callCompleteWithoutReturnTo - -class TestIdResCheckSignature(TestIdRes): - def setUp(self): - TestIdRes.setUp(self) - self.assoc = GoodAssociation() - self.assoc.handle = "{not_dumb}" - self.store.storeAssociation(self.endpoint.server_url, self.assoc) - - self.message = Message.fromPostArgs({ - 'openid.mode': 'id_res', - 'openid.identity': '=example', - 'openid.sig': GOODSIG, - 'openid.assoc_handle': self.assoc.handle, - 'openid.signed': 'mode,identity,assoc_handle,signed', - 'frobboz': 'banzit', - }) - - - def test_sign(self): - # assoc_handle to assoc with good sig - self.consumer._idResCheckSignature(self.message, - self.endpoint.server_url) - - - def test_signFailsWithBadSig(self): - self.message.setArg(OPENID_NS, 'sig', 'BAD SIGNATURE') - self.failUnlessRaises( - ProtocolError, self.consumer._idResCheckSignature, - self.message, self.endpoint.server_url) - - - def test_stateless(self): - # assoc_handle missing assoc, consumer._checkAuth returns goodthings - self.message.setArg(OPENID_NS, "assoc_handle", "dumbHandle") - self.consumer._processCheckAuthResponse = ( - lambda response, server_url: True) - self.consumer._makeKVPost = lambda args, server_url: {} - self.consumer._idResCheckSignature(self.message, - self.endpoint.server_url) - - def test_statelessRaisesError(self): - # assoc_handle missing assoc, consumer._checkAuth returns goodthings - self.message.setArg(OPENID_NS, "assoc_handle", "dumbHandle") - self.consumer._checkAuth = lambda unused1, unused2: False - self.failUnlessRaises( - ProtocolError, self.consumer._idResCheckSignature, - self.message, self.endpoint.server_url) - - def test_stateless_noStore(self): - # assoc_handle missing assoc, consumer._checkAuth returns goodthings - self.message.setArg(OPENID_NS, "assoc_handle", "dumbHandle") - self.consumer.store = None - self.consumer._processCheckAuthResponse = ( - lambda response, server_url: True) - self.consumer._makeKVPost = lambda args, server_url: {} - self.consumer._idResCheckSignature(self.message, - self.endpoint.server_url) - - def test_statelessRaisesError_noStore(self): - # assoc_handle missing assoc, consumer._checkAuth returns goodthings - self.message.setArg(OPENID_NS, "assoc_handle", "dumbHandle") - self.consumer._checkAuth = lambda unused1, unused2: False - self.consumer.store = None - self.failUnlessRaises( - ProtocolError, self.consumer._idResCheckSignature, - self.message, self.endpoint.server_url) - - -class TestQueryFormat(TestIdRes): - def test_notAList(self): - # XXX: should be a Message object test, not a consumer test - - # Value should be a single string. If it's a list, it should generate - # an exception. - query = {'openid.mode': ['cancel']} - try: - r = Message.fromPostArgs(query) - except TypeError, err: - self.failUnless(str(err).find('values') != -1, err) - else: - self.fail("expected TypeError, got this instead: %s" % (r,)) - -class TestComplete(TestIdRes): - """Testing GenericConsumer.complete. - - Other TestIdRes subclasses test more specific aspects. - """ - - def test_setupNeededIdRes(self): - message = Message.fromOpenIDArgs({'mode': 'id_res'}) - setup_url_sentinel = object() - - def raiseSetupNeeded(msg): - self.failUnless(msg is message) - raise SetupNeededError(setup_url_sentinel) - - self.consumer._checkSetupNeeded = raiseSetupNeeded - - response = self.consumer.complete(message, None, None) - self.failUnlessEqual(SETUP_NEEDED, response.status) - self.failUnless(setup_url_sentinel is response.setup_url) - - def test_cancel(self): - message = Message.fromPostArgs({'openid.mode': 'cancel'}) - self.disableReturnToChecking() - r = self.consumer.complete(message, self.endpoint) - self.failUnlessEqual(r.status, CANCEL) - self.failUnless(r.identity_url == self.endpoint.claimed_id) - - def test_cancel_with_return_to(self): - message = Message.fromPostArgs({'openid.mode': 'cancel'}) - r = self.consumer.complete(message, self.endpoint, self.return_to) - self.failUnlessEqual(r.status, CANCEL) - self.failUnless(r.identity_url == self.endpoint.claimed_id) - - def test_error(self): - msg = 'an error message' - message = Message.fromPostArgs({'openid.mode': 'error', - 'openid.error': msg, - }) - self.disableReturnToChecking() - r = self.consumer.complete(message, self.endpoint) - self.failUnlessEqual(r.status, FAILURE) - self.failUnless(r.identity_url == self.endpoint.claimed_id) - self.failUnlessEqual(r.message, msg) - - def test_errorWithNoOptionalKeys(self): - msg = 'an error message' - contact = 'some contact info here' - message = Message.fromPostArgs({'openid.mode': 'error', - 'openid.error': msg, - 'openid.contact': contact, - }) - self.disableReturnToChecking() - r = self.consumer.complete(message, self.endpoint) - self.failUnlessEqual(r.status, FAILURE) - self.failUnless(r.identity_url == self.endpoint.claimed_id) - self.failUnless(r.contact == contact) - self.failUnless(r.reference is None) - self.failUnlessEqual(r.message, msg) - - def test_errorWithOptionalKeys(self): - msg = 'an error message' - contact = 'me' - reference = 'support ticket' - message = Message.fromPostArgs({'openid.mode': 'error', - 'openid.error': msg, 'openid.reference': reference, - 'openid.contact': contact, 'openid.ns': OPENID2_NS, - }) - r = self.consumer.complete(message, self.endpoint, None) - self.failUnlessEqual(r.status, FAILURE) - self.failUnless(r.identity_url == self.endpoint.claimed_id) - self.failUnless(r.contact == contact) - self.failUnless(r.reference == reference) - self.failUnlessEqual(r.message, msg) - - def test_noMode(self): - message = Message.fromPostArgs({}) - r = self.consumer.complete(message, self.endpoint, None) - self.failUnlessEqual(r.status, FAILURE) - self.failUnless(r.identity_url == self.endpoint.claimed_id) - - def test_idResMissingField(self): - # XXX - this test is passing, but not necessarily by what it - # is supposed to test for. status in FAILURE, but it's because - # *check_auth* failed, not because it's missing an arg, exactly. - message = Message.fromPostArgs({'openid.mode': 'id_res'}) - self.failUnlessRaises(ProtocolError, self.consumer._doIdRes, - message, self.endpoint, None) - - def test_idResURLMismatch(self): - class VerifiedError(Exception): pass - - def discoverAndVerify(claimed_id, _to_match_endpoints): - raise VerifiedError - - self.consumer._discoverAndVerify = discoverAndVerify - self.disableReturnToChecking() - - message = Message.fromPostArgs( - {'openid.mode': 'id_res', - 'openid.return_to': 'return_to (just anything)', - 'openid.identity': 'something wrong (not self.consumer_id)', - 'openid.assoc_handle': 'does not matter', - 'openid.sig': GOODSIG, - 'openid.signed': 'identity,return_to', - }) - self.consumer.store = GoodAssocStore() - - self.failUnlessRaises(VerifiedError, - self.consumer.complete, - message, self.endpoint) - - self.failUnlessLogMatches('Error attempting to use stored', - 'Attempting discovery') - -class TestCompleteMissingSig(unittest.TestCase, CatchLogs): - - def setUp(self): - self.store = GoodAssocStore() - self.consumer = GenericConsumer(self.store) - self.server_url = "http://idp.unittest/" - CatchLogs.setUp(self) - - claimed_id = 'bogus.claimed' - - self.message = Message.fromOpenIDArgs( - {'mode': 'id_res', - 'return_to': 'return_to (just anything)', - 'identity': claimed_id, - 'assoc_handle': 'does not matter', - 'sig': GOODSIG, - 'response_nonce': mkNonce(), - 'signed': 'identity,return_to,response_nonce,assoc_handle,claimed_id,op_endpoint', - 'claimed_id': claimed_id, - 'op_endpoint': self.server_url, - 'ns':OPENID2_NS, - }) - - self.endpoint = OpenIDServiceEndpoint() - self.endpoint.server_url = self.server_url - self.endpoint.claimed_id = claimed_id - self.consumer._checkReturnTo = lambda unused1, unused2 : True - - def tearDown(self): - CatchLogs.tearDown(self) - - - def test_idResMissingNoSigs(self): - def _vrfy(resp_msg, endpoint=None): - return endpoint - - self.consumer._verifyDiscoveryResults = _vrfy - r = self.consumer.complete(self.message, self.endpoint, None) - self.failUnlessSuccess(r) - - - def test_idResNoIdentity(self): - self.message.delArg(OPENID_NS, 'identity') - self.message.delArg(OPENID_NS, 'claimed_id') - self.endpoint.claimed_id = None - self.message.setArg(OPENID_NS, 'signed', 'return_to,response_nonce,assoc_handle,op_endpoint') - r = self.consumer.complete(self.message, self.endpoint, None) - self.failUnlessSuccess(r) - - - def test_idResMissingIdentitySig(self): - self.message.setArg(OPENID_NS, 'signed', 'return_to,response_nonce,assoc_handle,claimed_id') - r = self.consumer.complete(self.message, self.endpoint, None) - self.failUnlessEqual(r.status, FAILURE) - - - def test_idResMissingReturnToSig(self): - self.message.setArg(OPENID_NS, 'signed', 'identity,response_nonce,assoc_handle,claimed_id') - r = self.consumer.complete(self.message, self.endpoint, None) - self.failUnlessEqual(r.status, FAILURE) - - - def test_idResMissingAssocHandleSig(self): - self.message.setArg(OPENID_NS, 'signed', 'identity,response_nonce,return_to,claimed_id') - r = self.consumer.complete(self.message, self.endpoint, None) - self.failUnlessEqual(r.status, FAILURE) - - - def test_idResMissingClaimedIDSig(self): - self.message.setArg(OPENID_NS, 'signed', 'identity,response_nonce,return_to,assoc_handle') - r = self.consumer.complete(self.message, self.endpoint, None) - self.failUnlessEqual(r.status, FAILURE) - - - def failUnlessSuccess(self, response): - if response.status != SUCCESS: - self.fail("Non-successful response: %s" % (response,)) - - - -class TestCheckAuthResponse(TestIdRes, CatchLogs): - def setUp(self): - CatchLogs.setUp(self) - TestIdRes.setUp(self) - - def tearDown(self): - CatchLogs.tearDown(self) - - def _createAssoc(self): - issued = time.time() - lifetime = 1000 - assoc = association.Association( - 'handle', 'secret', issued, lifetime, 'HMAC-SHA1') - store = self.consumer.store - store.storeAssociation(self.server_url, assoc) - assoc2 = store.getAssociation(self.server_url) - self.failUnlessEqual(assoc, assoc2) - - def test_goodResponse(self): - """successful response to check_authentication""" - response = Message.fromOpenIDArgs({'is_valid':'true',}) - r = self.consumer._processCheckAuthResponse(response, self.server_url) - self.failUnless(r) - - def test_missingAnswer(self): - """check_authentication returns false when the server sends no answer""" - response = Message.fromOpenIDArgs({}) - r = self.consumer._processCheckAuthResponse(response, self.server_url) - self.failIf(r) - - def test_badResponse(self): - """check_authentication returns false when is_valid is false""" - response = Message.fromOpenIDArgs({'is_valid':'false',}) - r = self.consumer._processCheckAuthResponse(response, self.server_url) - self.failIf(r) - - def test_badResponseInvalidate(self): - """Make sure that the handle is invalidated when is_valid is false - - From "Verifying directly with the OpenID Provider":: - - If the OP responds with "is_valid" set to "true", and - "invalidate_handle" is present, the Relying Party SHOULD - NOT send further authentication requests with that handle. - """ - self._createAssoc() - response = Message.fromOpenIDArgs({ - 'is_valid':'false', - 'invalidate_handle':'handle', - }) - r = self.consumer._processCheckAuthResponse(response, self.server_url) - self.failIf(r) - self.failUnless( - self.consumer.store.getAssociation(self.server_url) is None) - - def test_invalidateMissing(self): - """invalidate_handle with a handle that is not present""" - response = Message.fromOpenIDArgs({ - 'is_valid':'true', - 'invalidate_handle':'missing', - }) - r = self.consumer._processCheckAuthResponse(response, self.server_url) - self.failUnless(r) - self.failUnlessLogMatches( - 'Received "invalidate_handle"' - ) - - def test_invalidateMissing_noStore(self): - """invalidate_handle with a handle that is not present""" - response = Message.fromOpenIDArgs({ - 'is_valid':'true', - 'invalidate_handle':'missing', - }) - self.consumer.store = None - r = self.consumer._processCheckAuthResponse(response, self.server_url) - self.failUnless(r) - self.failUnlessLogMatches( - 'Received "invalidate_handle"', - 'Unexpectedly got invalidate_handle without a store') - - def test_invalidatePresent(self): - """invalidate_handle with a handle that exists - - From "Verifying directly with the OpenID Provider":: - - If the OP responds with "is_valid" set to "true", and - "invalidate_handle" is present, the Relying Party SHOULD - NOT send further authentication requests with that handle. - """ - self._createAssoc() - response = Message.fromOpenIDArgs({ - 'is_valid':'true', - 'invalidate_handle':'handle', - }) - r = self.consumer._processCheckAuthResponse(response, self.server_url) - self.failUnless(r) - self.failUnless( - self.consumer.store.getAssociation(self.server_url) is None) - -class TestSetupNeeded(TestIdRes): - def failUnlessSetupNeeded(self, expected_setup_url, message): - try: - self.consumer._checkSetupNeeded(message) - except SetupNeededError, why: - self.failUnlessEqual(expected_setup_url, why.user_setup_url) - else: - self.fail("Expected to find an immediate-mode response") - - def test_setupNeededOpenID1(self): - """The minimum conditions necessary to trigger Setup Needed""" - setup_url = 'http://unittest/setup-here' - message = Message.fromPostArgs({ - 'openid.mode': 'id_res', - 'openid.user_setup_url': setup_url, - }) - self.failUnless(message.isOpenID1()) - self.failUnlessSetupNeeded(setup_url, message) - - def test_setupNeededOpenID1_extra(self): - """Extra stuff along with setup_url still trigger Setup Needed""" - setup_url = 'http://unittest/setup-here' - message = Message.fromPostArgs({ - 'openid.mode': 'id_res', - 'openid.user_setup_url': setup_url, - 'openid.identity': 'bogus', - }) - self.failUnless(message.isOpenID1()) - self.failUnlessSetupNeeded(setup_url, message) - - def test_noSetupNeededOpenID1(self): - """When the user_setup_url is missing on an OpenID 1 message, - we assume that it's not a cancel response to checkid_immediate""" - message = Message.fromOpenIDArgs({'mode': 'id_res'}) - self.failUnless(message.isOpenID1()) - - # No SetupNeededError raised - self.consumer._checkSetupNeeded(message) - - def test_setupNeededOpenID2(self): - message = Message.fromOpenIDArgs({ - 'mode':'setup_needed', - 'ns':OPENID2_NS, - }) - self.failUnless(message.isOpenID2()) - response = self.consumer.complete(message, None, None) - self.failUnlessEqual('setup_needed', response.status) - self.failUnlessEqual(None, response.setup_url) - - def test_setupNeededDoesntWorkForOpenID1(self): - message = Message.fromOpenIDArgs({ - 'mode':'setup_needed', - }) - - # No SetupNeededError raised - self.consumer._checkSetupNeeded(message) - - response = self.consumer.complete(message, None, None) - self.failUnlessEqual('failure', response.status) - self.failUnless(response.message.startswith('Invalid openid.mode')) - - def test_noSetupNeededOpenID2(self): - message = Message.fromOpenIDArgs({ - 'mode':'id_res', - 'game':'puerto_rico', - 'ns':OPENID2_NS, - }) - self.failUnless(message.isOpenID2()) - - # No SetupNeededError raised - self.consumer._checkSetupNeeded(message) - -class IdResCheckForFieldsTest(TestIdRes): - def setUp(self): - self.consumer = GenericConsumer(None) - - def mkSuccessTest(openid_args, signed_list): - def test(self): - message = Message.fromOpenIDArgs(openid_args) - message.setArg(OPENID_NS, 'signed', ','.join(signed_list)) - self.consumer._idResCheckForFields(message) - return test - - test_openid1Success = mkSuccessTest( - {'return_to':'return', - 'assoc_handle':'assoc handle', - 'sig':'a signature', - 'identity':'someone', - }, - ['return_to', 'identity']) - - test_openid2Success = mkSuccessTest( - {'ns':OPENID2_NS, - 'return_to':'return', - 'assoc_handle':'assoc handle', - 'sig':'a signature', - 'op_endpoint':'my favourite server', - 'response_nonce':'use only once', - }, - ['return_to', 'response_nonce', 'assoc_handle', 'op_endpoint']) - - test_openid2Success_identifiers = mkSuccessTest( - {'ns':OPENID2_NS, - 'return_to':'return', - 'assoc_handle':'assoc handle', - 'sig':'a signature', - 'claimed_id':'i claim to be me', - 'identity':'my server knows me as me', - 'op_endpoint':'my favourite server', - 'response_nonce':'use only once', - }, - ['return_to', 'response_nonce', 'identity', - 'claimed_id', 'assoc_handle', 'op_endpoint']) - - def mkMissingFieldTest(openid_args): - def test(self): - message = Message.fromOpenIDArgs(openid_args) - try: - self.consumer._idResCheckForFields(message) - except ProtocolError, why: - self.failUnless(why[0].startswith('Missing required')) - else: - self.fail('Expected an error, but none occurred') - return test - - def mkMissingSignedTest(openid_args): - def test(self): - message = Message.fromOpenIDArgs(openid_args) - try: - self.consumer._idResCheckForFields(message) - except ProtocolError, why: - self.failUnless(why[0].endswith('not signed')) - else: - self.fail('Expected an error, but none occurred') - return test - - test_openid1Missing_returnToSig = mkMissingSignedTest( - {'return_to':'return', - 'assoc_handle':'assoc handle', - 'sig':'a signature', - 'identity':'someone', - 'signed':'identity', - }) - - test_openid1Missing_identitySig = mkMissingSignedTest( - {'return_to':'return', - 'assoc_handle':'assoc handle', - 'sig':'a signature', - 'identity':'someone', - 'signed':'return_to' - }) - - test_openid2Missing_opEndpointSig = mkMissingSignedTest( - {'ns':OPENID2_NS, - 'return_to':'return', - 'assoc_handle':'assoc handle', - 'sig':'a signature', - 'identity':'someone', - 'op_endpoint':'the endpoint', - 'signed':'return_to,identity,assoc_handle' - }) - - test_openid1MissingReturnTo = mkMissingFieldTest( - {'assoc_handle':'assoc handle', - 'sig':'a signature', - 'identity':'someone', - }) - - test_openid1MissingAssocHandle = mkMissingFieldTest( - {'return_to':'return', - 'sig':'a signature', - 'identity':'someone', - }) - - # XXX: I could go on... - -class CheckAuthHappened(Exception): pass - -class CheckNonceVerifyTest(TestIdRes, CatchLogs): - def setUp(self): - CatchLogs.setUp(self) - TestIdRes.setUp(self) - self.consumer.openid1_nonce_query_arg_name = 'nonce' - - def tearDown(self): - CatchLogs.tearDown(self) - - def test_openid1Success(self): - """use consumer-generated nonce""" - nonce_value = mkNonce() - self.return_to = 'http://rt.unittest/?nonce=%s' % (nonce_value,) - self.response = Message.fromOpenIDArgs({'return_to': self.return_to}) - self.response.setArg(BARE_NS, 'nonce', nonce_value) - self.consumer._idResCheckNonce(self.response, self.endpoint) - self.failUnlessLogEmpty() - - def test_openid1Missing(self): - """use consumer-generated nonce""" - self.response = Message.fromOpenIDArgs({}) - n = self.consumer._idResGetNonceOpenID1(self.response, self.endpoint) - self.failUnless(n is None, n) - self.failUnlessLogEmpty() - - def test_consumerNonceOpenID2(self): - """OpenID 2 does not use consumer-generated nonce""" - self.return_to = 'http://rt.unittest/?nonce=%s' % (mkNonce(),) - self.response = Message.fromOpenIDArgs( - {'return_to': self.return_to, 'ns':OPENID2_NS}) - self.failUnlessRaises(ProtocolError, self.consumer._idResCheckNonce, - self.response, self.endpoint) - self.failUnlessLogEmpty() - - def test_serverNonce(self): - """use server-generated nonce""" - self.response = Message.fromOpenIDArgs( - {'ns':OPENID2_NS, 'response_nonce': mkNonce(),}) - self.consumer._idResCheckNonce(self.response, self.endpoint) - self.failUnlessLogEmpty() - - def test_serverNonceOpenID1(self): - """OpenID 1 does not use server-generated nonce""" - self.response = Message.fromOpenIDArgs( - {'ns':OPENID1_NS, - 'return_to': 'http://return.to/', - 'response_nonce': mkNonce(),}) - self.failUnlessRaises(ProtocolError, self.consumer._idResCheckNonce, - self.response, self.endpoint) - self.failUnlessLogEmpty() - - def test_badNonce(self): - """remove the nonce from the store - - From "Checking the Nonce":: - - When the Relying Party checks the signature on an assertion, the - - Relying Party SHOULD ensure that an assertion has not yet - been accepted with the same value for "openid.response_nonce" - from the same OP Endpoint URL. - """ - nonce = mkNonce() - stamp, salt = splitNonce(nonce) - self.store.useNonce(self.server_url, stamp, salt) - self.response = Message.fromOpenIDArgs( - {'response_nonce': nonce, - 'ns':OPENID2_NS, - }) - self.failUnlessRaises(ProtocolError, self.consumer._idResCheckNonce, - self.response, self.endpoint) - - def test_successWithNoStore(self): - """When there is no store, checking the nonce succeeds""" - self.consumer.store = None - self.response = Message.fromOpenIDArgs( - {'response_nonce': mkNonce(), - 'ns':OPENID2_NS, - }) - self.consumer._idResCheckNonce(self.response, self.endpoint) - self.failUnlessLogEmpty() - - def test_tamperedNonce(self): - """Malformed nonce""" - self.response = Message.fromOpenIDArgs( - {'ns':OPENID2_NS, - 'response_nonce':'malformed'}) - self.failUnlessRaises(ProtocolError, self.consumer._idResCheckNonce, - self.response, self.endpoint) - - def test_missingNonce(self): - """no nonce parameter on the return_to""" - self.response = Message.fromOpenIDArgs( - {'return_to': self.return_to}) - self.failUnlessRaises(ProtocolError, self.consumer._idResCheckNonce, - self.response, self.endpoint) - -class CheckAuthDetectingConsumer(GenericConsumer): - def _checkAuth(self, *args): - raise CheckAuthHappened(args) - - def _idResCheckNonce(self, *args): - """We're not testing nonce-checking, so just return success - when it asks.""" - return True - -class TestCheckAuthTriggered(TestIdRes, CatchLogs): - consumer_class = CheckAuthDetectingConsumer - - def setUp(self): - TestIdRes.setUp(self) - CatchLogs.setUp(self) - self.disableDiscoveryVerification() - - def test_checkAuthTriggered(self): - message = Message.fromPostArgs({ - 'openid.return_to':self.return_to, - 'openid.identity':self.server_id, - 'openid.assoc_handle':'not_found', - 'openid.sig': GOODSIG, - 'openid.signed': 'identity,return_to', - }) - self.disableReturnToChecking() - try: - result = self.consumer._doIdRes(message, self.endpoint, None) - except CheckAuthHappened: - pass - else: - self.fail('_checkAuth did not happen. Result was: %r %s' % - (result, self.messages)) - - def test_checkAuthTriggeredWithAssoc(self): - # Store an association for this server that does not match the - # handle that is in the message - issued = time.time() - lifetime = 1000 - assoc = association.Association( - 'handle', 'secret', issued, lifetime, 'HMAC-SHA1') - self.store.storeAssociation(self.server_url, assoc) - self.disableReturnToChecking() - message = Message.fromPostArgs({ - 'openid.return_to':self.return_to, - 'openid.identity':self.server_id, - 'openid.assoc_handle':'not_found', - 'openid.sig': GOODSIG, - 'openid.signed': 'identity,return_to', - }) - try: - result = self.consumer._doIdRes(message, self.endpoint, None) - except CheckAuthHappened: - pass - else: - self.fail('_checkAuth did not happen. Result was: %r' % (result,)) - - def test_expiredAssoc(self): - # Store an expired association for the server with the handle - # that is in the message - issued = time.time() - 10 - lifetime = 0 - handle = 'handle' - assoc = association.Association( - handle, 'secret', issued, lifetime, 'HMAC-SHA1') - self.failUnless(assoc.expiresIn <= 0) - self.store.storeAssociation(self.server_url, assoc) - - message = Message.fromPostArgs({ - 'openid.return_to':self.return_to, - 'openid.identity':self.server_id, - 'openid.assoc_handle':handle, - 'openid.sig': GOODSIG, - 'openid.signed': 'identity,return_to', - }) - self.disableReturnToChecking() - self.failUnlessRaises(ProtocolError, self.consumer._doIdRes, - message, self.endpoint, None) - - def test_newerAssoc(self): - lifetime = 1000 - - good_issued = time.time() - 10 - good_handle = 'handle' - good_assoc = association.Association( - good_handle, 'secret', good_issued, lifetime, 'HMAC-SHA1') - self.store.storeAssociation(self.server_url, good_assoc) - - bad_issued = time.time() - 5 - bad_handle = 'handle2' - bad_assoc = association.Association( - bad_handle, 'secret', bad_issued, lifetime, 'HMAC-SHA1') - self.store.storeAssociation(self.server_url, bad_assoc) - - query = { - 'return_to':self.return_to, - 'identity':self.server_id, - 'assoc_handle':good_handle, - } - - message = Message.fromOpenIDArgs(query) - message = good_assoc.signMessage(message) - self.disableReturnToChecking() - info = self.consumer._doIdRes(message, self.endpoint, None) - self.failUnlessEqual(info.status, SUCCESS, info.message) - self.failUnlessEqual(self.consumer_id, info.identity_url) - - - -class TestReturnToArgs(unittest.TestCase): - """Verifying the Return URL paramaters. - From the specification "Verifying the Return URL":: - - To verify that the "openid.return_to" URL matches the URL that is - processing this assertion: - - - The URL scheme, authority, and path MUST be the same between the - two URLs. - - - Any query parameters that are present in the "openid.return_to" - URL MUST also be present with the same values in the - accepting URL. - - XXX: So far we have only tested the second item on the list above. - XXX: _verifyReturnToArgs is not invoked anywhere. - """ - - def setUp(self): - store = object() - self.consumer = GenericConsumer(store) - - def test_returnToArgsOkay(self): - query = { - 'openid.mode': 'id_res', - 'openid.return_to': 'http://example.com/?foo=bar', - 'foo': 'bar', - } - # no return value, success is assumed if there are no exceptions. - self.consumer._verifyReturnToArgs(query) - - def test_returnToArgsUnexpectedArg(self): - query = { - 'openid.mode': 'id_res', - 'openid.return_to': 'http://example.com/', - 'foo': 'bar', - } - # no return value, success is assumed if there are no exceptions. - self.failUnlessRaises(ProtocolError, - self.consumer._verifyReturnToArgs, query) - - def test_returnToMismatch(self): - query = { - 'openid.mode': 'id_res', - 'openid.return_to': 'http://example.com/?foo=bar', - } - # fail, query has no key 'foo'. - self.failUnlessRaises(ValueError, - self.consumer._verifyReturnToArgs, query) - - query['foo'] = 'baz' - # fail, values for 'foo' do not match. - self.failUnlessRaises(ValueError, - self.consumer._verifyReturnToArgs, query) - - - def test_noReturnTo(self): - query = {'openid.mode': 'id_res'} - self.failUnlessRaises(ValueError, - self.consumer._verifyReturnToArgs, query) - - def test_completeBadReturnTo(self): - """Test GenericConsumer.complete()'s handling of bad return_to - values. - """ - return_to = "http://some.url/path?foo=bar" - - # Scheme, authority, and path differences are checked by - # GenericConsumer._checkReturnTo. Query args checked by - # GenericConsumer._verifyReturnToArgs. - bad_return_tos = [ - # Scheme only - "https://some.url/path?foo=bar", - # Authority only - "http://some.url.invalid/path?foo=bar", - # Path only - "http://some.url/path_extra?foo=bar", - # Query args differ - "http://some.url/path?foo=bar2", - "http://some.url/path?foo2=bar", - ] - - m = Message(OPENID1_NS) - m.setArg(OPENID_NS, 'mode', 'cancel') - m.setArg(BARE_NS, 'foo', 'bar') - endpoint = None - - for bad in bad_return_tos: - m.setArg(OPENID_NS, 'return_to', bad) - self.failIf(self.consumer._checkReturnTo(m, return_to)) - - def test_completeGoodReturnTo(self): - """Test GenericConsumer.complete()'s handling of good - return_to values. - """ - return_to = "http://some.url/path" - - good_return_tos = [ - (return_to, {}), - (return_to + "?another=arg", {(BARE_NS, 'another'): 'arg'}), - (return_to + "?another=arg#fragment", {(BARE_NS, 'another'): 'arg'}), - ("HTTP"+return_to[4:], {}), - (return_to.replace('url','URL'), {}), - ("http://some.url:80/path", {}), - ("http://some.url/p%61th", {}), - ("http://some.url/./path", {}), - ] - - endpoint = None - - for good, extra in good_return_tos: - m = Message(OPENID1_NS) - m.setArg(OPENID_NS, 'mode', 'cancel') - - for ns, key in extra: - m.setArg(ns, key, extra[(ns, key)]) - - m.setArg(OPENID_NS, 'return_to', good) - result = self.consumer.complete(m, endpoint, return_to) - self.failUnless(isinstance(result, CancelResponse), \ - "Expected CancelResponse, got %r for %s" % (result, good,)) - -class MockFetcher(object): - def __init__(self, response=None): - self.response = response or HTTPResponse() - self.fetches = [] - - def fetch(self, url, body=None, headers=None): - self.fetches.append((url, body, headers)) - return self.response - -class ExceptionRaisingMockFetcher(object): - class MyException(Exception): - pass - - def fetch(self, url, body=None, headers=None): - raise self.MyException('mock fetcher exception') - -class BadArgCheckingConsumer(GenericConsumer): - def _makeKVPost(self, args, _): - assert args == { - 'openid.mode':'check_authentication', - 'openid.signed':'foo', - 'openid.ns':OPENID1_NS - }, args - return None - -class TestCheckAuth(unittest.TestCase, CatchLogs): - consumer_class = GenericConsumer - - def setUp(self): - CatchLogs.setUp(self) - self.store = memstore.MemoryStore() - - self.consumer = self.consumer_class(self.store) - - self._orig_fetcher = fetchers.getDefaultFetcher() - self.fetcher = MockFetcher() - fetchers.setDefaultFetcher(self.fetcher) - - def tearDown(self): - CatchLogs.tearDown(self) - fetchers.setDefaultFetcher(self._orig_fetcher, wrap_exceptions=False) - - def test_error(self): - self.fetcher.response = HTTPResponse( - "http://some_url", 404, {'Hea': 'der'}, 'blah:blah\n') - query = {'openid.signed': 'stuff', - 'openid.stuff':'a value'} - r = self.consumer._checkAuth(Message.fromPostArgs(query), - http_server_url) - self.failIf(r) - self.failUnless(self.messages) - - def test_bad_args(self): - query = { - 'openid.signed':'foo', - 'closid.foo':'something', - } - consumer = BadArgCheckingConsumer(self.store) - consumer._checkAuth(Message.fromPostArgs(query), 'does://not.matter') - - - def test_signedList(self): - query = Message.fromOpenIDArgs({ - 'mode': 'id_res', - 'sig': 'rabbits', - 'identity': '=example', - 'assoc_handle': 'munchkins', - 'ns.sreg': 'urn:sreg', - 'sreg.email': 'bogus@example.com', - 'signed': 'identity,mode,ns.sreg,sreg.email', - 'foo': 'bar', - }) - args = self.consumer._createCheckAuthRequest(query) - self.failUnless(args.isOpenID1()) - for signed_arg in query.getArg(OPENID_NS, 'signed').split(','): - self.failUnless(args.getAliasedArg(signed_arg), signed_arg) - - def test_112(self): - args = {'openid.assoc_handle': 'fa1f5ff0-cde4-11dc-a183-3714bfd55ca8', - 'openid.claimed_id': 'http://binkley.lan/user/test01', - 'openid.identity': 'http://test01.binkley.lan/', - 'openid.mode': 'id_res', - 'openid.ns': 'http://specs.openid.net/auth/2.0', - 'openid.ns.pape': 'http://specs.openid.net/extensions/pape/1.0', - 'openid.op_endpoint': 'http://binkley.lan/server', - 'openid.pape.auth_policies': 'none', - 'openid.pape.auth_time': '2008-01-28T20:42:36Z', - 'openid.pape.nist_auth_level': '0', - 'openid.response_nonce': '2008-01-28T21:07:04Z99Q=', - 'openid.return_to': 'http://binkley.lan:8001/process?janrain_nonce=2008-01-28T21%3A07%3A02Z0tMIKx', - 'openid.sig': 'YJlWH4U6SroB1HoPkmEKx9AyGGg=', - 'openid.signed': 'assoc_handle,identity,response_nonce,return_to,claimed_id,op_endpoint,pape.auth_time,ns.pape,pape.nist_auth_level,pape.auth_policies' - } - self.failUnlessEqual(OPENID2_NS, args['openid.ns']) - incoming = Message.fromPostArgs(args) - self.failUnless(incoming.isOpenID2()) - car = self.consumer._createCheckAuthRequest(incoming) - expected_args = args.copy() - expected_args['openid.mode'] = 'check_authentication' - expected =Message.fromPostArgs(expected_args) - self.failUnless(expected.isOpenID2()) - self.failUnlessEqual(expected, car) - self.failUnlessEqual(expected_args, car.toPostArgs()) - - - -class TestFetchAssoc(unittest.TestCase, CatchLogs): - consumer_class = GenericConsumer - - def setUp(self): - CatchLogs.setUp(self) - self.store = memstore.MemoryStore() - self.fetcher = MockFetcher() - fetchers.setDefaultFetcher(self.fetcher) - self.consumer = self.consumer_class(self.store) - - def test_error_404(self): - """404 from a kv post raises HTTPFetchingError""" - self.fetcher.response = HTTPResponse( - "http://some_url", 404, {'Hea': 'der'}, 'blah:blah\n') - self.failUnlessRaises( - fetchers.HTTPFetchingError, - self.consumer._makeKVPost, - Message.fromPostArgs({'mode':'associate'}), - "http://server_url") - - def test_error_exception_unwrapped(self): - """Ensure that exceptions are bubbled through from fetchers - when making associations - """ - self.fetcher = ExceptionRaisingMockFetcher() - fetchers.setDefaultFetcher(self.fetcher, wrap_exceptions=False) - self.failUnlessRaises(self.fetcher.MyException, - self.consumer._makeKVPost, - Message.fromPostArgs({'mode':'associate'}), - "http://server_url") - - # exception fetching returns no association - e = OpenIDServiceEndpoint() - e.server_url = 'some://url' - self.failUnlessRaises(self.fetcher.MyException, - self.consumer._getAssociation, e) - - self.failUnlessRaises(self.fetcher.MyException, - self.consumer._checkAuth, - Message.fromPostArgs({'openid.signed':''}), - 'some://url') - - def test_error_exception_wrapped(self): - """Ensure that openid.fetchers.HTTPFetchingError is caught by - the association creation stuff. - """ - self.fetcher = ExceptionRaisingMockFetcher() - # This will wrap exceptions! - fetchers.setDefaultFetcher(self.fetcher) - self.failUnlessRaises(fetchers.HTTPFetchingError, - self.consumer._makeKVPost, - Message.fromOpenIDArgs({'mode':'associate'}), - "http://server_url") - - # exception fetching returns no association - e = OpenIDServiceEndpoint() - e.server_url = 'some://url' - self.failUnless(self.consumer._getAssociation(e) is None) - - msg = Message.fromPostArgs({'openid.signed':''}) - self.failIf(self.consumer._checkAuth(msg, 'some://url')) - - -class TestSuccessResponse(unittest.TestCase): - def setUp(self): - self.endpoint = OpenIDServiceEndpoint() - self.endpoint.claimed_id = 'identity_url' - - def test_extensionResponse(self): - resp = mkSuccess(self.endpoint, { - 'ns.sreg':'urn:sreg', - 'ns.unittest':'urn:unittest', - 'unittest.one':'1', - 'unittest.two':'2', - 'sreg.nickname':'j3h', - 'return_to':'return_to', - }) - utargs = resp.extensionResponse('urn:unittest', False) - self.failUnlessEqual(utargs, {'one':'1', 'two':'2'}) - sregargs = resp.extensionResponse('urn:sreg', False) - self.failUnlessEqual(sregargs, {'nickname':'j3h'}) - - def test_extensionResponseSigned(self): - args = { - 'ns.sreg':'urn:sreg', - 'ns.unittest':'urn:unittest', - 'unittest.one':'1', - 'unittest.two':'2', - 'sreg.nickname':'j3h', - 'sreg.dob':'yesterday', - 'return_to':'return_to', - 'signed': 'sreg.nickname,unittest.one,sreg.dob', - } - - signed_list = ['openid.sreg.nickname', - 'openid.unittest.one', - 'openid.sreg.dob',] - - # Don't use mkSuccess because it creates an all-inclusive - # signed list. - msg = Message.fromOpenIDArgs(args) - resp = SuccessResponse(self.endpoint, msg, signed_list) - - # All args in this NS are signed, so expect all. - sregargs = resp.extensionResponse('urn:sreg', True) - self.failUnlessEqual(sregargs, {'nickname':'j3h', 'dob': 'yesterday'}) - - # Not all args in this NS are signed, so expect None when - # asking for them. - utargs = resp.extensionResponse('urn:unittest', True) - self.failUnlessEqual(utargs, None) - - def test_noReturnTo(self): - resp = mkSuccess(self.endpoint, {}) - self.failUnless(resp.getReturnTo() is None) - - def test_returnTo(self): - resp = mkSuccess(self.endpoint, {'return_to':'return_to'}) - self.failUnlessEqual(resp.getReturnTo(), 'return_to') - - def test_displayIdentifierClaimedId(self): - resp = mkSuccess(self.endpoint, {}) - self.failUnlessEqual(resp.getDisplayIdentifier(), - resp.endpoint.claimed_id) - - def test_displayIdentifierOverride(self): - self.endpoint.display_identifier = "http://input.url/" - resp = mkSuccess(self.endpoint, {}) - self.failUnlessEqual(resp.getDisplayIdentifier(), - "http://input.url/") - -class StubConsumer(object): - def __init__(self): - self.assoc = object() - self.response = None - self.endpoint = None - - def begin(self, service): - auth_req = AuthRequest(service, self.assoc) - self.endpoint = service - return auth_req - - def complete(self, message, endpoint, return_to): - assert endpoint is self.endpoint - return self.response - -class ConsumerTest(unittest.TestCase): - """Tests for high-level consumer.Consumer functions. - - Its GenericConsumer component is stubbed out with StubConsumer. - """ - def setUp(self): - self.endpoint = OpenIDServiceEndpoint() - self.endpoint.claimed_id = self.identity_url = 'http://identity.url/' - self.store = None - self.session = {} - self.consumer = Consumer(self.session, self.store) - self.consumer.consumer = StubConsumer() - self.discovery = Discovery(self.session, - self.identity_url, - self.consumer.session_key_prefix) - - def test_setAssociationPreference(self): - self.consumer.setAssociationPreference([]) - self.failUnless(isinstance(self.consumer.consumer.negotiator, - association.SessionNegotiator)) - self.failUnlessEqual([], - self.consumer.consumer.negotiator.allowed_types) - self.consumer.setAssociationPreference([('HMAC-SHA1', 'DH-SHA1')]) - self.failUnlessEqual([('HMAC-SHA1', 'DH-SHA1')], - self.consumer.consumer.negotiator.allowed_types) - - def withDummyDiscovery(self, callable, dummy_getNextService): - class DummyDisco(object): - def __init__(self, *ignored): - pass - - getNextService = dummy_getNextService - - from askbot.deps.openid.consumer import consumer - old_discovery = consumer.Discovery - try: - consumer.Discovery = DummyDisco - callable() - finally: - consumer.Discovery = old_discovery - - def test_beginHTTPError(self): - """Make sure that the discovery HTTP failure case behaves properly - """ - def getNextService(self, ignored): - raise HTTPFetchingError("Unit test") - - def test(): - try: - self.consumer.begin('unused in this test') - except DiscoveryFailure, why: - self.failUnless(why[0].startswith('Error fetching')) - self.failIf(why[0].find('Unit test') == -1) - else: - self.fail('Expected DiscoveryFailure') - - self.withDummyDiscovery(test, getNextService) - - def test_beginNoServices(self): - def getNextService(self, ignored): - return None - - url = 'http://a.user.url/' - def test(): - try: - self.consumer.begin(url) - except DiscoveryFailure, why: - self.failUnless(why[0].startswith('No usable OpenID')) - self.failIf(why[0].find(url) == -1) - else: - self.fail('Expected DiscoveryFailure') - - self.withDummyDiscovery(test, getNextService) - - - def test_beginWithoutDiscovery(self): - # Does this really test anything non-trivial? - result = self.consumer.beginWithoutDiscovery(self.endpoint) - - # The result is an auth request - self.failUnless(isinstance(result, AuthRequest)) - - # Side-effect of calling beginWithoutDiscovery is setting the - # session value to the endpoint attribute of the result - self.failUnless(self.session[self.consumer._token_key] is result.endpoint) - - # The endpoint that we passed in is the endpoint on the auth_request - self.failUnless(result.endpoint is self.endpoint) - - def test_completeEmptySession(self): - text = "failed complete" - - def checkEndpoint(message, endpoint, return_to): - self.failUnless(endpoint is None) - return FailureResponse(endpoint, text) - - self.consumer.consumer.complete = checkEndpoint - - response = self.consumer.complete({}, None) - self.failUnlessEqual(response.status, FAILURE) - self.failUnlessEqual(response.message, text) - self.failUnless(response.identity_url is None) - - def _doResp(self, auth_req, exp_resp): - """complete a transaction, using the expected response from - the generic consumer.""" - # response is an attribute of StubConsumer, returned by - # StubConsumer.complete. - self.consumer.consumer.response = exp_resp - - # endpoint is stored in the session - self.failUnless(self.session) - resp = self.consumer.complete({}, None) - - # All responses should have the same identity URL, and the - # session should be cleaned out - if self.endpoint.claimed_id != IDENTIFIER_SELECT: - self.failUnless(resp.identity_url is self.identity_url) - - self.failIf(self.consumer._token_key in self.session) - - # Expected status response - self.failUnlessEqual(resp.status, exp_resp.status) - - return resp - - def _doRespNoDisco(self, exp_resp): - """Set up a transaction without discovery""" - auth_req = self.consumer.beginWithoutDiscovery(self.endpoint) - resp = self._doResp(auth_req, exp_resp) - # There should be nothing left in the session once we have completed. - self.failIf(self.session) - return resp - - def test_noDiscoCompleteSuccessWithToken(self): - self._doRespNoDisco(mkSuccess(self.endpoint, {})) - - def test_noDiscoCompleteCancelWithToken(self): - self._doRespNoDisco(CancelResponse(self.endpoint)) - - def test_noDiscoCompleteFailure(self): - msg = 'failed!' - resp = self._doRespNoDisco(FailureResponse(self.endpoint, msg)) - self.failUnless(resp.message is msg) - - def test_noDiscoCompleteSetupNeeded(self): - setup_url = 'http://setup.url/' - resp = self._doRespNoDisco( - SetupNeededResponse(self.endpoint, setup_url)) - self.failUnless(resp.setup_url is setup_url) - - # To test that discovery is cleaned up, we need to initialize a - # Yadis manager, and have it put its values in the session. - def _doRespDisco(self, is_clean, exp_resp): - """Set up and execute a transaction, with discovery""" - self.discovery.createManager([self.endpoint], self.identity_url) - auth_req = self.consumer.begin(self.identity_url) - resp = self._doResp(auth_req, exp_resp) - - manager = self.discovery.getManager() - if is_clean: - self.failUnless(self.discovery.getManager() is None, manager) - else: - self.failIf(self.discovery.getManager() is None, manager) - - return resp - - # Cancel and success DO clean up the discovery process - def test_completeSuccess(self): - self._doRespDisco(True, mkSuccess(self.endpoint, {})) - - def test_completeCancel(self): - self._doRespDisco(True, CancelResponse(self.endpoint)) - - # Failure and setup_needed don't clean up the discovery process - def test_completeFailure(self): - msg = 'failed!' - resp = self._doRespDisco(False, FailureResponse(self.endpoint, msg)) - self.failUnless(resp.message is msg) - - def test_completeSetupNeeded(self): - setup_url = 'http://setup.url/' - resp = self._doRespDisco( - False, - SetupNeededResponse(self.endpoint, setup_url)) - self.failUnless(resp.setup_url is setup_url) - - def test_successDifferentURL(self): - """ - Be sure that the session gets cleaned up when the response is - successful and has a different URL than the one in the - request. - """ - # Set up a request endpoint describing an IDP URL - self.identity_url = 'http://idp.url/' - self.endpoint.claimed_id = self.endpoint.local_id = IDENTIFIER_SELECT - - # Use a response endpoint with a different URL (asserted by - # the IDP) - resp_endpoint = OpenIDServiceEndpoint() - resp_endpoint.claimed_id = "http://user.url/" - - resp = self._doRespDisco( - True, - mkSuccess(resp_endpoint, {})) - self.failUnless(self.discovery.getManager(force=True) is None) - - def test_begin(self): - self.discovery.createManager([self.endpoint], self.identity_url) - # Should not raise an exception - auth_req = self.consumer.begin(self.identity_url) - self.failUnless(isinstance(auth_req, AuthRequest)) - self.failUnless(auth_req.endpoint is self.endpoint) - self.failUnless(auth_req.endpoint is self.consumer.consumer.endpoint) - self.failUnless(auth_req.assoc is self.consumer.consumer.assoc) - - - -class IDPDrivenTest(unittest.TestCase): - - def setUp(self): - self.store = GoodAssocStore() - self.consumer = GenericConsumer(self.store) - self.endpoint = OpenIDServiceEndpoint() - self.endpoint.server_url = "http://idp.unittest/" - - - def test_idpDrivenBegin(self): - # Testing here that the token-handling doesn't explode... - self.consumer.begin(self.endpoint) - - - def test_idpDrivenComplete(self): - identifier = '=directed_identifier' - message = Message.fromPostArgs({ - 'openid.identity': '=directed_identifier', - 'openid.return_to': 'x', - 'openid.assoc_handle': 'z', - 'openid.signed': 'identity,return_to', - 'openid.sig': GOODSIG, - }) - - discovered_endpoint = OpenIDServiceEndpoint() - discovered_endpoint.claimed_id = identifier - discovered_endpoint.server_url = self.endpoint.server_url - discovered_endpoint.local_id = identifier - iverified = [] - def verifyDiscoveryResults(identifier, endpoint): - self.failUnless(endpoint is self.endpoint) - iverified.append(discovered_endpoint) - return discovered_endpoint - self.consumer._verifyDiscoveryResults = verifyDiscoveryResults - self.consumer._idResCheckNonce = lambda *args: True - self.consumer._checkReturnTo = lambda unused1, unused2 : True - response = self.consumer._doIdRes(message, self.endpoint, None) - - self.failUnlessSuccess(response) - self.failUnlessEqual(response.identity_url, "=directed_identifier") - - # assert that discovery attempt happens and returns good - self.failUnlessEqual(iverified, [discovered_endpoint]) - - - def test_idpDrivenCompleteFraud(self): - # crap with an identifier that doesn't match discovery info - message = Message.fromPostArgs({ - 'openid.identity': '=directed_identifier', - 'openid.return_to': 'x', - 'openid.assoc_handle': 'z', - 'openid.signed': 'identity,return_to', - 'openid.sig': GOODSIG, - }) - def verifyDiscoveryResults(identifier, endpoint): - raise DiscoveryFailure("PHREAK!", None) - self.consumer._verifyDiscoveryResults = verifyDiscoveryResults - self.consumer._checkReturnTo = lambda unused1, unused2 : True - self.failUnlessRaises(DiscoveryFailure, self.consumer._doIdRes, - message, self.endpoint, None) - - - def failUnlessSuccess(self, response): - if response.status != SUCCESS: - self.fail("Non-successful response: %s" % (response,)) - - - -class TestDiscoveryVerification(unittest.TestCase): - services = [] - - def setUp(self): - self.store = GoodAssocStore() - self.consumer = GenericConsumer(self.store) - - self.consumer._discover = self.discoveryFunc - - self.identifier = "http://idp.unittest/1337" - self.server_url = "http://endpoint.unittest/" - - self.message = Message.fromPostArgs({ - 'openid.ns': OPENID2_NS, - 'openid.identity': self.identifier, - 'openid.claimed_id': self.identifier, - 'openid.op_endpoint': self.server_url, - }) - - self.endpoint = OpenIDServiceEndpoint() - self.endpoint.server_url = self.server_url - - def test_theGoodStuff(self): - endpoint = OpenIDServiceEndpoint() - endpoint.type_uris = [OPENID_2_0_TYPE] - endpoint.claimed_id = self.identifier - endpoint.server_url = self.server_url - endpoint.local_id = self.identifier - self.services = [endpoint] - r = self.consumer._verifyDiscoveryResults(self.message, endpoint) - - self.failUnlessEqual(r, endpoint) - - - def test_otherServer(self): - text = "verify failed" - - def discoverAndVerify(claimed_id, to_match_endpoints): - self.failUnlessEqual(claimed_id, self.identifier) - for to_match in to_match_endpoints: - self.failUnlessEqual(claimed_id, to_match.claimed_id) - raise ProtocolError(text) - - self.consumer._discoverAndVerify = discoverAndVerify - - # a set of things without the stuff - endpoint = OpenIDServiceEndpoint() - endpoint.type_uris = [OPENID_2_0_TYPE] - endpoint.claimed_id = self.identifier - endpoint.server_url = "http://the-MOON.unittest/" - endpoint.local_id = self.identifier - self.services = [endpoint] - try: - r = self.consumer._verifyDiscoveryResults(self.message, endpoint) - except ProtocolError, e: - # Should we make more ProtocolError subclasses? - self.failUnless(str(e), text) - else: - self.fail("expected ProtocolError, %r returned." % (r,)) - - - def test_foreignDelegate(self): - text = "verify failed" - - def discoverAndVerify(claimed_id, to_match_endpoints): - self.failUnlessEqual(claimed_id, self.identifier) - for to_match in to_match_endpoints: - self.failUnlessEqual(claimed_id, to_match.claimed_id) - raise ProtocolError(text) - - self.consumer._discoverAndVerify = discoverAndVerify - - # a set of things with the server stuff but other delegate - endpoint = OpenIDServiceEndpoint() - endpoint.type_uris = [OPENID_2_0_TYPE] - endpoint.claimed_id = self.identifier - endpoint.server_url = self.server_url - endpoint.local_id = "http://unittest/juan-carlos" - - try: - r = self.consumer._verifyDiscoveryResults(self.message, endpoint) - except ProtocolError, e: - self.failUnlessEqual(str(e), text) - else: - self.fail("Exepected ProtocolError, %r returned" % (r,)) - - def test_nothingDiscovered(self): - # a set of no things. - self.services = [] - self.failUnlessRaises(DiscoveryFailure, - self.consumer._verifyDiscoveryResults, - self.message, self.endpoint) - - - def discoveryFunc(self, identifier): - return identifier, self.services - - -class TestCreateAssociationRequest(unittest.TestCase): - def setUp(self): - class DummyEndpoint(object): - use_compatibility = False - - def compatibilityMode(self): - return self.use_compatibility - - self.endpoint = DummyEndpoint() - self.consumer = GenericConsumer(store=None) - self.assoc_type = 'HMAC-SHA1' - - def test_noEncryptionSendsType(self): - session_type = 'no-encryption' - session, args = self.consumer._createAssociateRequest( - self.endpoint, self.assoc_type, session_type) - - self.failUnless(isinstance(session, PlainTextConsumerSession)) - expected = Message.fromOpenIDArgs( - {'ns':OPENID2_NS, - 'session_type':session_type, - 'mode':'associate', - 'assoc_type':self.assoc_type, - }) - - self.failUnlessEqual(expected, args) - - def test_noEncryptionCompatibility(self): - self.endpoint.use_compatibility = True - session_type = 'no-encryption' - session, args = self.consumer._createAssociateRequest( - self.endpoint, self.assoc_type, session_type) - - self.failUnless(isinstance(session, PlainTextConsumerSession)) - self.failUnlessEqual(Message.fromOpenIDArgs({'mode':'associate', - 'assoc_type':self.assoc_type, - }), args) - - def test_dhSHA1Compatibility(self): - # Set the consumer's session type to a fast session since we - # need it here. - setConsumerSession(self.consumer) - - self.endpoint.use_compatibility = True - session_type = 'DH-SHA1' - session, args = self.consumer._createAssociateRequest( - self.endpoint, self.assoc_type, session_type) - - self.failUnless(isinstance(session, DiffieHellmanSHA1ConsumerSession)) - - # This is a random base-64 value, so just check that it's - # present. - self.failUnless(args.getArg(OPENID1_NS, 'dh_consumer_public')) - args.delArg(OPENID1_NS, 'dh_consumer_public') - - # OK, session_type is set here and not for no-encryption - # compatibility - expected = Message.fromOpenIDArgs({'mode':'associate', - 'session_type':'DH-SHA1', - 'assoc_type':self.assoc_type, - 'dh_modulus': 'BfvStQ==', - 'dh_gen': 'Ag==', - }) - - self.failUnlessEqual(expected, args) - - # XXX: test the other types - -class TestDiffieHellmanResponseParameters(object): - session_cls = None - message_namespace = None - - def setUp(self): - # Pre-compute DH with small prime so tests run quickly. - self.server_dh = DiffieHellman(100389557, 2) - self.consumer_dh = DiffieHellman(100389557, 2) - - # base64(btwoc(g ^ xb mod p)) - self.dh_server_public = cryptutil.longToBase64(self.server_dh.public) - - self.secret = cryptutil.randomString(self.session_cls.secret_size) - - self.enc_mac_key = oidutil.toBase64( - self.server_dh.xorSecret(self.consumer_dh.public, - self.secret, - self.session_cls.hash_func)) - - self.consumer_session = self.session_cls(self.consumer_dh) - - self.msg = Message(self.message_namespace) - - def testExtractSecret(self): - self.msg.setArg(OPENID_NS, 'dh_server_public', self.dh_server_public) - self.msg.setArg(OPENID_NS, 'enc_mac_key', self.enc_mac_key) - - extracted = self.consumer_session.extractSecret(self.msg) - self.failUnlessEqual(extracted, self.secret) - - def testAbsentServerPublic(self): - self.msg.setArg(OPENID_NS, 'enc_mac_key', self.enc_mac_key) - - self.failUnlessRaises(KeyError, self.consumer_session.extractSecret, self.msg) - - def testAbsentMacKey(self): - self.msg.setArg(OPENID_NS, 'dh_server_public', self.dh_server_public) - - self.failUnlessRaises(KeyError, self.consumer_session.extractSecret, self.msg) - - def testInvalidBase64Public(self): - self.msg.setArg(OPENID_NS, 'dh_server_public', 'n o t b a s e 6 4.') - self.msg.setArg(OPENID_NS, 'enc_mac_key', self.enc_mac_key) - - self.failUnlessRaises(ValueError, self.consumer_session.extractSecret, self.msg) - - def testInvalidBase64MacKey(self): - self.msg.setArg(OPENID_NS, 'dh_server_public', self.dh_server_public) - self.msg.setArg(OPENID_NS, 'enc_mac_key', 'n o t base 64') - - self.failUnlessRaises(ValueError, self.consumer_session.extractSecret, self.msg) - -class TestOpenID1SHA1(TestDiffieHellmanResponseParameters, unittest.TestCase): - session_cls = DiffieHellmanSHA1ConsumerSession - message_namespace = OPENID1_NS - -class TestOpenID2SHA1(TestDiffieHellmanResponseParameters, unittest.TestCase): - session_cls = DiffieHellmanSHA1ConsumerSession - message_namespace = OPENID2_NS - -if cryptutil.SHA256_AVAILABLE: - class TestOpenID2SHA256(TestDiffieHellmanResponseParameters, unittest.TestCase): - session_cls = DiffieHellmanSHA256ConsumerSession - message_namespace = OPENID2_NS -else: - warnings.warn("Not running SHA256 association session tests.") - -class TestNoStore(unittest.TestCase): - def setUp(self): - self.consumer = GenericConsumer(None) - - def test_completeNoGetAssoc(self): - """_getAssociation is never called when the store is None""" - def notCalled(unused): - self.fail('This method was unexpectedly called') - - endpoint = OpenIDServiceEndpoint() - endpoint.claimed_id = 'identity_url' - - self.consumer._getAssociation = notCalled - auth_request = self.consumer.begin(endpoint) - # _getAssociation was not called - - - - -class NonAnonymousAuthRequest(object): - endpoint = 'unused' - - def setAnonymous(self, unused): - raise ValueError('Should trigger ProtocolError') - -class TestConsumerAnonymous(unittest.TestCase): - def test_beginWithoutDiscoveryAnonymousFail(self): - """Make sure that ValueError for setting an auth request - anonymous gets converted to a ProtocolError - """ - sess = {} - consumer = Consumer(sess, None) - def bogusBegin(unused): - return NonAnonymousAuthRequest() - consumer.consumer.begin = bogusBegin - self.failUnlessRaises( - ProtocolError, - consumer.beginWithoutDiscovery, None) - - -class TestDiscoverAndVerify(unittest.TestCase): - def setUp(self): - self.consumer = GenericConsumer(None) - self.discovery_result = None - def dummyDiscover(unused_identifier): - return self.discovery_result - self.consumer._discover = dummyDiscover - self.to_match = OpenIDServiceEndpoint() - - def failUnlessDiscoveryFailure(self): - self.failUnlessRaises( - DiscoveryFailure, - self.consumer._discoverAndVerify, - 'http://claimed-id.com/', - [self.to_match]) - - def test_noServices(self): - """Discovery returning no results results in a - DiscoveryFailure exception""" - self.discovery_result = (None, []) - self.failUnlessDiscoveryFailure() - - def test_noMatches(self): - """If no discovered endpoint matches the values from the - assertion, then we end up raising a ProtocolError - """ - self.discovery_result = (None, ['unused']) - def raiseProtocolError(unused1, unused2): - raise ProtocolError('unit test') - self.consumer._verifyDiscoverySingle = raiseProtocolError - self.failUnlessDiscoveryFailure() - - def test_matches(self): - """If an endpoint matches, we return it - """ - # Discovery returns a single "endpoint" object - matching_endpoint = 'matching endpoint' - self.discovery_result = (None, [matching_endpoint]) - - # Make verifying discovery return True for this endpoint - def returnTrue(unused1, unused2): - return True - self.consumer._verifyDiscoverySingle = returnTrue - - # Since _verifyDiscoverySingle returns True, we should get the - # first endpoint that we passed in as a result. - result = self.consumer._discoverAndVerify( - 'http://claimed.id/', [self.to_match]) - self.failUnlessEqual(matching_endpoint, result) - -from askbot.deps.openid.extension import Extension -class SillyExtension(Extension): - ns_uri = 'http://silly.example.com/' - ns_alias = 'silly' - - def getExtensionArgs(self): - return {'i_am':'silly'} - -class TestAddExtension(unittest.TestCase): - - def test_SillyExtension(self): - ext = SillyExtension() - ar = AuthRequest(OpenIDServiceEndpoint(), None) - ar.addExtension(ext) - ext_args = ar.message.getArgs(ext.ns_uri) - self.failUnlessEqual(ext.getExtensionArgs(), ext_args) - - - -class TestKVPost(unittest.TestCase): - def setUp(self): - self.server_url = 'http://unittest/%s' % (self.id(),) - - def test_200(self): - from askbot.deps.openid.fetchers import HTTPResponse - response = HTTPResponse() - response.status = 200 - response.body = "foo:bar\nbaz:quux\n" - r = _httpResponseToMessage(response, self.server_url) - expected_msg = Message.fromOpenIDArgs({'foo':'bar','baz':'quux'}) - self.failUnlessEqual(expected_msg, r) - - - def test_400(self): - response = HTTPResponse() - response.status = 400 - response.body = "error:bonk\nerror_code:7\n" - try: - r = _httpResponseToMessage(response, self.server_url) - except ServerError, e: - self.failUnlessEqual(e.error_text, 'bonk') - self.failUnlessEqual(e.error_code, '7') - else: - self.fail("Expected ServerError, got return %r" % (r,)) - - - def test_500(self): - # 500 as an example of any non-200, non-400 code. - response = HTTPResponse() - response.status = 500 - response.body = "foo:bar\nbaz:quux\n" - self.failUnlessRaises(fetchers.HTTPFetchingError, - _httpResponseToMessage, response, - self.server_url) - - - - -if __name__ == '__main__': - unittest.main() diff --git a/askbot/deps/openid/test/test_discover.py b/askbot/deps/openid/test/test_discover.py deleted file mode 100644 index e985b601..00000000 --- a/askbot/deps/openid/test/test_discover.py +++ /dev/null @@ -1,783 +0,0 @@ -import sys -import unittest -import datadriven -import os.path -from askbot.deps.openid import fetchers -from askbot.deps.openid.fetchers import HTTPResponse -from askbot.deps.openid.yadis.discover import DiscoveryFailure -from askbot.deps.openid.consumer import discover -from askbot.deps.openid.yadis import xrires -from askbot.deps.openid.yadis.xri import XRI -from urlparse import urlsplit -from askbot.deps.openid import message - -### Tests for conditions that trigger DiscoveryFailure - -class SimpleMockFetcher(object): - def __init__(self, responses): - self.responses = list(responses) - - def fetch(self, url, body=None, headers=None): - response = self.responses.pop(0) - assert body is None - assert response.final_url == url - return response - -class TestDiscoveryFailure(datadriven.DataDrivenTestCase): - cases = [ - [HTTPResponse('http://network.error/', None)], - [HTTPResponse('http://not.found/', 404)], - [HTTPResponse('http://bad.request/', 400)], - [HTTPResponse('http://server.error/', 500)], - [HTTPResponse('http://header.found/', 200, - headers={'x-xrds-location':'http://xrds.missing/'}), - HTTPResponse('http://xrds.missing/', 404)], - ] - - def __init__(self, responses): - self.url = responses[0].final_url - datadriven.DataDrivenTestCase.__init__(self, self.url) - self.responses = responses - - def setUp(self): - fetcher = SimpleMockFetcher(self.responses) - fetchers.setDefaultFetcher(fetcher) - - def tearDown(self): - fetchers.setDefaultFetcher(None) - - def runOneTest(self): - expected_status = self.responses[-1].status - try: - discover.discover(self.url) - except DiscoveryFailure, why: - self.failUnlessEqual(why.http_response.status, expected_status) - else: - self.fail('Did not raise DiscoveryFailure') - - -### Tests for raising/catching exceptions from the fetcher through the -### discover function - -# Python 2.5 displays a message when running this test, which is -# testing the behaviour in the presence of string exceptions, -# deprecated or not, so tell it no to complain when this particular -# string exception is raised. -import warnings -warnings.filterwarnings('ignore', 'raising a string.*', DeprecationWarning, - r'^openid\.test\.test_discover$', 77) - -class ErrorRaisingFetcher(object): - """Just raise an exception when fetch is called""" - - def __init__(self, thing_to_raise): - self.thing_to_raise = thing_to_raise - - def fetch(self, url, body=None, headers=None): - raise self.thing_to_raise - -class DidFetch(Exception): - """Custom exception just to make sure it's not handled differently""" - -class TestFetchException(datadriven.DataDrivenTestCase): - """Make sure exceptions get passed through discover function from - fetcher.""" - - cases = [ - Exception(), - DidFetch(), - ValueError(), - RuntimeError(), - ] - - # String exceptions are finally gone from Python 2.6. - if sys.version_info[:2] < (2, 6): - cases.append('oi!') - - def __init__(self, exc): - datadriven.DataDrivenTestCase.__init__(self, repr(exc)) - self.exc = exc - - def setUp(self): - fetcher = ErrorRaisingFetcher(self.exc) - fetchers.setDefaultFetcher(fetcher, wrap_exceptions=False) - - def tearDown(self): - fetchers.setDefaultFetcher(None) - - def runOneTest(self): - try: - discover.discover('http://doesnt.matter/') - except: - exc = sys.exc_info()[1] - if exc is None: - # str exception - self.failUnless(self.exc is sys.exc_info()[0]) - else: - self.failUnless(self.exc is exc, exc) - else: - self.fail('Expected %r', self.exc) - - -### Tests for openid.consumer.discover.discover - -class TestNormalization(unittest.TestCase): - def testAddingProtocol(self): - f = ErrorRaisingFetcher(RuntimeError()) - fetchers.setDefaultFetcher(f, wrap_exceptions=False) - - try: - discover.discover('users.stompy.janrain.com:8000/x') - except DiscoveryFailure, why: - self.fail('failed to parse url with port correctly') - except RuntimeError: - pass #expected - - fetchers.setDefaultFetcher(None) - - -class DiscoveryMockFetcher(object): - redirect = None - - def __init__(self, documents): - self.documents = documents - self.fetchlog = [] - - def fetch(self, url, body=None, headers=None): - self.fetchlog.append((url, body, headers)) - if self.redirect: - final_url = self.redirect - else: - final_url = url - - try: - ctype, body = self.documents[url] - except KeyError: - status = 404 - ctype = 'text/plain' - body = '' - else: - status = 200 - - return HTTPResponse(final_url, status, {'content-type': ctype}, body) - -# from twisted.trial import unittest as trialtest - -class BaseTestDiscovery(unittest.TestCase): - id_url = "http://someuser.unittest/" - - documents = {} - fetcherClass = DiscoveryMockFetcher - - def _checkService(self, s, - server_url, - claimed_id=None, - local_id=None, - canonical_id=None, - types=None, - used_yadis=False, - display_identifier=None - ): - self.failUnlessEqual(server_url, s.server_url) - if types == ['2.0 OP']: - self.failIf(claimed_id) - self.failIf(local_id) - self.failIf(s.claimed_id) - self.failIf(s.local_id) - self.failIf(s.getLocalID()) - self.failIf(s.compatibilityMode()) - self.failUnless(s.isOPIdentifier()) - self.failUnlessEqual(s.preferredNamespace(), - discover.OPENID_2_0_MESSAGE_NS) - else: - self.failUnlessEqual(claimed_id, s.claimed_id) - self.failUnlessEqual(local_id, s.getLocalID()) - - if used_yadis: - self.failUnless(s.used_yadis, "Expected to use Yadis") - else: - self.failIf(s.used_yadis, - "Expected to use old-style discovery") - - openid_types = { - '1.1': discover.OPENID_1_1_TYPE, - '1.0': discover.OPENID_1_0_TYPE, - '2.0': discover.OPENID_2_0_TYPE, - '2.0 OP': discover.OPENID_IDP_2_0_TYPE, - } - - type_uris = [openid_types[t] for t in types] - self.failUnlessEqual(type_uris, s.type_uris) - self.failUnlessEqual(canonical_id, s.canonicalID) - - if s.canonicalID: - self.failUnless(s.getDisplayIdentifier() != claimed_id) - self.failUnless(s.getDisplayIdentifier() is not None) - self.failUnlessEqual(display_identifier, s.getDisplayIdentifier()) - self.failUnlessEqual(s.claimed_id, s.canonicalID) - - self.failUnlessEqual(s.display_identifier or s.claimed_id, s.getDisplayIdentifier()) - - def setUp(self): - self.documents = self.documents.copy() - self.fetcher = self.fetcherClass(self.documents) - fetchers.setDefaultFetcher(self.fetcher) - - def tearDown(self): - fetchers.setDefaultFetcher(None) - -def readDataFile(filename): - module_directory = os.path.dirname(os.path.abspath(__file__)) - filename = os.path.join( - module_directory, 'data', 'test_discover', filename) - return file(filename).read() - -class TestDiscovery(BaseTestDiscovery): - def _discover(self, content_type, data, - expected_services, expected_id=None): - if expected_id is None: - expected_id = self.id_url - - self.documents[self.id_url] = (content_type, data) - id_url, services = discover.discover(self.id_url) - self.failUnlessEqual(expected_services, len(services)) - self.failUnlessEqual(expected_id, id_url) - return services - - def test_404(self): - self.failUnlessRaises(DiscoveryFailure, - discover.discover, self.id_url + '/404') - - def test_noOpenID(self): - services = self._discover(content_type='text/plain', - data="junk", - expected_services=0) - - services = self._discover( - content_type='text/html', - data=readDataFile('openid_no_delegate.html'), - expected_services=1, - ) - - self._checkService( - services[0], - used_yadis=False, - types=['1.1'], - server_url="http://www.myopenid.com/server", - claimed_id=self.id_url, - local_id=self.id_url, - ) - - def test_html1(self): - services = self._discover( - content_type='text/html', - data=readDataFile('openid.html'), - expected_services=1) - - - self._checkService( - services[0], - used_yadis=False, - types=['1.1'], - server_url="http://www.myopenid.com/server", - claimed_id=self.id_url, - local_id='http://smoker.myopenid.com/', - display_identifier=self.id_url, - ) - - def test_html1Fragment(self): - """Ensure that the Claimed Identifier does not have a fragment - if one is supplied in the User Input.""" - content_type = 'text/html' - data = readDataFile('openid.html') - expected_services = 1 - - self.documents[self.id_url] = (content_type, data) - expected_id = self.id_url - self.id_url = self.id_url + '#fragment' - id_url, services = discover.discover(self.id_url) - self.failUnlessEqual(expected_services, len(services)) - self.failUnlessEqual(expected_id, id_url) - - self._checkService( - services[0], - used_yadis=False, - types=['1.1'], - server_url="http://www.myopenid.com/server", - claimed_id=expected_id, - local_id='http://smoker.myopenid.com/', - display_identifier=expected_id, - ) - - def test_html2(self): - services = self._discover( - content_type='text/html', - data=readDataFile('openid2.html'), - expected_services=1, - ) - - self._checkService( - services[0], - used_yadis=False, - types=['2.0'], - server_url="http://www.myopenid.com/server", - claimed_id=self.id_url, - local_id='http://smoker.myopenid.com/', - display_identifier=self.id_url, - ) - - def test_html1And2(self): - services = self._discover( - content_type='text/html', - data=readDataFile('openid_1_and_2.html'), - expected_services=2, - ) - - for t, s in zip(['2.0', '1.1'], services): - self._checkService( - s, - used_yadis=False, - types=[t], - server_url="http://www.myopenid.com/server", - claimed_id=self.id_url, - local_id='http://smoker.myopenid.com/', - display_identifier=self.id_url, - ) - - def test_yadisEmpty(self): - services = self._discover(content_type='application/xrds+xml', - data=readDataFile('yadis_0entries.xml'), - expected_services=0) - - def test_htmlEmptyYadis(self): - """HTML document has discovery information, but points to an - empty Yadis document.""" - # The XRDS document pointed to by "openid_and_yadis.html" - self.documents[self.id_url + 'xrds'] = ( - 'application/xrds+xml', readDataFile('yadis_0entries.xml')) - - services = self._discover(content_type='text/html', - data=readDataFile('openid_and_yadis.html'), - expected_services=1) - - self._checkService( - services[0], - used_yadis=False, - types=['1.1'], - server_url="http://www.myopenid.com/server", - claimed_id=self.id_url, - local_id='http://smoker.myopenid.com/', - display_identifier=self.id_url, - ) - - def test_yadis1NoDelegate(self): - services = self._discover(content_type='application/xrds+xml', - data=readDataFile('yadis_no_delegate.xml'), - expected_services=1) - - self._checkService( - services[0], - used_yadis=True, - types=['1.0'], - server_url="http://www.myopenid.com/server", - claimed_id=self.id_url, - local_id=self.id_url, - display_identifier=self.id_url, - ) - - def test_yadis2NoLocalID(self): - services = self._discover( - content_type='application/xrds+xml', - data=readDataFile('openid2_xrds_no_local_id.xml'), - expected_services=1, - ) - - self._checkService( - services[0], - used_yadis=True, - types=['2.0'], - server_url="http://www.myopenid.com/server", - claimed_id=self.id_url, - local_id=self.id_url, - display_identifier=self.id_url, - ) - - def test_yadis2(self): - services = self._discover( - content_type='application/xrds+xml', - data=readDataFile('openid2_xrds.xml'), - expected_services=1, - ) - - self._checkService( - services[0], - used_yadis=True, - types=['2.0'], - server_url="http://www.myopenid.com/server", - claimed_id=self.id_url, - local_id='http://smoker.myopenid.com/', - display_identifier=self.id_url, - ) - - def test_yadis2OP(self): - services = self._discover( - content_type='application/xrds+xml', - data=readDataFile('yadis_idp.xml'), - expected_services=1, - ) - - self._checkService( - services[0], - used_yadis=True, - types=['2.0 OP'], - server_url="http://www.myopenid.com/server", - display_identifier=self.id_url, - ) - - def test_yadis2OPDelegate(self): - """The delegate tag isn't meaningful for OP entries.""" - services = self._discover( - content_type='application/xrds+xml', - data=readDataFile('yadis_idp_delegate.xml'), - expected_services=1, - ) - - self._checkService( - services[0], - used_yadis=True, - types=['2.0 OP'], - server_url="http://www.myopenid.com/server", - display_identifier=self.id_url, - ) - - def test_yadis2BadLocalID(self): - self.failUnlessRaises(DiscoveryFailure, self._discover, - content_type='application/xrds+xml', - data=readDataFile('yadis_2_bad_local_id.xml'), - expected_services=1, - ) - - def test_yadis1And2(self): - services = self._discover( - content_type='application/xrds+xml', - data=readDataFile('openid_1_and_2_xrds.xml'), - expected_services=1, - ) - - self._checkService( - services[0], - used_yadis=True, - types=['2.0', '1.1'], - server_url="http://www.myopenid.com/server", - claimed_id=self.id_url, - local_id='http://smoker.myopenid.com/', - display_identifier=self.id_url, - ) - - def test_yadis1And2BadLocalID(self): - self.failUnlessRaises(DiscoveryFailure, self._discover, - content_type='application/xrds+xml', - data=readDataFile('openid_1_and_2_xrds_bad_delegate.xml'), - expected_services=1, - ) - -class MockFetcherForXRIProxy(object): - - def __init__(self, documents, proxy_url=xrires.DEFAULT_PROXY): - self.documents = documents - self.fetchlog = [] - self.proxy_url = None - - - def fetch(self, url, body=None, headers=None): - self.fetchlog.append((url, body, headers)) - - u = urlsplit(url) - proxy_host = u[1] - xri = u[2] - query = u[3] - - if not headers and not query: - raise ValueError("No headers or query; you probably didn't " - "mean to do that.") - - if xri.startswith('/'): - xri = xri[1:] - - try: - ctype, body = self.documents[xri] - except KeyError: - status = 404 - ctype = 'text/plain' - body = '' - else: - status = 200 - - return HTTPResponse(url, status, {'content-type': ctype}, body) - - -class TestXRIDiscovery(BaseTestDiscovery): - fetcherClass = MockFetcherForXRIProxy - - documents = {'=smoker': ('application/xrds+xml', - readDataFile('yadis_2entries_delegate.xml')), - '=smoker*bad': ('application/xrds+xml', - readDataFile('yadis_another_delegate.xml')) } - - def test_xri(self): - user_xri, services = discover.discoverXRI('=smoker') - - self._checkService( - services[0], - used_yadis=True, - types=['1.0'], - server_url="http://www.myopenid.com/server", - claimed_id=XRI("=!1000"), - canonical_id=XRI("=!1000"), - local_id='http://smoker.myopenid.com/', - display_identifier='=smoker' - ) - - self._checkService( - services[1], - used_yadis=True, - types=['1.0'], - server_url="http://www.livejournal.com/openid/server.bml", - claimed_id=XRI("=!1000"), - canonical_id=XRI("=!1000"), - local_id='http://frank.livejournal.com/', - display_identifier='=smoker' - ) - - def test_xri_normalize(self): - user_xri, services = discover.discoverXRI('xri://=smoker') - - self._checkService( - services[0], - used_yadis=True, - types=['1.0'], - server_url="http://www.myopenid.com/server", - claimed_id=XRI("=!1000"), - canonical_id=XRI("=!1000"), - local_id='http://smoker.myopenid.com/', - display_identifier='=smoker' - ) - - self._checkService( - services[1], - used_yadis=True, - types=['1.0'], - server_url="http://www.livejournal.com/openid/server.bml", - claimed_id=XRI("=!1000"), - canonical_id=XRI("=!1000"), - local_id='http://frank.livejournal.com/', - display_identifier='=smoker' - ) - - def test_xriNoCanonicalID(self): - user_xri, services = discover.discoverXRI('=smoker*bad') - self.failIf(services) - - def test_useCanonicalID(self): - """When there is no delegate, the CanonicalID should be used with XRI. - """ - endpoint = discover.OpenIDServiceEndpoint() - endpoint.claimed_id = XRI("=!1000") - endpoint.canonicalID = XRI("=!1000") - self.failUnlessEqual(endpoint.getLocalID(), XRI("=!1000")) - - -class TestXRIDiscoveryIDP(BaseTestDiscovery): - fetcherClass = MockFetcherForXRIProxy - - documents = {'=smoker': ('application/xrds+xml', - readDataFile('yadis_2entries_idp.xml')) } - - def test_xri(self): - user_xri, services = discover.discoverXRI('=smoker') - self.failUnless(services, "Expected services, got zero") - self.failUnlessEqual(services[0].server_url, - "http://www.livejournal.com/openid/server.bml") - - -class TestPreferredNamespace(datadriven.DataDrivenTestCase): - def __init__(self, expected_ns, type_uris): - datadriven.DataDrivenTestCase.__init__( - self, 'Expecting %s from %s' % (expected_ns, type_uris)) - self.expected_ns = expected_ns - self.type_uris = type_uris - - def runOneTest(self): - endpoint = discover.OpenIDServiceEndpoint() - endpoint.type_uris = self.type_uris - actual_ns = endpoint.preferredNamespace() - self.failUnlessEqual(actual_ns, self.expected_ns) - - cases = [ - (message.OPENID1_NS, []), - (message.OPENID1_NS, ['http://jyte.com/']), - (message.OPENID1_NS, [discover.OPENID_1_0_TYPE]), - (message.OPENID1_NS, [discover.OPENID_1_1_TYPE]), - (message.OPENID2_NS, [discover.OPENID_2_0_TYPE]), - (message.OPENID2_NS, [discover.OPENID_IDP_2_0_TYPE]), - (message.OPENID2_NS, [discover.OPENID_2_0_TYPE, - discover.OPENID_1_0_TYPE]), - (message.OPENID2_NS, [discover.OPENID_1_0_TYPE, - discover.OPENID_2_0_TYPE]), - ] - -class TestIsOPIdentifier(unittest.TestCase): - def setUp(self): - self.endpoint = discover.OpenIDServiceEndpoint() - - def test_none(self): - self.failIf(self.endpoint.isOPIdentifier()) - - def test_openid1_0(self): - self.endpoint.type_uris = [discover.OPENID_1_0_TYPE] - self.failIf(self.endpoint.isOPIdentifier()) - - def test_openid1_1(self): - self.endpoint.type_uris = [discover.OPENID_1_1_TYPE] - self.failIf(self.endpoint.isOPIdentifier()) - - def test_openid2(self): - self.endpoint.type_uris = [discover.OPENID_2_0_TYPE] - self.failIf(self.endpoint.isOPIdentifier()) - - def test_openid2OP(self): - self.endpoint.type_uris = [discover.OPENID_IDP_2_0_TYPE] - self.failUnless(self.endpoint.isOPIdentifier()) - - def test_multipleMissing(self): - self.endpoint.type_uris = [discover.OPENID_2_0_TYPE, - discover.OPENID_1_0_TYPE] - self.failIf(self.endpoint.isOPIdentifier()) - - def test_multiplePresent(self): - self.endpoint.type_uris = [discover.OPENID_2_0_TYPE, - discover.OPENID_1_0_TYPE, - discover.OPENID_IDP_2_0_TYPE] - self.failUnless(self.endpoint.isOPIdentifier()) - -class TestFromOPEndpointURL(unittest.TestCase): - def setUp(self): - self.op_endpoint_url = 'http://example.com/op/endpoint' - self.endpoint = discover.OpenIDServiceEndpoint.fromOPEndpointURL( - self.op_endpoint_url) - - def test_isOPEndpoint(self): - self.failUnless(self.endpoint.isOPIdentifier()) - - def test_noIdentifiers(self): - self.failUnlessEqual(self.endpoint.getLocalID(), None) - self.failUnlessEqual(self.endpoint.claimed_id, None) - - def test_compatibility(self): - self.failIf(self.endpoint.compatibilityMode()) - - def test_canonicalID(self): - self.failUnlessEqual(self.endpoint.canonicalID, None) - - def test_serverURL(self): - self.failUnlessEqual(self.endpoint.server_url, self.op_endpoint_url) - -class TestDiscoverFunction(unittest.TestCase): - def setUp(self): - self._old_discoverURI = discover.discoverURI - self._old_discoverXRI = discover.discoverXRI - - discover.discoverXRI = self.discoverXRI - discover.discoverURI = self.discoverURI - - def tearDown(self): - discover.discoverURI = self._old_discoverURI - discover.discoverXRI = self._old_discoverXRI - - def discoverXRI(self, identifier): - return 'XRI' - - def discoverURI(self, identifier): - return 'URI' - - def test_uri(self): - self.failUnlessEqual('URI', discover.discover('http://woo!')) - - def test_uriForBogus(self): - self.failUnlessEqual('URI', discover.discover('not a URL or XRI')) - - def test_xri(self): - self.failUnlessEqual('XRI', discover.discover('xri://=something')) - - def test_xriChar(self): - self.failUnlessEqual('XRI', discover.discover('=something')) - -class TestEndpointSupportsType(unittest.TestCase): - def setUp(self): - self.endpoint = discover.OpenIDServiceEndpoint() - - def failUnlessSupportsOnly(self, *types): - for t in [ - 'foo', - discover.OPENID_1_1_TYPE, - discover.OPENID_1_0_TYPE, - discover.OPENID_2_0_TYPE, - discover.OPENID_IDP_2_0_TYPE, - ]: - if t in types: - self.failUnless(self.endpoint.supportsType(t), - "Must support %r" % (t,)) - else: - self.failIf(self.endpoint.supportsType(t), - "Shouldn't support %r" % (t,)) - - def test_supportsNothing(self): - self.failUnlessSupportsOnly() - - def test_openid2(self): - self.endpoint.type_uris = [discover.OPENID_2_0_TYPE] - self.failUnlessSupportsOnly(discover.OPENID_2_0_TYPE) - - def test_openid2provider(self): - self.endpoint.type_uris = [discover.OPENID_IDP_2_0_TYPE] - self.failUnlessSupportsOnly(discover.OPENID_IDP_2_0_TYPE, - discover.OPENID_2_0_TYPE) - - def test_openid1_0(self): - self.endpoint.type_uris = [discover.OPENID_1_0_TYPE] - self.failUnlessSupportsOnly(discover.OPENID_1_0_TYPE) - - def test_openid1_1(self): - self.endpoint.type_uris = [discover.OPENID_1_1_TYPE] - self.failUnlessSupportsOnly(discover.OPENID_1_1_TYPE) - - def test_multiple(self): - self.endpoint.type_uris = [discover.OPENID_1_1_TYPE, - discover.OPENID_2_0_TYPE] - self.failUnlessSupportsOnly(discover.OPENID_1_1_TYPE, - discover.OPENID_2_0_TYPE) - - def test_multipleWithProvider(self): - self.endpoint.type_uris = [discover.OPENID_1_1_TYPE, - discover.OPENID_2_0_TYPE, - discover.OPENID_IDP_2_0_TYPE] - self.failUnlessSupportsOnly(discover.OPENID_1_1_TYPE, - discover.OPENID_2_0_TYPE, - discover.OPENID_IDP_2_0_TYPE, - ) - - -class TestEndpointDisplayIdentifier(unittest.TestCase): - def test_strip_fragment(self): - endpoint = discover.OpenIDServiceEndpoint() - endpoint.claimed_id = 'http://recycled.invalid/#123' - self.failUnlessEqual('http://recycled.invalid/', endpoint.getDisplayIdentifier()) - - -def pyUnitTests(): - return datadriven.loadTests(__name__) - -if __name__ == '__main__': - suite = pyUnitTests() - runner = unittest.TextTestRunner() - runner.run(suite) diff --git a/askbot/deps/openid/test/test_etxrd.py b/askbot/deps/openid/test/test_etxrd.py deleted file mode 100644 index 653ae1fb..00000000 --- a/askbot/deps/openid/test/test_etxrd.py +++ /dev/null @@ -1,194 +0,0 @@ -import unittest -from askbot.deps.openid.yadis import services, etxrd, xri -import os.path - -def datapath(filename): - module_directory = os.path.dirname(os.path.abspath(__file__)) - return os.path.join(module_directory, 'data', 'test_etxrd', filename) - -XRD_FILE = datapath('valid-populated-xrds.xml') -NOXRDS_FILE = datapath('not-xrds.xml') -NOXRD_FILE = datapath('no-xrd.xml') - -# None of the namespaces or service URIs below are official (or even -# sanctioned by the owners of that piece of URL-space) - -LID_2_0 = "http://lid.netmesh.org/sso/2.0b5" -TYPEKEY_1_0 = "http://typekey.com/services/1.0" - -def simpleOpenIDTransformer(endpoint): - """Function to extract information from an OpenID service element""" - if 'http://openid.net/signon/1.0' not in endpoint.type_uris: - return None - - delegates = list(endpoint.service_element.findall( - '{http://openid.net/xmlns/1.0}Delegate')) - assert len(delegates) == 1 - delegate = delegates[0].text - return (endpoint.uri, delegate) - -class TestServiceParser(unittest.TestCase): - def setUp(self): - self.xmldoc = file(XRD_FILE).read() - self.yadis_url = 'http://unittest.url/' - - def _getServices(self, flt=None): - return list(services.applyFilter(self.yadis_url, self.xmldoc, flt)) - - def testParse(self): - """Make sure that parsing succeeds at all""" - services = self._getServices() - - def testParseOpenID(self): - """Parse for OpenID services with a transformer function""" - services = self._getServices(simpleOpenIDTransformer) - - expectedServices = [ - ("http://www.myopenid.com/server", "http://josh.myopenid.com/"), - ("http://www.schtuff.com/openid", "http://users.schtuff.com/josh"), - ("http://www.livejournal.com/openid/server.bml", - "http://www.livejournal.com/users/nedthealpaca/"), - ] - - it = iter(services) - for (server_url, delegate) in expectedServices: - for (actual_url, actual_delegate) in it: - self.failUnlessEqual(server_url, actual_url) - self.failUnlessEqual(delegate, actual_delegate) - break - else: - self.fail('Not enough services found') - - def _checkServices(self, expectedServices): - """Check to make sure that the expected services are found in - that order in the parsed document.""" - it = iter(self._getServices()) - for (type_uri, uri) in expectedServices: - for service in it: - if type_uri in service.type_uris: - self.failUnlessEqual(service.uri, uri) - break - else: - self.fail('Did not find %r service' % (type_uri,)) - - def testGetSeveral(self): - """Get some services in order""" - expectedServices = [ - # type, URL - (TYPEKEY_1_0, None), - (LID_2_0, "http://mylid.net/josh"), - ] - - self._checkServices(expectedServices) - - def testGetSeveralForOne(self): - """Getting services for one Service with several Type elements.""" - types = [ 'http://lid.netmesh.org/sso/2.0b5' - , 'http://lid.netmesh.org/2.0b5' - ] - - uri = "http://mylid.net/josh" - - for service in self._getServices(): - if service.uri == uri: - found_types = service.matchTypes(types) - if found_types == types: - break - else: - self.fail('Did not find service with expected types and uris') - - def testNoXRDS(self): - """Make sure that we get an exception when an XRDS element is - not present""" - self.xmldoc = file(NOXRDS_FILE).read() - self.failUnlessRaises( - etxrd.XRDSError, - services.applyFilter, self.yadis_url, self.xmldoc, None) - - def testEmpty(self): - """Make sure that we get an exception when an XRDS element is - not present""" - self.xmldoc = '' - self.failUnlessRaises( - etxrd.XRDSError, - services.applyFilter, self.yadis_url, self.xmldoc, None) - - def testNoXRD(self): - """Make sure that we get an exception when there is no XRD - element present.""" - self.xmldoc = file(NOXRD_FILE).read() - self.failUnlessRaises( - etxrd.XRDSError, - services.applyFilter, self.yadis_url, self.xmldoc, None) - - -class TestCanonicalID(unittest.TestCase): - - def mkTest(iname, filename, expectedID): - """This function builds a method that runs the CanonicalID - test for the given set of inputs""" - - filename = datapath(filename) - def test(self): - xrds = etxrd.parseXRDS(file(filename).read()) - self._getCanonicalID(iname, xrds, expectedID) - return test - - test_delegated = mkTest( - "@ootao*test1", "delegated-20060809.xrds", - "@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01") - - test_delegated_r1 = mkTest( - "@ootao*test1", "delegated-20060809-r1.xrds", - "@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01") - - test_delegated_r2 = mkTest( - "@ootao*test1", "delegated-20060809-r2.xrds", - "@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01") - - test_sometimesprefix = mkTest( - "@ootao*test1", "sometimesprefix.xrds", - "@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01") - - test_prefixsometimes = mkTest( - "@ootao*test1", "prefixsometimes.xrds", - "@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01") - - test_spoof1 = mkTest("=keturn*isDrummond", "spoof1.xrds", etxrd.XRDSFraud) - - test_spoof2 = mkTest("=keturn*isDrummond", "spoof2.xrds", etxrd.XRDSFraud) - - test_spoof3 = mkTest("@keturn*is*drummond", "spoof3.xrds", etxrd.XRDSFraud) - - test_status222 = mkTest("=x", "status222.xrds", None) - - test_multisegment_xri = mkTest('xri://=nishitani*masaki', - 'subsegments.xrds', - '=!E117.EF2F.454B.C707!0000.0000.3B9A.CA01') - - test_iri_auth_not_allowed = mkTest( - "phreak.example.com", "delegated-20060809-r2.xrds", etxrd.XRDSFraud) - test_iri_auth_not_allowed.__doc__ = \ - "Don't let IRI authorities be canonical for the GCS." - - # TODO: Refs - # test_ref = mkTest("@ootao*test.ref", "ref.xrds", "@!BAE.A650.823B.2475") - - # TODO: Add a IRI authority with an IRI canonicalID. - # TODO: Add test cases with real examples of multiple CanonicalIDs - # somewhere in the resolution chain. - - def _getCanonicalID(self, iname, xrds, expectedID): - if isinstance(expectedID, (str, unicode, type(None))): - cid = etxrd.getCanonicalID(iname, xrds) - self.failUnlessEqual(cid, expectedID and xri.XRI(expectedID)) - elif issubclass(expectedID, etxrd.XRDSError): - self.failUnlessRaises(expectedID, etxrd.getCanonicalID, - iname, xrds) - else: - self.fail("Don't know how to test for expected value %r" - % (expectedID,)) - - -if __name__ == '__main__': - unittest.main() diff --git a/askbot/deps/openid/test/test_examples.py b/askbot/deps/openid/test/test_examples.py deleted file mode 100644 index d2360354..00000000 --- a/askbot/deps/openid/test/test_examples.py +++ /dev/null @@ -1,185 +0,0 @@ -"Test some examples." - -import socket -import os.path, unittest, sys, time -from cStringIO import StringIO - -import twill.commands, twill.parse, twill.unit - -from askbot.deps.openid.consumer.discover import \ - OpenIDServiceEndpoint, OPENID_1_1_TYPE -from askbot.deps.openid.consumer.consumer import AuthRequest - -class TwillTest(twill.unit.TestInfo): - """Variant of twill.unit.TestInfo that runs a function as a test script, - not twill script from a file. - """ - - # twill.unit is pretty small to start with, we're overriding - # run_script and bypassing twill.parse, so it may make sense to - # rewrite twill.unit altogether. - - # Desirable features: - # * better unittest.TestCase integration. - # - handle logs on setup and teardown. - # - treat TwillAssertionError as failed test assertion, make twill - # assertions more consistant with TestCase.failUnless idioms. - # - better error reporting on failed assertions. - # - The amount of functions passed back and forth between TestInfo - # and TestCase is currently pretty silly. - # * access to child process's logs. - # TestInfo.start_server redirects stdout/stderr to StringIO - # objects which are, afaict, inaccessible to the caller of - # test.unit.run_child_process. - # * notice when the child process dies, i.e. if you muck up and - # your runExampleServer function throws an exception. - - def run_script(self): - time.sleep(self.sleep) - # twill.commands.go(self.get_url()) - self.script(self) - - -def splitDir(d, count): - # in python2.4 and above, it's easier to spell this as - # d.rsplit(os.sep, count) - for i in xrange(count): - d = os.path.dirname(d) - return d - -def runExampleServer(host, port, data_path): - thisfile = os.path.abspath(sys.modules[__name__].__file__) - topDir = splitDir(thisfile, 3) - exampleDir = os.path.join(topDir, 'examples') - serverExample = os.path.join(exampleDir, 'server.py') - serverModule = {} - execfile(serverExample, serverModule) - serverMain = serverModule['main'] - - serverMain(host, port, data_path) - - - -class TestServer(unittest.TestCase): - """Acceptance tests for examples/server.py. - - These are more acceptance tests than unit tests as they actually - start the whole server running and test it on its external HTTP - interface. - """ - - def setUp(self): - self.twillOutput = StringIO() - self.twillErr = StringIO() - twill.set_output(self.twillOutput) - twill.set_errout(self.twillErr) - # FIXME: make sure we pick an available port. - self.server_port = 8080 - - # We need something to feed the server as a realm, but it needn't - # be reachable. (Until we test realm verification.) - self.realm = 'http://127.0.0.1/%s' % (self.id(),) - self.return_to = self.realm + '/return_to' - - twill.commands.reset_browser() - - - def runExampleServer(self): - """Zero-arg run-the-server function to be passed to TestInfo.""" - # FIXME - make sure sstore starts clean. - runExampleServer('127.0.0.1', self.server_port, 'sstore') - - - def v1endpoint(self, port): - """Return an OpenID 1.1 OpenIDServiceEndpoint for the server.""" - base = "http://%s:%s" % (socket.getfqdn('127.0.0.1'), port) - ep = OpenIDServiceEndpoint() - ep.claimed_id = base + "/id/bob" - ep.server_url = base + "/openidserver" - ep.type_uris = [OPENID_1_1_TYPE] - return ep - - - # TODO: test discovery - - def test_checkidv1(self): - """OpenID 1.1 checkid_setup request.""" - ti = TwillTest(self.twill_checkidv1, self.runExampleServer, - self.server_port, sleep=0.2) - twill.unit.run_test(ti) - - if self.twillErr.getvalue(): - self.fail(self.twillErr.getvalue()) - - - def test_allowed(self): - """OpenID 1.1 checkid_setup request.""" - ti = TwillTest(self.twill_allowed, self.runExampleServer, - self.server_port, sleep=0.2) - twill.unit.run_test(ti) - - if self.twillErr.getvalue(): - self.fail(self.twillErr.getvalue()) - - - def twill_checkidv1(self, twillInfo): - endpoint = self.v1endpoint(self.server_port) - authreq = AuthRequest(endpoint, assoc=None) - url = authreq.redirectURL(self.realm, self.return_to) - - c = twill.commands - - try: - c.go(url) - c.get_browser()._browser.set_handle_redirect(False) - c.submit("yes") - c.code(302) - headers = c.get_browser()._browser.response().info() - finalURL = headers['Location'] - self.failUnless('openid.mode=id_res' in finalURL, finalURL) - self.failUnless('openid.identity=' in finalURL, finalURL) - except twill.commands.TwillAssertionError, e: - msg = '%s\nFinal page:\n%s' % ( - str(e), c.get_browser().get_html()) - self.fail(msg) - - - def twill_allowed(self, twillInfo): - endpoint = self.v1endpoint(self.server_port) - authreq = AuthRequest(endpoint, assoc=None) - url = authreq.redirectURL(self.realm, self.return_to) - - c = twill.commands - - try: - c.go(url) - c.code(200) - c.get_browser()._browser.set_handle_redirect(False) - c.formvalue(1, 'remember', 'true') - c.find('name="login_as" value="bob"') - c.submit("yes") - c.code(302) - # Since we set remember=yes, the second time we shouldn't - # see that page. - c.go(url) - c.code(302) - headers = c.get_browser()._browser.response().info() - finalURL = headers['Location'] - self.failUnless(finalURL.startswith(self.return_to)) - except twill.commands.TwillAssertionError, e: - from traceback import format_exc - msg = '%s\nTwill output:%s\nTwill errors:%s\nFinal page:\n%s' % ( - format_exc(), - self.twillOutput.getvalue(), - self.twillErr.getvalue(), - c.get_browser().get_html()) - self.fail(msg) - - - def tearDown(self): - twill.set_output(None) - twill.set_errout(None) - - -if __name__ == '__main__': - unittest.main() diff --git a/askbot/deps/openid/test/test_extension.py b/askbot/deps/openid/test/test_extension.py deleted file mode 100644 index 297adfc3..00000000 --- a/askbot/deps/openid/test/test_extension.py +++ /dev/null @@ -1,36 +0,0 @@ -from askbot.deps.openid import extension -from askbot.deps.openid import message - -import unittest - -class DummyExtension(extension.Extension): - ns_uri = 'http://an.extension/' - ns_alias = 'dummy' - - def getExtensionArgs(self): - return {} - -class ToMessageTest(unittest.TestCase): - def test_OpenID1(self): - oid1_msg = message.Message(message.OPENID1_NS) - ext = DummyExtension() - ext.toMessage(oid1_msg) - namespaces = oid1_msg.namespaces - self.failUnless(namespaces.isImplicit(DummyExtension.ns_uri)) - self.failUnlessEqual( - DummyExtension.ns_uri, - namespaces.getNamespaceURI(DummyExtension.ns_alias)) - self.failUnlessEqual(DummyExtension.ns_alias, - namespaces.getAlias(DummyExtension.ns_uri)) - - def test_OpenID2(self): - oid2_msg = message.Message(message.OPENID2_NS) - ext = DummyExtension() - ext.toMessage(oid2_msg) - namespaces = oid2_msg.namespaces - self.failIf(namespaces.isImplicit(DummyExtension.ns_uri)) - self.failUnlessEqual( - DummyExtension.ns_uri, - namespaces.getNamespaceURI(DummyExtension.ns_alias)) - self.failUnlessEqual(DummyExtension.ns_alias, - namespaces.getAlias(DummyExtension.ns_uri)) diff --git a/askbot/deps/openid/test/test_fetchers.py b/askbot/deps/openid/test/test_fetchers.py deleted file mode 100644 index 2a53fea8..00000000 --- a/askbot/deps/openid/test/test_fetchers.py +++ /dev/null @@ -1,285 +0,0 @@ -import warnings -import unittest -import sys -import urllib2 -import socket - -from askbot.deps.openid import fetchers - -# XXX: make these separate test cases - -def failUnlessResponseExpected(expected, actual): - assert expected.final_url == actual.final_url, ( - "%r != %r" % (expected.final_url, actual.final_url)) - assert expected.status == actual.status - assert expected.body == actual.body - got_headers = dict(actual.headers) - del got_headers['date'] - del got_headers['server'] - for k, v in expected.headers.iteritems(): - assert got_headers[k] == v, (k, v, got_headers[k]) - -def test_fetcher(fetcher, exc, server): - def geturl(path): - return 'http://%s:%s%s' % (socket.getfqdn(server.server_name), - server.socket.getsockname()[1], - path) - - expected_headers = {'content-type':'text/plain'} - - def plain(path, code): - path = '/' + path - expected = fetchers.HTTPResponse( - geturl(path), code, expected_headers, path) - return (path, expected) - - expect_success = fetchers.HTTPResponse( - geturl('/success'), 200, expected_headers, '/success') - cases = [ - ('/success', expect_success), - ('/301redirect', expect_success), - ('/302redirect', expect_success), - ('/303redirect', expect_success), - ('/307redirect', expect_success), - plain('notfound', 404), - plain('badreq', 400), - plain('forbidden', 403), - plain('error', 500), - plain('server_error', 503), - ] - - for path, expected in cases: - fetch_url = geturl(path) - try: - actual = fetcher.fetch(fetch_url) - except (SystemExit, KeyboardInterrupt): - pass - except: - print fetcher, fetch_url - raise - else: - failUnlessResponseExpected(expected, actual) - - for err_url in [geturl('/closed'), - 'http://invalid.janrain.com/', - 'not:a/url', - 'ftp://janrain.com/pub/']: - try: - result = fetcher.fetch(err_url) - except (KeyboardInterrupt, SystemExit): - raise - except fetchers.HTTPError, why: - # This is raised by the Curl fetcher for bad cases - # detected by the fetchers module, but it's a subclass of - # HTTPFetchingError, so we have to catch it explicitly. - assert exc - except fetchers.HTTPFetchingError, why: - assert not exc, (fetcher, exc, server) - except: - assert exc - else: - assert False, 'An exception was expected for %r (%r)' % (fetcher, result) - -def run_fetcher_tests(server): - exc_fetchers = [] - for klass, library_name in [ - (fetchers.Urllib2Fetcher, 'urllib2'), - (fetchers.CurlHTTPFetcher, 'pycurl'), - (fetchers.HTTPLib2Fetcher, 'httplib2'), - ]: - try: - exc_fetchers.append(klass()) - except RuntimeError, why: - if why[0].startswith('Cannot find %s library' % (library_name,)): - try: - __import__(library_name) - except ImportError: - warnings.warn( - 'Skipping tests for %r fetcher because ' - 'the library did not import.' % (library_name,)) - pass - else: - assert False, ('%s present but not detected' % (library_name,)) - else: - raise - - non_exc_fetchers = [] - for f in exc_fetchers: - non_exc_fetchers.append(fetchers.ExceptionWrappingFetcher(f)) - - for f in exc_fetchers: - test_fetcher(f, True, server) - - for f in non_exc_fetchers: - test_fetcher(f, False, server) - -from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer - -class FetcherTestHandler(BaseHTTPRequestHandler): - cases = { - '/success':(200, None), - '/301redirect':(301, '/success'), - '/302redirect':(302, '/success'), - '/303redirect':(303, '/success'), - '/307redirect':(307, '/success'), - '/notfound':(404, None), - '/badreq':(400, None), - '/forbidden':(403, None), - '/error':(500, None), - '/server_error':(503, None), - } - - def log_request(self, *args): - pass - - def do_GET(self): - if self.path == '/closed': - self.wfile.close() - else: - try: - http_code, location = self.cases[self.path] - except KeyError: - self.errorResponse('Bad path') - else: - extra_headers = [('Content-type', 'text/plain')] - if location is not None: - host, port = self.server.server_address - base = ('http://%s:%s' % (socket.getfqdn(host), port,)) - location = base + location - extra_headers.append(('Location', location)) - self._respond(http_code, extra_headers, self.path) - - def do_POST(self): - try: - http_code, extra_headers = self.cases[self.path] - except KeyError: - self.errorResponse('Bad path') - else: - if http_code in [301, 302, 303, 307]: - self.errorResponse() - else: - content_type = self.headers.get('content-type', 'text/plain') - extra_headers.append(('Content-type', content_type)) - content_length = int(self.headers.get('Content-length', '-1')) - body = self.rfile.read(content_length) - self._respond(http_code, extra_headers, body) - - def errorResponse(self, message=None): - req = [ - ('HTTP method', self.command), - ('path', self.path), - ] - if message: - req.append(('message', message)) - - body_parts = ['Bad request:\r\n'] - for k, v in req: - body_parts.append(' %s: %s\r\n' % (k, v)) - body = ''.join(body_parts) - self._respond(400, [('Content-type', 'text/plain')], body) - - def _respond(self, http_code, extra_headers, body): - self.send_response(http_code) - for k, v in extra_headers: - self.send_header(k, v) - self.end_headers() - self.wfile.write(body) - self.wfile.close() - - def finish(self): - if not self.wfile.closed: - self.wfile.flush() - self.wfile.close() - self.rfile.close() - -def test(): - import socket - host = socket.getfqdn('127.0.0.1') - # When I use port 0 here, it works for the first fetch and the - # next one gets connection refused. Bummer. So instead, pick a - # port that's *probably* not in use. - import os - port = (os.getpid() % 31000) + 1024 - - server = HTTPServer((host, port), FetcherTestHandler) - - import threading - server_thread = threading.Thread(target=server.serve_forever) - server_thread.setDaemon(True) - server_thread.start() - - run_fetcher_tests(server) - -class FakeFetcher(object): - sentinel = object() - - def fetch(self, *args, **kwargs): - return self.sentinel - -class DefaultFetcherTest(unittest.TestCase): - def setUp(self): - """reset the default fetcher to None""" - fetchers.setDefaultFetcher(None) - - def tearDown(self): - """reset the default fetcher to None""" - fetchers.setDefaultFetcher(None) - - def test_getDefaultNotNone(self): - """Make sure that None is never returned as a default fetcher""" - self.failUnless(fetchers.getDefaultFetcher() is not None) - fetchers.setDefaultFetcher(None) - self.failUnless(fetchers.getDefaultFetcher() is not None) - - def test_setDefault(self): - """Make sure the getDefaultFetcher returns the object set for - setDefaultFetcher""" - sentinel = object() - fetchers.setDefaultFetcher(sentinel, wrap_exceptions=False) - self.failUnless(fetchers.getDefaultFetcher() is sentinel) - - def test_callFetch(self): - """Make sure that fetchers.fetch() uses the default fetcher - instance that was set.""" - fetchers.setDefaultFetcher(FakeFetcher()) - actual = fetchers.fetch('bad://url') - self.failUnless(actual is FakeFetcher.sentinel) - - def test_wrappedByDefault(self): - """Make sure that the default fetcher instance wraps - exceptions by default""" - default_fetcher = fetchers.getDefaultFetcher() - self.failUnless(isinstance(default_fetcher, - fetchers.ExceptionWrappingFetcher), - default_fetcher) - - self.failUnlessRaises(fetchers.HTTPFetchingError, - fetchers.fetch, 'http://invalid.janrain.com/') - - def test_notWrapped(self): - """Make sure that if we set a non-wrapped fetcher as default, - it will not wrap exceptions.""" - # A fetcher that will raise an exception when it encounters a - # host that will not resolve - fetcher = fetchers.Urllib2Fetcher() - fetchers.setDefaultFetcher(fetcher, wrap_exceptions=False) - - self.failIf(isinstance(fetchers.getDefaultFetcher(), - fetchers.ExceptionWrappingFetcher)) - - try: - fetchers.fetch('http://invalid.janrain.com/') - except fetchers.HTTPFetchingError: - self.fail('Should not be wrapping exception') - except: - exc = sys.exc_info()[1] - self.failUnless(isinstance(exc, urllib2.URLError), exc) - pass - else: - self.fail('Should have raised an exception') - -def pyUnitTests(): - case1 = unittest.FunctionTestCase(test) - loadTests = unittest.defaultTestLoader.loadTestsFromTestCase - case2 = loadTests(DefaultFetcherTest) - return unittest.TestSuite([case1, case2]) diff --git a/askbot/deps/openid/test/test_htmldiscover.py b/askbot/deps/openid/test/test_htmldiscover.py deleted file mode 100644 index c6206bee..00000000 --- a/askbot/deps/openid/test/test_htmldiscover.py +++ /dev/null @@ -1,21 +0,0 @@ -from askbot.deps.openid.consumer.discover import OpenIDServiceEndpoint -import datadriven - -class BadLinksTestCase(datadriven.DataDrivenTestCase): - cases = [ - '', - "http://not.in.a.link.tag/", - '<link rel="openid.server" href="not.in.html.or.head" />', - ] - - def __init__(self, data): - datadriven.DataDrivenTestCase.__init__(self, data) - self.data = data - - def runOneTest(self): - actual = OpenIDServiceEndpoint.fromHTML('http://unused.url/', self.data) - expected = [] - self.failUnlessEqual(expected, actual) - -def pyUnitTests(): - return datadriven.loadTests(__name__) diff --git a/askbot/deps/openid/test/test_message.py b/askbot/deps/openid/test/test_message.py deleted file mode 100644 index 4b363dcd..00000000 --- a/askbot/deps/openid/test/test_message.py +++ /dev/null @@ -1,998 +0,0 @@ -from askbot.deps.openid import message -from askbot.deps.openid import oidutil -from askbot.deps.openid.extensions import sreg - -import urllib -import cgi -import unittest - -def mkGetArgTest(ns, key, expected=None): - def test(self): - a_default = object() - self.failUnlessEqual(self.msg.getArg(ns, key), expected) - if expected is None: - self.failUnlessEqual( - self.msg.getArg(ns, key, a_default), a_default) - self.failUnlessRaises( - KeyError, self.msg.getArg, ns, key, message.no_default) - else: - self.failUnlessEqual( - self.msg.getArg(ns, key, a_default), expected) - self.failUnlessEqual( - self.msg.getArg(ns, key, message.no_default), expected) - - return test - -class EmptyMessageTest(unittest.TestCase): - def setUp(self): - self.msg = message.Message() - - def test_toPostArgs(self): - self.failUnlessEqual(self.msg.toPostArgs(), {}) - - def test_toArgs(self): - self.failUnlessEqual(self.msg.toArgs(), {}) - - def test_toKVForm(self): - self.failUnlessEqual(self.msg.toKVForm(), '') - - def test_toURLEncoded(self): - self.failUnlessEqual(self.msg.toURLEncoded(), '') - - def test_toURL(self): - base_url = 'http://base.url/' - self.failUnlessEqual(self.msg.toURL(base_url), base_url) - - def test_getOpenID(self): - self.failUnlessEqual(self.msg.getOpenIDNamespace(), None) - - def test_getKeyOpenID(self): - # Could reasonably return None instead of raising an - # exception. I'm not sure which one is more right, since this - # case should only happen when you're building a message from - # scratch and so have no default namespace. - self.failUnlessRaises(message.UndefinedOpenIDNamespace, - self.msg.getKey, message.OPENID_NS, 'foo') - - def test_getKeyBARE(self): - self.failUnlessEqual(self.msg.getKey(message.BARE_NS, 'foo'), 'foo') - - def test_getKeyNS1(self): - self.failUnlessEqual(self.msg.getKey(message.OPENID1_NS, 'foo'), None) - - def test_getKeyNS2(self): - self.failUnlessEqual(self.msg.getKey(message.OPENID2_NS, 'foo'), None) - - def test_getKeyNS3(self): - self.failUnlessEqual(self.msg.getKey('urn:nothing-significant', 'foo'), - None) - - def test_hasKey(self): - # Could reasonably return False instead of raising an - # exception. I'm not sure which one is more right, since this - # case should only happen when you're building a message from - # scratch and so have no default namespace. - self.failUnlessRaises(message.UndefinedOpenIDNamespace, - self.msg.hasKey, message.OPENID_NS, 'foo') - - def test_hasKeyBARE(self): - self.failUnlessEqual(self.msg.hasKey(message.BARE_NS, 'foo'), False) - - def test_hasKeyNS1(self): - self.failUnlessEqual(self.msg.hasKey(message.OPENID1_NS, 'foo'), False) - - def test_hasKeyNS2(self): - self.failUnlessEqual(self.msg.hasKey(message.OPENID2_NS, 'foo'), False) - - def test_hasKeyNS3(self): - self.failUnlessEqual(self.msg.hasKey('urn:nothing-significant', 'foo'), - False) - - def test_getAliasedArgSuccess(self): - msg = message.Message.fromPostArgs({'openid.ns.test': 'urn://foo', - 'openid.test.flub': 'bogus'}) - actual_uri = msg.getAliasedArg('ns.test', message.no_default) - self.assertEquals("urn://foo", actual_uri) - - def test_getAliasedArgFailure(self): - msg = message.Message.fromPostArgs({'openid.test.flub': 'bogus'}) - self.assertRaises(KeyError, - msg.getAliasedArg, 'ns.test', message.no_default) - - def test_getArg(self): - # Could reasonably return None instead of raising an - # exception. I'm not sure which one is more right, since this - # case should only happen when you're building a message from - # scratch and so have no default namespace. - self.failUnlessRaises(message.UndefinedOpenIDNamespace, - self.msg.getArg, message.OPENID_NS, 'foo') - - test_getArgBARE = mkGetArgTest(message.BARE_NS, 'foo') - test_getArgNS1 = mkGetArgTest(message.OPENID1_NS, 'foo') - test_getArgNS2 = mkGetArgTest(message.OPENID2_NS, 'foo') - test_getArgNS3 = mkGetArgTest('urn:nothing-significant', 'foo') - - def test_getArgs(self): - # Could reasonably return {} instead of raising an - # exception. I'm not sure which one is more right, since this - # case should only happen when you're building a message from - # scratch and so have no default namespace. - self.failUnlessRaises(message.UndefinedOpenIDNamespace, - self.msg.getArgs, message.OPENID_NS) - - def test_getArgsBARE(self): - self.failUnlessEqual(self.msg.getArgs(message.BARE_NS), {}) - - def test_getArgsNS1(self): - self.failUnlessEqual(self.msg.getArgs(message.OPENID1_NS), {}) - - def test_getArgsNS2(self): - self.failUnlessEqual(self.msg.getArgs(message.OPENID2_NS), {}) - - def test_getArgsNS3(self): - self.failUnlessEqual(self.msg.getArgs('urn:nothing-significant'), {}) - - def test_updateArgs(self): - self.failUnlessRaises(message.UndefinedOpenIDNamespace, - self.msg.updateArgs, message.OPENID_NS, - {'does not':'matter'}) - - def _test_updateArgsNS(self, ns): - update_args = { - 'Camper van Beethoven':'David Lowery', - 'Magnolia Electric Co.':'Jason Molina', - } - - self.failUnlessEqual(self.msg.getArgs(ns), {}) - self.msg.updateArgs(ns, update_args) - self.failUnlessEqual(self.msg.getArgs(ns), update_args) - - def test_updateArgsBARE(self): - self._test_updateArgsNS(message.BARE_NS) - - def test_updateArgsNS1(self): - self._test_updateArgsNS(message.OPENID1_NS) - - def test_updateArgsNS2(self): - self._test_updateArgsNS(message.OPENID2_NS) - - def test_updateArgsNS3(self): - self._test_updateArgsNS('urn:nothing-significant') - - def test_setArg(self): - self.failUnlessRaises(message.UndefinedOpenIDNamespace, - self.msg.setArg, message.OPENID_NS, - 'does not', 'matter') - - def _test_setArgNS(self, ns): - key = 'Camper van Beethoven' - value = 'David Lowery' - self.failUnlessEqual(self.msg.getArg(ns, key), None) - self.msg.setArg(ns, key, value) - self.failUnlessEqual(self.msg.getArg(ns, key), value) - - def test_setArgBARE(self): - self._test_setArgNS(message.BARE_NS) - - def test_setArgNS1(self): - self._test_setArgNS(message.OPENID1_NS) - - def test_setArgNS2(self): - self._test_setArgNS(message.OPENID2_NS) - - def test_setArgNS3(self): - self._test_setArgNS('urn:nothing-significant') - - def test_setArgToNone(self): - self.failUnlessRaises(AssertionError, self.msg.setArg, - message.OPENID1_NS, 'op_endpoint', None) - - def test_delArg(self): - # Could reasonably raise KeyError instead of raising - # UndefinedOpenIDNamespace. I'm not sure which one is more - # right, since this case should only happen when you're - # building a message from scratch and so have no default - # namespace. - self.failUnlessRaises(message.UndefinedOpenIDNamespace, - self.msg.delArg, message.OPENID_NS, 'key') - - def _test_delArgNS(self, ns): - key = 'Camper van Beethoven' - self.failUnlessRaises(KeyError, self.msg.delArg, ns, key) - - def test_delArgBARE(self): - self._test_delArgNS(message.BARE_NS) - - def test_delArgNS1(self): - self._test_delArgNS(message.OPENID1_NS) - - def test_delArgNS2(self): - self._test_delArgNS(message.OPENID2_NS) - - def test_delArgNS3(self): - self._test_delArgNS('urn:nothing-significant') - - def test_isOpenID1(self): - self.failIf(self.msg.isOpenID1()) - - def test_isOpenID2(self): - self.failIf(self.msg.isOpenID2()) - -class OpenID1MessageTest(unittest.TestCase): - def setUp(self): - self.msg = message.Message.fromPostArgs({'openid.mode':'error', - 'openid.error':'unit test'}) - - def test_toPostArgs(self): - self.failUnlessEqual(self.msg.toPostArgs(), - {'openid.mode':'error', - 'openid.error':'unit test'}) - - def test_toArgs(self): - self.failUnlessEqual(self.msg.toArgs(), {'mode':'error', - 'error':'unit test'}) - - def test_toKVForm(self): - self.failUnlessEqual(self.msg.toKVForm(), - 'error:unit test\nmode:error\n') - - def test_toURLEncoded(self): - self.failUnlessEqual(self.msg.toURLEncoded(), - 'openid.error=unit+test&openid.mode=error') - - def test_toURL(self): - base_url = 'http://base.url/' - actual = self.msg.toURL(base_url) - actual_base = actual[:len(base_url)] - self.failUnlessEqual(actual_base, base_url) - self.failUnlessEqual(actual[len(base_url)], '?') - query = actual[len(base_url) + 1:] - parsed = cgi.parse_qs(query) - self.failUnlessEqual(parsed, {'openid.mode':['error'], - 'openid.error':['unit test']}) - - def test_getOpenID(self): - self.failUnlessEqual(self.msg.getOpenIDNamespace(), message.OPENID1_NS) - - def test_getKeyOpenID(self): - self.failUnlessEqual(self.msg.getKey(message.OPENID_NS, 'mode'), - 'openid.mode') - - def test_getKeyBARE(self): - self.failUnlessEqual(self.msg.getKey(message.BARE_NS, 'mode'), 'mode') - - def test_getKeyNS1(self): - self.failUnlessEqual( - self.msg.getKey(message.OPENID1_NS, 'mode'), 'openid.mode') - - def test_getKeyNS2(self): - self.failUnlessEqual(self.msg.getKey(message.OPENID2_NS, 'mode'), None) - - def test_getKeyNS3(self): - self.failUnlessEqual( - self.msg.getKey('urn:nothing-significant', 'mode'), None) - - def test_hasKey(self): - self.failUnlessEqual(self.msg.hasKey(message.OPENID_NS, 'mode'), True) - - def test_hasKeyBARE(self): - self.failUnlessEqual(self.msg.hasKey(message.BARE_NS, 'mode'), False) - - def test_hasKeyNS1(self): - self.failUnlessEqual(self.msg.hasKey(message.OPENID1_NS, 'mode'), True) - - def test_hasKeyNS2(self): - self.failUnlessEqual( - self.msg.hasKey(message.OPENID2_NS, 'mode'), False) - - def test_hasKeyNS3(self): - self.failUnlessEqual( - self.msg.hasKey('urn:nothing-significant', 'mode'), False) - - test_getArgBARE = mkGetArgTest(message.BARE_NS, 'mode') - test_getArgNS = mkGetArgTest(message.OPENID_NS, 'mode', 'error') - test_getArgNS1 = mkGetArgTest(message.OPENID1_NS, 'mode', 'error') - test_getArgNS2 = mkGetArgTest(message.OPENID2_NS, 'mode') - test_getArgNS3 = mkGetArgTest('urn:nothing-significant', 'mode') - - def test_getArgs(self): - self.failUnlessEqual(self.msg.getArgs(message.OPENID_NS), - {'mode':'error', - 'error':'unit test', - }) - - def test_getArgsBARE(self): - self.failUnlessEqual(self.msg.getArgs(message.BARE_NS), {}) - - def test_getArgsNS1(self): - self.failUnlessEqual(self.msg.getArgs(message.OPENID1_NS), - {'mode':'error', - 'error':'unit test', - }) - - def test_getArgsNS2(self): - self.failUnlessEqual(self.msg.getArgs(message.OPENID2_NS), {}) - - def test_getArgsNS3(self): - self.failUnlessEqual(self.msg.getArgs('urn:nothing-significant'), {}) - - def _test_updateArgsNS(self, ns, before=None): - if before is None: - before = {} - update_args = { - 'Camper van Beethoven':'David Lowery', - 'Magnolia Electric Co.':'Jason Molina', - } - - self.failUnlessEqual(self.msg.getArgs(ns), before) - self.msg.updateArgs(ns, update_args) - after = dict(before) - after.update(update_args) - self.failUnlessEqual(self.msg.getArgs(ns), after) - - def test_updateArgs(self): - self._test_updateArgsNS(message.OPENID_NS, - before={'mode':'error', 'error':'unit test'}) - - def test_updateArgsBARE(self): - self._test_updateArgsNS(message.BARE_NS) - - def test_updateArgsNS1(self): - self._test_updateArgsNS(message.OPENID1_NS, - before={'mode':'error', 'error':'unit test'}) - - def test_updateArgsNS2(self): - self._test_updateArgsNS(message.OPENID2_NS) - - def test_updateArgsNS3(self): - self._test_updateArgsNS('urn:nothing-significant') - - def _test_setArgNS(self, ns): - key = 'Camper van Beethoven' - value = 'David Lowery' - self.failUnlessEqual(self.msg.getArg(ns, key), None) - self.msg.setArg(ns, key, value) - self.failUnlessEqual(self.msg.getArg(ns, key), value) - - def test_setArg(self): - self._test_setArgNS(message.OPENID_NS) - - def test_setArgBARE(self): - self._test_setArgNS(message.BARE_NS) - - def test_setArgNS1(self): - self._test_setArgNS(message.OPENID1_NS) - - def test_setArgNS2(self): - self._test_setArgNS(message.OPENID2_NS) - - def test_setArgNS3(self): - self._test_setArgNS('urn:nothing-significant') - - def _test_delArgNS(self, ns): - key = 'Camper van Beethoven' - value = 'David Lowery' - - self.failUnlessRaises(KeyError, self.msg.delArg, ns, key) - self.msg.setArg(ns, key, value) - self.failUnlessEqual(self.msg.getArg(ns, key), value) - self.msg.delArg(ns, key) - self.failUnlessEqual(self.msg.getArg(ns, key), None) - - def test_delArg(self): - self._test_delArgNS(message.OPENID_NS) - - def test_delArgBARE(self): - self._test_delArgNS(message.BARE_NS) - - def test_delArgNS1(self): - self._test_delArgNS(message.OPENID1_NS) - - def test_delArgNS2(self): - self._test_delArgNS(message.OPENID2_NS) - - def test_delArgNS3(self): - self._test_delArgNS('urn:nothing-significant') - - - def test_isOpenID1(self): - self.failUnless(self.msg.isOpenID1()) - - def test_isOpenID2(self): - self.failIf(self.msg.isOpenID2()) - -class OpenID1ExplicitMessageTest(unittest.TestCase): - def setUp(self): - self.msg = message.Message.fromPostArgs({'openid.mode':'error', - 'openid.error':'unit test', - 'openid.ns':message.OPENID1_NS - }) - - def test_toPostArgs(self): - self.failUnlessEqual(self.msg.toPostArgs(), - {'openid.mode':'error', - 'openid.error':'unit test', - 'openid.ns':message.OPENID1_NS - }) - - def test_toArgs(self): - self.failUnlessEqual(self.msg.toArgs(), {'mode':'error', - 'error':'unit test', - 'ns':message.OPENID1_NS}) - - def test_toKVForm(self): - self.failUnlessEqual(self.msg.toKVForm(), - 'error:unit test\nmode:error\nns:%s\n' - %message.OPENID1_NS) - - def test_toURLEncoded(self): - self.failUnlessEqual(self.msg.toURLEncoded(), - 'openid.error=unit+test&openid.mode=error&openid.ns=http%3A%2F%2Fopenid.net%2Fsignon%2F1.0') - - def test_toURL(self): - base_url = 'http://base.url/' - actual = self.msg.toURL(base_url) - actual_base = actual[:len(base_url)] - self.failUnlessEqual(actual_base, base_url) - self.failUnlessEqual(actual[len(base_url)], '?') - query = actual[len(base_url) + 1:] - parsed = cgi.parse_qs(query) - self.failUnlessEqual(parsed, {'openid.mode':['error'], - 'openid.error':['unit test'], - 'openid.ns':[message.OPENID1_NS] - }) - - def test_isOpenID1(self): - self.failUnless(self.msg.isOpenID1()) - - -class OpenID2MessageTest(unittest.TestCase): - def setUp(self): - self.msg = message.Message.fromPostArgs({'openid.mode':'error', - 'openid.error':'unit test', - 'openid.ns':message.OPENID2_NS - }) - self.msg.setArg(message.BARE_NS, "xey", "value") - - def test_toPostArgs(self): - self.failUnlessEqual(self.msg.toPostArgs(), - {'openid.mode':'error', - 'openid.error':'unit test', - 'openid.ns':message.OPENID2_NS, - 'xey': 'value', - }) - - def test_toArgs(self): - # This method can't tolerate BARE_NS. - self.msg.delArg(message.BARE_NS, "xey") - self.failUnlessEqual(self.msg.toArgs(), {'mode':'error', - 'error':'unit test', - 'ns':message.OPENID2_NS, - }) - - def test_toKVForm(self): - # Can't tolerate BARE_NS in kvform - self.msg.delArg(message.BARE_NS, "xey") - self.failUnlessEqual(self.msg.toKVForm(), - 'error:unit test\nmode:error\nns:%s\n' % - (message.OPENID2_NS,)) - - def _test_urlencoded(self, s): - expected = ('openid.error=unit+test&openid.mode=error&' - 'openid.ns=%s&xey=value' % ( - urllib.quote(message.OPENID2_NS, ''),)) - self.failUnlessEqual(s, expected) - - - def test_toURLEncoded(self): - self._test_urlencoded(self.msg.toURLEncoded()) - - def test_toURL(self): - base_url = 'http://base.url/' - actual = self.msg.toURL(base_url) - actual_base = actual[:len(base_url)] - self.failUnlessEqual(actual_base, base_url) - self.failUnlessEqual(actual[len(base_url)], '?') - query = actual[len(base_url) + 1:] - self._test_urlencoded(query) - - def test_getOpenID(self): - self.failUnlessEqual(self.msg.getOpenIDNamespace(), message.OPENID2_NS) - - def test_getKeyOpenID(self): - self.failUnlessEqual(self.msg.getKey(message.OPENID_NS, 'mode'), - 'openid.mode') - - def test_getKeyBARE(self): - self.failUnlessEqual(self.msg.getKey(message.BARE_NS, 'mode'), 'mode') - - def test_getKeyNS1(self): - self.failUnlessEqual( - self.msg.getKey(message.OPENID1_NS, 'mode'), None) - - def test_getKeyNS2(self): - self.failUnlessEqual( - self.msg.getKey(message.OPENID2_NS, 'mode'), 'openid.mode') - - def test_getKeyNS3(self): - self.failUnlessEqual( - self.msg.getKey('urn:nothing-significant', 'mode'), None) - - def test_hasKeyOpenID(self): - self.failUnlessEqual(self.msg.hasKey(message.OPENID_NS, 'mode'), True) - - def test_hasKeyBARE(self): - self.failUnlessEqual(self.msg.hasKey(message.BARE_NS, 'mode'), False) - - def test_hasKeyNS1(self): - self.failUnlessEqual( - self.msg.hasKey(message.OPENID1_NS, 'mode'), False) - - def test_hasKeyNS2(self): - self.failUnlessEqual( - self.msg.hasKey(message.OPENID2_NS, 'mode'), True) - - def test_hasKeyNS3(self): - self.failUnlessEqual( - self.msg.hasKey('urn:nothing-significant', 'mode'), False) - - test_getArgBARE = mkGetArgTest(message.BARE_NS, 'mode') - test_getArgNS = mkGetArgTest(message.OPENID_NS, 'mode', 'error') - test_getArgNS1 = mkGetArgTest(message.OPENID1_NS, 'mode') - test_getArgNS2 = mkGetArgTest(message.OPENID2_NS, 'mode', 'error') - test_getArgNS3 = mkGetArgTest('urn:nothing-significant', 'mode') - - def test_getArgsOpenID(self): - self.failUnlessEqual(self.msg.getArgs(message.OPENID_NS), - {'mode':'error', - 'error':'unit test', - }) - - def test_getArgsBARE(self): - self.failUnlessEqual(self.msg.getArgs(message.BARE_NS), - {'xey': 'value'}) - - def test_getArgsNS1(self): - self.failUnlessEqual(self.msg.getArgs(message.OPENID1_NS), {}) - - def test_getArgsNS2(self): - self.failUnlessEqual(self.msg.getArgs(message.OPENID2_NS), - {'mode':'error', - 'error':'unit test', - }) - - def test_getArgsNS3(self): - self.failUnlessEqual(self.msg.getArgs('urn:nothing-significant'), {}) - - def _test_updateArgsNS(self, ns, before=None): - if before is None: - before = {} - update_args = { - 'Camper van Beethoven':'David Lowery', - 'Magnolia Electric Co.':'Jason Molina', - } - - self.failUnlessEqual(self.msg.getArgs(ns), before) - self.msg.updateArgs(ns, update_args) - after = dict(before) - after.update(update_args) - self.failUnlessEqual(self.msg.getArgs(ns), after) - - def test_updateArgsOpenID(self): - self._test_updateArgsNS(message.OPENID_NS, - before={'mode':'error', 'error':'unit test'}) - - def test_updateArgsBARE(self): - self._test_updateArgsNS(message.BARE_NS, - before={'xey':'value'}) - - def test_updateArgsNS1(self): - self._test_updateArgsNS(message.OPENID1_NS) - - def test_updateArgsNS2(self): - self._test_updateArgsNS(message.OPENID2_NS, - before={'mode':'error', 'error':'unit test'}) - - def test_updateArgsNS3(self): - self._test_updateArgsNS('urn:nothing-significant') - - def _test_setArgNS(self, ns): - key = 'Camper van Beethoven' - value = 'David Lowery' - self.failUnlessEqual(self.msg.getArg(ns, key), None) - self.msg.setArg(ns, key, value) - self.failUnlessEqual(self.msg.getArg(ns, key), value) - - def test_setArgOpenID(self): - self._test_setArgNS(message.OPENID_NS) - - def test_setArgBARE(self): - self._test_setArgNS(message.BARE_NS) - - def test_setArgNS1(self): - self._test_setArgNS(message.OPENID1_NS) - - def test_setArgNS2(self): - self._test_setArgNS(message.OPENID2_NS) - - def test_setArgNS3(self): - self._test_setArgNS('urn:nothing-significant') - - def test_badAlias(self): - """Make sure dotted aliases and OpenID protocol fields are not - allowed as namespace aliases.""" - - for f in message.OPENID_PROTOCOL_FIELDS + ['dotted.alias']: - args = {'openid.ns.%s' % f: 'blah', - 'openid.%s.foo' % f: 'test'} - - # .fromPostArgs covers .fromPostArgs, .fromOpenIDArgs, - # ._fromOpenIDArgs, and .fromOpenIDArgs (since it calls - # .fromPostArgs). - self.failUnlessRaises(AssertionError, self.msg.fromPostArgs, - args) - - def test_mysterious_missing_namespace_bug(self): - """A failing test for bug #112""" - openid_args = { - 'assoc_handle': '{{HMAC-SHA256}{1211477242.29743}{v5cadg==}', - 'claimed_id': 'http://nerdbank.org/OPAffirmative/AffirmativeIdentityWithSregNoAssoc.aspx', - 'ns.sreg': 'http://openid.net/extensions/sreg/1.1', - 'response_nonce': '2008-05-22T17:27:22ZUoW5.\\NV', - 'signed': 'return_to,identity,claimed_id,op_endpoint,response_nonce,ns.sreg,sreg.email,sreg.nickname,assoc_handle', - 'sig': 'e3eGZ10+TNRZitgq5kQlk5KmTKzFaCRI8OrRoXyoFa4=', - 'mode': 'check_authentication', - 'op_endpoint': 'http://nerdbank.org/OPAffirmative/ProviderNoAssoc.aspx', - 'sreg.nickname': 'Andy', - 'return_to': 'http://localhost.localdomain:8001/process?janrain_nonce=2008-05-22T17%3A27%3A21ZnxHULd', - 'invalidate_handle': '{{HMAC-SHA1}{1211477241.92242}{H0akXw==}', - 'identity': 'http://nerdbank.org/OPAffirmative/AffirmativeIdentityWithSregNoAssoc.aspx', - 'sreg.email': 'a@b.com' - } - m = message.Message.fromOpenIDArgs(openid_args) - - self.failUnless(('http://openid.net/extensions/sreg/1.1', 'sreg') in - list(m.namespaces.iteritems())) - missing = [] - for k in openid_args['signed'].split(','): - if not ("openid."+k) in m.toPostArgs().keys(): - missing.append(k) - self.assertEqual([], missing, missing) - self.assertEqual(openid_args, m.toArgs()) - self.failUnless(m.isOpenID1()) - - def test_112B(self): - args = {'openid.assoc_handle': 'fa1f5ff0-cde4-11dc-a183-3714bfd55ca8', - 'openid.claimed_id': 'http://binkley.lan/user/test01', - 'openid.identity': 'http://test01.binkley.lan/', - 'openid.mode': 'id_res', - 'openid.ns': 'http://specs.openid.net/auth/2.0', - 'openid.ns.pape': 'http://specs.openid.net/extensions/pape/1.0', - 'openid.op_endpoint': 'http://binkley.lan/server', - 'openid.pape.auth_policies': 'none', - 'openid.pape.auth_time': '2008-01-28T20:42:36Z', - 'openid.pape.nist_auth_level': '0', - 'openid.response_nonce': '2008-01-28T21:07:04Z99Q=', - 'openid.return_to': 'http://binkley.lan:8001/process?janrain_nonce=2008-01-28T21%3A07%3A02Z0tMIKx', - 'openid.sig': 'YJlWH4U6SroB1HoPkmEKx9AyGGg=', - 'openid.signed': 'assoc_handle,identity,response_nonce,return_to,claimed_id,op_endpoint,pape.auth_time,ns.pape,pape.nist_auth_level,pape.auth_policies' - } - m = message.Message.fromPostArgs(args) - missing = [] - for k in args['openid.signed'].split(','): - if not ("openid."+k) in m.toPostArgs().keys(): - missing.append(k) - self.assertEqual([], missing, missing) - self.assertEqual(args, m.toPostArgs()) - self.failUnless(m.isOpenID2()) - - def test_implicit_sreg_ns(self): - openid_args = { - 'sreg.email': 'a@b.com' - } - m = message.Message.fromOpenIDArgs(openid_args) - self.failUnless((sreg.ns_uri, 'sreg') in - list(m.namespaces.iteritems())) - self.assertEqual('a@b.com', m.getArg(sreg.ns_uri, 'email')) - self.assertEqual(openid_args, m.toArgs()) - self.failUnless(m.isOpenID1()) - - def _test_delArgNS(self, ns): - key = 'Camper van Beethoven' - value = 'David Lowery' - - self.failUnlessRaises(KeyError, self.msg.delArg, ns, key) - self.msg.setArg(ns, key, value) - self.failUnlessEqual(self.msg.getArg(ns, key), value) - self.msg.delArg(ns, key) - self.failUnlessEqual(self.msg.getArg(ns, key), None) - - def test_delArgOpenID(self): - self._test_delArgNS(message.OPENID_NS) - - def test_delArgBARE(self): - self._test_delArgNS(message.BARE_NS) - - def test_delArgNS1(self): - self._test_delArgNS(message.OPENID1_NS) - - def test_delArgNS2(self): - self._test_delArgNS(message.OPENID2_NS) - - def test_delArgNS3(self): - self._test_delArgNS('urn:nothing-significant') - - def test_overwriteExtensionArg(self): - ns = 'urn:unittest_extension' - key = 'mykey' - value_1 = 'value_1' - value_2 = 'value_2' - - self.msg.setArg(ns, key, value_1) - self.failUnless(self.msg.getArg(ns, key) == value_1) - self.msg.setArg(ns, key, value_2) - self.failUnless(self.msg.getArg(ns, key) == value_2) - - def test_argList(self): - self.failUnlessRaises(TypeError, self.msg.fromPostArgs, - {'arg': [1, 2, 3]}) - - def test_isOpenID1(self): - self.failIf(self.msg.isOpenID1()) - - def test_isOpenID2(self): - self.failUnless(self.msg.isOpenID2()) - -class MessageTest(unittest.TestCase): - def setUp(self): - self.postargs = { - 'openid.ns': message.OPENID2_NS, - 'openid.mode': 'checkid_setup', - 'openid.identity': 'http://bogus.example.invalid:port/', - 'openid.assoc_handle': 'FLUB', - 'openid.return_to': 'Neverland', - } - - self.action_url = 'scheme://host:port/path?query' - - self.form_tag_attrs = { - 'company': 'janrain', - 'class': 'fancyCSS', - } - - self.submit_text = 'GO!' - - ### Expected data regardless of input - - self.required_form_attrs = { - 'accept-charset':'UTF-8', - 'enctype':'application/x-www-form-urlencoded', - 'method': 'post', - } - - def _checkForm(self, html, message_, action_url, - form_tag_attrs, submit_text): - E = oidutil.importElementTree() - - # Build element tree from HTML source - input_tree = E.ElementTree(E.fromstring(html)) - - # Get root element - form = input_tree.getroot() - - # Check required form attributes - for k, v in self.required_form_attrs.iteritems(): - assert form.attrib[k] == v, \ - "Expected '%s' for required form attribute '%s', got '%s'" % \ - (v, k, form.attrib[k]) - - # Check extra form attributes - for k, v in form_tag_attrs.iteritems(): - - # Skip attributes that already passed the required - # attribute check, since they should be ignored by the - # form generation code. - if k in self.required_form_attrs: - continue - - assert form.attrib[k] == v, \ - "Form attribute '%s' should be '%s', found '%s'" % \ - (k, v, form.attrib[k]) - - # Check hidden fields against post args - hiddens = [e for e in form \ - if e.tag.upper() == 'INPUT' and \ - e.attrib['type'].upper() == 'HIDDEN'] - - # For each post arg, make sure there is a hidden with that - # value. Make sure there are no other hiddens. - for name, value in message_.toPostArgs().iteritems(): - for e in hiddens: - if e.attrib['name'] == name: - assert e.attrib['value'] == value, \ - "Expected value of hidden input '%s' to be '%s', got '%s'" % \ - (e.attrib['name'], value, e.attrib['value']) - break - else: - self.fail("Post arg '%s' not found in form" % (name,)) - - for e in hiddens: - assert e.attrib['name'] in message_.toPostArgs().keys(), \ - "Form element for '%s' not in " + \ - "original message" % (e.attrib['name']) - - # Check action URL - assert form.attrib['action'] == action_url, \ - "Expected form 'action' to be '%s', got '%s'" % \ - (action_url, form.attrib['action']) - - # Check submit text - submits = [e for e in form \ - if e.tag.upper() == 'INPUT' and \ - e.attrib['type'].upper() == 'SUBMIT'] - - assert len(submits) == 1, \ - "Expected only one 'input' with type = 'submit', got %d" % \ - (len(submits),) - - assert submits[0].attrib['value'] == submit_text, \ - "Expected submit value to be '%s', got '%s'" % \ - (submit_text, submits[0].attrib['value']) - - def test_toFormMarkup(self): - m = message.Message.fromPostArgs(self.postargs) - html = m.toFormMarkup(self.action_url, self.form_tag_attrs, - self.submit_text) - self._checkForm(html, m, self.action_url, - self.form_tag_attrs, self.submit_text) - - def test_overrideMethod(self): - """Be sure that caller cannot change form method to GET.""" - m = message.Message.fromPostArgs(self.postargs) - - tag_attrs = dict(self.form_tag_attrs) - tag_attrs['method'] = 'GET' - - html = m.toFormMarkup(self.action_url, self.form_tag_attrs, - self.submit_text) - self._checkForm(html, m, self.action_url, - self.form_tag_attrs, self.submit_text) - - def test_overrideRequired(self): - """Be sure that caller CANNOT change the form charset for - encoding type.""" - m = message.Message.fromPostArgs(self.postargs) - - tag_attrs = dict(self.form_tag_attrs) - tag_attrs['accept-charset'] = 'UCS4' - tag_attrs['enctype'] = 'invalid/x-broken' - - html = m.toFormMarkup(self.action_url, tag_attrs, - self.submit_text) - self._checkForm(html, m, self.action_url, - tag_attrs, self.submit_text) - - - def test_setOpenIDNamespace_invalid(self): - m = message.Message() - invalid_things = [ - # Empty string is not okay here. - '', - # Good guess! But wrong. - 'http://openid.net/signon/2.0', - # What? - u'http://specs%\\\r2Eopenid.net/auth/2.0', - # Too much escapings! - 'http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0', - # This is a Type URI, not a openid.ns value. - 'http://specs.openid.net/auth/2.0/signon', - ] - - for x in invalid_things: - self.failUnlessRaises(message.InvalidOpenIDNamespace, - m.setOpenIDNamespace, x, False) - - - def test_isOpenID1(self): - v1_namespaces = [ - # Yes, there are two of them. - 'http://openid.net/signon/1.1', - 'http://openid.net/signon/1.0', - ] - - for ns in v1_namespaces: - m = message.Message(ns) - self.failUnless(m.isOpenID1(), "%r not recognized as OpenID 1" % - (ns,)) - self.failUnlessEqual(ns, m.getOpenIDNamespace()) - self.failUnless(m.namespaces.isImplicit(ns), - m.namespaces.getNamespaceURI(message.NULL_NAMESPACE)) - - def test_isOpenID2(self): - ns = 'http://specs.openid.net/auth/2.0' - m = message.Message(ns) - self.failUnless(m.isOpenID2()) - self.failIf(m.namespaces.isImplicit(message.NULL_NAMESPACE)) - self.failUnlessEqual(ns, m.getOpenIDNamespace()) - - def test_setOpenIDNamespace_explicit(self): - m = message.Message() - m.setOpenIDNamespace(message.THE_OTHER_OPENID1_NS, False) - self.failIf(m.namespaces.isImplicit(message.THE_OTHER_OPENID1_NS)) - - def test_setOpenIDNamespace_implicit(self): - m = message.Message() - m.setOpenIDNamespace(message.THE_OTHER_OPENID1_NS, True) - self.failUnless(m.namespaces.isImplicit(message.THE_OTHER_OPENID1_NS)) - - - def test_explicitOpenID11NSSerialzation(self): - m = message.Message() - m.setOpenIDNamespace(message.THE_OTHER_OPENID1_NS, implicit=False) - - post_args = m.toPostArgs() - self.failUnlessEqual(post_args, - {'openid.ns':message.THE_OTHER_OPENID1_NS}) - - def test_fromPostArgs_ns11(self): - # An example of the stuff that some Drupal installations send us, - # which includes openid.ns but is 1.1. - query = { - u'openid.assoc_handle': u'', - u'openid.claimed_id': u'http://foobar.invalid/', - u'openid.identity': u'http://foobar.myopenid.com', - u'openid.mode': u'checkid_setup', - u'openid.ns': u'http://openid.net/signon/1.1', - u'openid.ns.sreg': u'http://openid.net/extensions/sreg/1.1', - u'openid.return_to': u'http://drupal.invalid/return_to', - u'openid.sreg.required': u'nickname,email', - u'openid.trust_root': u'http://drupal.invalid', - } - m = message.Message.fromPostArgs(query) - self.failUnless(m.isOpenID1()) - - - -class NamespaceMapTest(unittest.TestCase): - def test_onealias(self): - nsm = message.NamespaceMap() - uri = 'http://example.com/foo' - alias = "foo" - nsm.addAlias(uri, alias) - self.failUnless(nsm.getNamespaceURI(alias) == uri) - self.failUnless(nsm.getAlias(uri) == alias) - - def test_iteration(self): - nsm = message.NamespaceMap() - uripat = 'http://example.com/foo%r' - - nsm.add(uripat%0) - for n in range(1,23): - self.failUnless(uripat%(n-1) in nsm) - self.failUnless(nsm.isDefined(uripat%(n-1))) - nsm.add(uripat%n) - - for (uri, alias) in nsm.iteritems(): - self.failUnless(uri[22:]==alias[3:]) - - i=0 - it = nsm.iterAliases() - try: - while True: - it.next() - i += 1 - except StopIteration: - self.failUnless(i == 23) - - i=0 - it = nsm.iterNamespaceURIs() - try: - while True: - it.next() - i += 1 - except StopIteration: - self.failUnless(i == 23) - - -if __name__ == '__main__': - unittest.main() diff --git a/askbot/deps/openid/test/test_negotiation.py b/askbot/deps/openid/test/test_negotiation.py deleted file mode 100644 index c572f9ec..00000000 --- a/askbot/deps/openid/test/test_negotiation.py +++ /dev/null @@ -1,271 +0,0 @@ - -import unittest -from support import CatchLogs - -from askbot.deps.openid.message import Message, OPENID2_NS, OPENID1_NS, OPENID_NS -from askbot.deps.openid import association -from askbot.deps.openid.consumer.consumer import GenericConsumer, ServerError -from askbot.deps.openid.consumer.discover import OpenIDServiceEndpoint, OPENID_2_0_TYPE - -class ErrorRaisingConsumer(GenericConsumer): - """ - A consumer whose _requestAssocation will return predefined results - instead of trying to actually perform association requests. - """ - - # The list of objects to be returned by successive calls to - # _requestAssocation. Each call will pop the first element from - # this list and return it to _negotiateAssociation. If the - # element is a Message object, it will be wrapped in a ServerError - # exception. Otherwise it will be returned as-is. - return_messages = [] - - def _requestAssociation(self, endpoint, assoc_type, session_type): - m = self.return_messages.pop(0) - if isinstance(m, Message): - raise ServerError.fromMessage(m) - else: - return m - -class TestOpenID2SessionNegotiation(unittest.TestCase, CatchLogs): - """ - Test the session type negotiation behavior of an OpenID 2 - consumer. - """ - def setUp(self): - CatchLogs.setUp(self) - self.consumer = ErrorRaisingConsumer(store=None) - - self.endpoint = OpenIDServiceEndpoint() - self.endpoint.type_uris = [OPENID_2_0_TYPE] - self.endpoint.server_url = 'bogus' - - def testBadResponse(self): - """ - Test the case where the response to an associate request is a - server error or is otherwise undecipherable. - """ - self.consumer.return_messages = [Message(self.endpoint.preferredNamespace())] - self.assertEqual(self.consumer._negotiateAssociation(self.endpoint), None) - self.failUnlessLogMatches('Server error when requesting an association') - - def testEmptyAssocType(self): - """ - Test the case where the association type (assoc_type) returned - in an unsupported-type response is absent. - """ - msg = Message(self.endpoint.preferredNamespace()) - msg.setArg(OPENID_NS, 'error', 'Unsupported type') - msg.setArg(OPENID_NS, 'error_code', 'unsupported-type') - # not set: msg.delArg(OPENID_NS, 'assoc_type') - msg.setArg(OPENID_NS, 'session_type', 'new-session-type') - - self.consumer.return_messages = [msg] - self.assertEqual(self.consumer._negotiateAssociation(self.endpoint), None) - - self.failUnlessLogMatches('Unsupported association type', - 'Server responded with unsupported association ' + - 'session but did not supply a fallback.') - - def testEmptySessionType(self): - """ - Test the case where the session type (session_type) returned - in an unsupported-type response is absent. - """ - msg = Message(self.endpoint.preferredNamespace()) - msg.setArg(OPENID_NS, 'error', 'Unsupported type') - msg.setArg(OPENID_NS, 'error_code', 'unsupported-type') - msg.setArg(OPENID_NS, 'assoc_type', 'new-assoc-type') - # not set: msg.setArg(OPENID_NS, 'session_type', None) - - self.consumer.return_messages = [msg] - self.assertEqual(self.consumer._negotiateAssociation(self.endpoint), None) - - self.failUnlessLogMatches('Unsupported association type', - 'Server responded with unsupported association ' + - 'session but did not supply a fallback.') - - def testNotAllowed(self): - """ - Test the case where an unsupported-type response specifies a - preferred (assoc_type, session_type) combination that is not - allowed by the consumer's SessionNegotiator. - """ - allowed_types = [] - - negotiator = association.SessionNegotiator(allowed_types) - self.consumer.negotiator = negotiator - - msg = Message(self.endpoint.preferredNamespace()) - msg.setArg(OPENID_NS, 'error', 'Unsupported type') - msg.setArg(OPENID_NS, 'error_code', 'unsupported-type') - msg.setArg(OPENID_NS, 'assoc_type', 'not-allowed') - msg.setArg(OPENID_NS, 'session_type', 'not-allowed') - - self.consumer.return_messages = [msg] - self.assertEqual(self.consumer._negotiateAssociation(self.endpoint), None) - - self.failUnlessLogMatches('Unsupported association type', - 'Server sent unsupported session/association type:') - - def testUnsupportedWithRetry(self): - """ - Test the case where an unsupported-type response triggers a - retry to get an association with the new preferred type. - """ - msg = Message(self.endpoint.preferredNamespace()) - msg.setArg(OPENID_NS, 'error', 'Unsupported type') - msg.setArg(OPENID_NS, 'error_code', 'unsupported-type') - msg.setArg(OPENID_NS, 'assoc_type', 'HMAC-SHA1') - msg.setArg(OPENID_NS, 'session_type', 'DH-SHA1') - - assoc = association.Association( - 'handle', 'secret', 'issued', 10000, 'HMAC-SHA1') - - self.consumer.return_messages = [msg, assoc] - self.failUnless(self.consumer._negotiateAssociation(self.endpoint) is assoc) - - self.failUnlessLogMatches('Unsupported association type') - - def testUnsupportedWithRetryAndFail(self): - """ - Test the case where an unsupported-typ response triggers a - retry, but the retry fails and None is returned instead. - """ - msg = Message(self.endpoint.preferredNamespace()) - msg.setArg(OPENID_NS, 'error', 'Unsupported type') - msg.setArg(OPENID_NS, 'error_code', 'unsupported-type') - msg.setArg(OPENID_NS, 'assoc_type', 'HMAC-SHA1') - msg.setArg(OPENID_NS, 'session_type', 'DH-SHA1') - - self.consumer.return_messages = [msg, - Message(self.endpoint.preferredNamespace())] - - self.failUnlessEqual(self.consumer._negotiateAssociation(self.endpoint), None) - - self.failUnlessLogMatches('Unsupported association type', - 'Server %s refused' % (self.endpoint.server_url)) - - def testValid(self): - """ - Test the valid case, wherein an association is returned on the - first attempt to get one. - """ - assoc = association.Association( - 'handle', 'secret', 'issued', 10000, 'HMAC-SHA1') - - self.consumer.return_messages = [assoc] - self.failUnless(self.consumer._negotiateAssociation(self.endpoint) is assoc) - self.failUnlessLogEmpty() - -class TestOpenID1SessionNegotiation(unittest.TestCase, CatchLogs): - """ - Tests for the OpenID 1 consumer association session behavior. See - the docs for TestOpenID2SessionNegotiation. Notice that this - class is not a subclass of the OpenID 2 tests. Instead, it uses - many of the same inputs but inspects the log messages logged with - oidutil.log. See the calls to self.failUnlessLogMatches. Some of - these tests pass openid2-style messages to the openid 1 - association processing logic to be sure it ignores the extra data. - """ - def setUp(self): - CatchLogs.setUp(self) - self.consumer = ErrorRaisingConsumer(store=None) - - self.endpoint = OpenIDServiceEndpoint() - self.endpoint.type_uris = [OPENID1_NS] - self.endpoint.server_url = 'bogus' - - def testBadResponse(self): - self.consumer.return_messages = [Message(self.endpoint.preferredNamespace())] - self.assertEqual(self.consumer._negotiateAssociation(self.endpoint), None) - self.failUnlessLogMatches('Server error when requesting an association') - - def testEmptyAssocType(self): - msg = Message(self.endpoint.preferredNamespace()) - msg.setArg(OPENID_NS, 'error', 'Unsupported type') - msg.setArg(OPENID_NS, 'error_code', 'unsupported-type') - # not set: msg.setArg(OPENID_NS, 'assoc_type', None) - msg.setArg(OPENID_NS, 'session_type', 'new-session-type') - - self.consumer.return_messages = [msg] - self.assertEqual(self.consumer._negotiateAssociation(self.endpoint), None) - - self.failUnlessLogMatches('Server error when requesting an association') - - def testEmptySessionType(self): - msg = Message(self.endpoint.preferredNamespace()) - msg.setArg(OPENID_NS, 'error', 'Unsupported type') - msg.setArg(OPENID_NS, 'error_code', 'unsupported-type') - msg.setArg(OPENID_NS, 'assoc_type', 'new-assoc-type') - # not set: msg.setArg(OPENID_NS, 'session_type', None) - - self.consumer.return_messages = [msg] - self.assertEqual(self.consumer._negotiateAssociation(self.endpoint), None) - - self.failUnlessLogMatches('Server error when requesting an association') - - def testNotAllowed(self): - allowed_types = [] - - negotiator = association.SessionNegotiator(allowed_types) - self.consumer.negotiator = negotiator - - msg = Message(self.endpoint.preferredNamespace()) - msg.setArg(OPENID_NS, 'error', 'Unsupported type') - msg.setArg(OPENID_NS, 'error_code', 'unsupported-type') - msg.setArg(OPENID_NS, 'assoc_type', 'not-allowed') - msg.setArg(OPENID_NS, 'session_type', 'not-allowed') - - self.consumer.return_messages = [msg] - self.assertEqual(self.consumer._negotiateAssociation(self.endpoint), None) - - self.failUnlessLogMatches('Server error when requesting an association') - - def testUnsupportedWithRetry(self): - msg = Message(self.endpoint.preferredNamespace()) - msg.setArg(OPENID_NS, 'error', 'Unsupported type') - msg.setArg(OPENID_NS, 'error_code', 'unsupported-type') - msg.setArg(OPENID_NS, 'assoc_type', 'HMAC-SHA1') - msg.setArg(OPENID_NS, 'session_type', 'DH-SHA1') - - assoc = association.Association( - 'handle', 'secret', 'issued', 10000, 'HMAC-SHA1') - - self.consumer.return_messages = [msg, assoc] - self.failUnless(self.consumer._negotiateAssociation(self.endpoint) is None) - - self.failUnlessLogMatches('Server error when requesting an association') - - def testValid(self): - assoc = association.Association( - 'handle', 'secret', 'issued', 10000, 'HMAC-SHA1') - - self.consumer.return_messages = [assoc] - self.failUnless(self.consumer._negotiateAssociation(self.endpoint) is assoc) - self.failUnlessLogEmpty() - -class TestNegotiatorBehaviors(unittest.TestCase, CatchLogs): - def setUp(self): - self.allowed_types = [ - ('HMAC-SHA1', 'no-encryption'), - ('HMAC-SHA256', 'no-encryption'), - ] - - self.n = association.SessionNegotiator(self.allowed_types) - - def testAddAllowedTypeNoSessionTypes(self): - self.assertRaises(ValueError, self.n.addAllowedType, 'invalid') - - def testAddAllowedTypeBadSessionType(self): - self.assertRaises(ValueError, self.n.addAllowedType, 'assoc1', 'invalid') - - def testAddAllowedTypeContents(self): - assoc_type = 'HMAC-SHA1' - self.failUnless(self.n.addAllowedType(assoc_type) is None) - - for typ in association.getSessionTypes(assoc_type): - self.failUnless((assoc_type, typ) in self.n.allowed_types) - -if __name__ == '__main__': - unittest.main() diff --git a/askbot/deps/openid/test/test_nonce.py b/askbot/deps/openid/test/test_nonce.py deleted file mode 100644 index 5e5a13be..00000000 --- a/askbot/deps/openid/test/test_nonce.py +++ /dev/null @@ -1,104 +0,0 @@ -from askbot.deps.openid.test import datadriven -import time -import unittest -import re - -from askbot.deps.openid.store.nonce import \ - mkNonce, \ - split as splitNonce, \ - checkTimestamp - -nonce_re = re.compile(r'\A\d{4}-\d\d-\d\dT\d\d:\d\d:\d\dZ') - -class NonceTest(unittest.TestCase): - def test_mkNonce(self): - nonce = mkNonce() - self.failUnless(nonce_re.match(nonce)) - self.failUnless(len(nonce) == 26) - - def test_mkNonce_when(self): - nonce = mkNonce(0) - self.failUnless(nonce_re.match(nonce)) - self.failUnless(nonce.startswith('1970-01-01T00:00:00Z')) - self.failUnless(len(nonce) == 26) - - def test_splitNonce(self): - s = '1970-01-01T00:00:00Z' - expected_t = 0 - expected_salt = '' - actual_t, actual_salt = splitNonce(s) - self.failUnlessEqual(expected_t, actual_t) - self.failUnlessEqual(expected_salt, actual_salt) - - def test_mkSplit(self): - t = 42 - nonce_str = mkNonce(t) - self.failUnless(nonce_re.match(nonce_str)) - et, salt = splitNonce(nonce_str) - self.failUnlessEqual(len(salt), 6) - self.failUnlessEqual(et, t) - -class BadSplitTest(datadriven.DataDrivenTestCase): - cases = [ - '', - '1970-01-01T00:00:00+1:00', - '1969-01-01T00:00:00Z', - '1970-00-01T00:00:00Z', - '1970.01-01T00:00:00Z', - 'Thu Sep 7 13:29:31 PDT 2006', - 'monkeys', - ] - - def __init__(self, nonce_str): - datadriven.DataDrivenTestCase.__init__(self, nonce_str) - self.nonce_str = nonce_str - - def runOneTest(self): - self.failUnlessRaises(ValueError, splitNonce, self.nonce_str) - -class CheckTimestampTest(datadriven.DataDrivenTestCase): - cases = [ - # exact, no allowed skew - ('1970-01-01T00:00:00Z', 0, 0, True), - - # exact, large skew - ('1970-01-01T00:00:00Z', 1000, 0, True), - - # no allowed skew, one second old - ('1970-01-01T00:00:00Z', 0, 1, False), - - # many seconds old, outside of skew - ('1970-01-01T00:00:00Z', 10, 50, False), - - # one second old, one second skew allowed - ('1970-01-01T00:00:00Z', 1, 1, True), - - # One second in the future, one second skew allowed - ('1970-01-01T00:00:02Z', 1, 1, True), - - # two seconds in the future, one second skew allowed - ('1970-01-01T00:00:02Z', 1, 0, False), - - # malformed nonce string - ('monkeys', 0, 0, False), - ] - - def __init__(self, nonce_string, allowed_skew, now, expected): - datadriven.DataDrivenTestCase.__init__( - self, repr((nonce_string, allowed_skew, now))) - self.nonce_string = nonce_string - self.allowed_skew = allowed_skew - self.now = now - self.expected = expected - - def runOneTest(self): - actual = checkTimestamp(self.nonce_string, self.allowed_skew, self.now) - self.failUnlessEqual(bool(self.expected), bool(actual)) - -def pyUnitTests(): - return datadriven.loadTests(__name__) - -if __name__ == '__main__': - suite = pyUnitTests() - runner = unittest.TextTestRunner() - runner.run(suite) diff --git a/askbot/deps/openid/test/test_openidyadis.py b/askbot/deps/openid/test/test_openidyadis.py deleted file mode 100644 index dcebf186..00000000 --- a/askbot/deps/openid/test/test_openidyadis.py +++ /dev/null @@ -1,164 +0,0 @@ -import unittest -from askbot.deps.openid.consumer.discover import \ - OpenIDServiceEndpoint, OPENID_1_1_TYPE, OPENID_1_0_TYPE - -from askbot.deps.openid.yadis.services import applyFilter - - -XRDS_BOILERPLATE = '''\ -<?xml version="1.0" encoding="UTF-8"?> -<xrds:XRDS xmlns:xrds="xri://$xrds" - xmlns="xri://$xrd*($v*2.0)" - xmlns:openid="http://openid.net/xmlns/1.0"> - <XRD> -%s\ - </XRD> -</xrds:XRDS> -''' - -def mkXRDS(services): - return XRDS_BOILERPLATE % (services,) - -def mkService(uris=None, type_uris=None, local_id=None, dent=' '): - chunks = [dent, '<Service>\n'] - dent2 = dent + ' ' - if type_uris: - for type_uri in type_uris: - chunks.extend([dent2 + '<Type>', type_uri, '</Type>\n']) - - if uris: - for uri in uris: - if type(uri) is tuple: - uri, prio = uri - else: - prio = None - - chunks.extend([dent2, '<URI']) - if prio is not None: - chunks.extend([' priority="', str(prio), '"']) - chunks.extend(['>', uri, '</URI>\n']) - - if local_id: - chunks.extend( - [dent2, '<openid:Delegate>', local_id, '</openid:Delegate>\n']) - - chunks.extend([dent, '</Service>\n']) - - return ''.join(chunks) - -# Different sets of server URLs for use in the URI tag -server_url_options = [ - [], # This case should not generate an endpoint object - ['http://server.url/'], - ['https://server.url/'], - ['https://server.url/', 'http://server.url/'], - ['https://server.url/', - 'http://server.url/', - 'http://example.server.url/'], - ] - -# Used for generating test data -def subsets(l): - """Generate all non-empty sublists of a list""" - subsets_list = [[]] - for x in l: - subsets_list += [[x] + t for t in subsets_list] - return subsets_list - -# A couple of example extension type URIs. These are not at all -# official, but are just here for testing. -ext_types = [ - 'http://janrain.com/extension/blah', - 'http://openid.net/sreg/1.0', - ] - -# All valid combinations of Type tags that should produce an OpenID endpoint -type_uri_options = [ - exts + ts - - # All non-empty sublists of the valid OpenID type URIs - for ts in subsets([OPENID_1_0_TYPE, OPENID_1_1_TYPE]) - if ts - - # All combinations of extension types (including empty extenstion list) - for exts in subsets(ext_types) - ] - -# Range of valid Delegate tag values for generating test data -local_id_options = [ - None, - 'http://vanity.domain/', - 'https://somewhere/yadis/', - ] - -# All combinations of valid URIs, Type URIs and Delegate tags -data = [ - (uris, type_uris, local_id) - for uris in server_url_options - for type_uris in type_uri_options - for local_id in local_id_options - ] - -class OpenIDYadisTest(unittest.TestCase): - def __init__(self, uris, type_uris, local_id): - unittest.TestCase.__init__(self) - self.uris = uris - self.type_uris = type_uris - self.local_id = local_id - - def shortDescription(self): - # XXX: - return 'Successful OpenID Yadis parsing case' - - def setUp(self): - self.yadis_url = 'http://unit.test/' - - # Create an XRDS document to parse - services = mkService(uris=self.uris, - type_uris=self.type_uris, - local_id=self.local_id) - self.xrds = mkXRDS(services) - - def runTest(self): - # Parse into endpoint objects that we will check - endpoints = applyFilter( - self.yadis_url, self.xrds, OpenIDServiceEndpoint) - - # make sure there are the same number of endpoints as - # URIs. This assumes that the type_uris contains at least one - # OpenID type. - self.failUnlessEqual(len(self.uris), len(endpoints)) - - # So that we can check equality on the endpoint types - type_uris = list(self.type_uris) - type_uris.sort() - - seen_uris = [] - for endpoint in endpoints: - seen_uris.append(endpoint.server_url) - - # All endpoints will have same yadis_url - self.failUnlessEqual(self.yadis_url, endpoint.claimed_id) - - # and local_id - self.failUnlessEqual(self.local_id, endpoint.local_id) - - # and types - actual_types = list(endpoint.type_uris) - actual_types.sort() - self.failUnlessEqual(actual_types, type_uris) - - # So that they will compare equal, because we don't care what - # order they are in - seen_uris.sort() - uris = list(self.uris) - uris.sort() - - # Make sure we saw all URIs, and saw each one once - self.failUnlessEqual(uris, seen_uris) - -def pyUnitTests(): - cases = [] - for args in data: - cases.append(OpenIDYadisTest(*args)) - return unittest.TestSuite(cases) diff --git a/askbot/deps/openid/test/test_pape.py b/askbot/deps/openid/test/test_pape.py deleted file mode 100644 index c54fb485..00000000 --- a/askbot/deps/openid/test/test_pape.py +++ /dev/null @@ -1,9 +0,0 @@ - -from askbot.deps.openid.extensions import pape - -import unittest - -class PapeImportTestCase(unittest.TestCase): - def test_version(self): - from askbot.deps.openid.extensions.draft import pape5 - self.assert_(pape is pape5) diff --git a/askbot/deps/openid/test/test_pape_draft2.py b/askbot/deps/openid/test/test_pape_draft2.py deleted file mode 100644 index 7458a775..00000000 --- a/askbot/deps/openid/test/test_pape_draft2.py +++ /dev/null @@ -1,217 +0,0 @@ - -from askbot.deps.openid.extensions.draft import pape2 as pape -from askbot.deps.openid.message import * -from askbot.deps.openid.server import server - -import unittest - -class PapeRequestTestCase(unittest.TestCase): - def setUp(self): - self.req = pape.Request() - - def test_construct(self): - self.failUnlessEqual([], self.req.preferred_auth_policies) - self.failUnlessEqual(None, self.req.max_auth_age) - self.failUnlessEqual('pape', self.req.ns_alias) - - req2 = pape.Request([pape.AUTH_MULTI_FACTOR], 1000) - self.failUnlessEqual([pape.AUTH_MULTI_FACTOR], req2.preferred_auth_policies) - self.failUnlessEqual(1000, req2.max_auth_age) - - def test_add_policy_uri(self): - self.failUnlessEqual([], self.req.preferred_auth_policies) - self.req.addPolicyURI(pape.AUTH_MULTI_FACTOR) - self.failUnlessEqual([pape.AUTH_MULTI_FACTOR], self.req.preferred_auth_policies) - self.req.addPolicyURI(pape.AUTH_MULTI_FACTOR) - self.failUnlessEqual([pape.AUTH_MULTI_FACTOR], self.req.preferred_auth_policies) - self.req.addPolicyURI(pape.AUTH_PHISHING_RESISTANT) - self.failUnlessEqual([pape.AUTH_MULTI_FACTOR, pape.AUTH_PHISHING_RESISTANT], - self.req.preferred_auth_policies) - self.req.addPolicyURI(pape.AUTH_MULTI_FACTOR) - self.failUnlessEqual([pape.AUTH_MULTI_FACTOR, pape.AUTH_PHISHING_RESISTANT], - self.req.preferred_auth_policies) - - def test_getExtensionArgs(self): - self.failUnlessEqual({'preferred_auth_policies': ''}, self.req.getExtensionArgs()) - self.req.addPolicyURI('http://uri') - self.failUnlessEqual({'preferred_auth_policies': 'http://uri'}, self.req.getExtensionArgs()) - self.req.addPolicyURI('http://zig') - self.failUnlessEqual({'preferred_auth_policies': 'http://uri http://zig'}, self.req.getExtensionArgs()) - self.req.max_auth_age = 789 - self.failUnlessEqual({'preferred_auth_policies': 'http://uri http://zig', 'max_auth_age': '789'}, self.req.getExtensionArgs()) - - def test_parseExtensionArgs(self): - args = {'preferred_auth_policies': 'http://foo http://bar', - 'max_auth_age': '9'} - self.req.parseExtensionArgs(args) - self.failUnlessEqual(9, self.req.max_auth_age) - self.failUnlessEqual(['http://foo','http://bar'], self.req.preferred_auth_policies) - - def test_parseExtensionArgs_empty(self): - self.req.parseExtensionArgs({}) - self.failUnlessEqual(None, self.req.max_auth_age) - self.failUnlessEqual([], self.req.preferred_auth_policies) - - def test_fromOpenIDRequest(self): - openid_req_msg = Message.fromOpenIDArgs({ - 'mode': 'checkid_setup', - 'ns': OPENID2_NS, - 'ns.pape': pape.ns_uri, - 'pape.preferred_auth_policies': ' '.join([pape.AUTH_MULTI_FACTOR, pape.AUTH_PHISHING_RESISTANT]), - 'pape.max_auth_age': '5476' - }) - oid_req = server.OpenIDRequest() - oid_req.message = openid_req_msg - req = pape.Request.fromOpenIDRequest(oid_req) - self.failUnlessEqual([pape.AUTH_MULTI_FACTOR, pape.AUTH_PHISHING_RESISTANT], req.preferred_auth_policies) - self.failUnlessEqual(5476, req.max_auth_age) - - def test_fromOpenIDRequest_no_pape(self): - message = Message() - openid_req = server.OpenIDRequest() - openid_req.message = message - pape_req = pape.Request.fromOpenIDRequest(openid_req) - assert(pape_req is None) - - def test_preferred_types(self): - self.req.addPolicyURI(pape.AUTH_PHISHING_RESISTANT) - self.req.addPolicyURI(pape.AUTH_MULTI_FACTOR) - pt = self.req.preferredTypes([pape.AUTH_MULTI_FACTOR, - pape.AUTH_MULTI_FACTOR_PHYSICAL]) - self.failUnlessEqual([pape.AUTH_MULTI_FACTOR], pt) - -class DummySuccessResponse: - def __init__(self, message, signed_stuff): - self.message = message - self.signed_stuff = signed_stuff - - def getSignedNS(self, ns_uri): - return self.signed_stuff - -class PapeResponseTestCase(unittest.TestCase): - def setUp(self): - self.req = pape.Response() - - def test_construct(self): - self.failUnlessEqual([], self.req.auth_policies) - self.failUnlessEqual(None, self.req.auth_time) - self.failUnlessEqual('pape', self.req.ns_alias) - self.failUnlessEqual(None, self.req.nist_auth_level) - - req2 = pape.Response([pape.AUTH_MULTI_FACTOR], "2004-12-11T10:30:44Z", 3) - self.failUnlessEqual([pape.AUTH_MULTI_FACTOR], req2.auth_policies) - self.failUnlessEqual("2004-12-11T10:30:44Z", req2.auth_time) - self.failUnlessEqual(3, req2.nist_auth_level) - - def test_add_policy_uri(self): - self.failUnlessEqual([], self.req.auth_policies) - self.req.addPolicyURI(pape.AUTH_MULTI_FACTOR) - self.failUnlessEqual([pape.AUTH_MULTI_FACTOR], self.req.auth_policies) - self.req.addPolicyURI(pape.AUTH_MULTI_FACTOR) - self.failUnlessEqual([pape.AUTH_MULTI_FACTOR], self.req.auth_policies) - self.req.addPolicyURI(pape.AUTH_PHISHING_RESISTANT) - self.failUnlessEqual([pape.AUTH_MULTI_FACTOR, pape.AUTH_PHISHING_RESISTANT], self.req.auth_policies) - self.req.addPolicyURI(pape.AUTH_MULTI_FACTOR) - self.failUnlessEqual([pape.AUTH_MULTI_FACTOR, pape.AUTH_PHISHING_RESISTANT], self.req.auth_policies) - - def test_getExtensionArgs(self): - self.failUnlessEqual({'auth_policies': 'none'}, self.req.getExtensionArgs()) - self.req.addPolicyURI('http://uri') - self.failUnlessEqual({'auth_policies': 'http://uri'}, self.req.getExtensionArgs()) - self.req.addPolicyURI('http://zig') - self.failUnlessEqual({'auth_policies': 'http://uri http://zig'}, self.req.getExtensionArgs()) - self.req.auth_time = "1776-07-04T14:43:12Z" - self.failUnlessEqual({'auth_policies': 'http://uri http://zig', 'auth_time': "1776-07-04T14:43:12Z"}, self.req.getExtensionArgs()) - self.req.nist_auth_level = 3 - self.failUnlessEqual({'auth_policies': 'http://uri http://zig', 'auth_time': "1776-07-04T14:43:12Z", 'nist_auth_level': '3'}, self.req.getExtensionArgs()) - - def test_getExtensionArgs_error_auth_age(self): - self.req.auth_time = "long ago" - self.failUnlessRaises(ValueError, self.req.getExtensionArgs) - - def test_getExtensionArgs_error_nist_auth_level(self): - self.req.nist_auth_level = "high as a kite" - self.failUnlessRaises(ValueError, self.req.getExtensionArgs) - self.req.nist_auth_level = 5 - self.failUnlessRaises(ValueError, self.req.getExtensionArgs) - self.req.nist_auth_level = -1 - self.failUnlessRaises(ValueError, self.req.getExtensionArgs) - - def test_parseExtensionArgs(self): - args = {'auth_policies': 'http://foo http://bar', - 'auth_time': '1970-01-01T00:00:00Z'} - self.req.parseExtensionArgs(args) - self.failUnlessEqual('1970-01-01T00:00:00Z', self.req.auth_time) - self.failUnlessEqual(['http://foo','http://bar'], self.req.auth_policies) - - def test_parseExtensionArgs_empty(self): - self.req.parseExtensionArgs({}) - self.failUnlessEqual(None, self.req.auth_time) - self.failUnlessEqual([], self.req.auth_policies) - - def test_parseExtensionArgs_strict_bogus1(self): - args = {'auth_policies': 'http://foo http://bar', - 'auth_time': 'yesterday'} - self.failUnlessRaises(ValueError, self.req.parseExtensionArgs, - args, True) - - def test_parseExtensionArgs_strict_bogus2(self): - args = {'auth_policies': 'http://foo http://bar', - 'auth_time': '1970-01-01T00:00:00Z', - 'nist_auth_level': 'some'} - self.failUnlessRaises(ValueError, self.req.parseExtensionArgs, - args, True) - - def test_parseExtensionArgs_strict_good(self): - args = {'auth_policies': 'http://foo http://bar', - 'auth_time': '1970-01-01T00:00:00Z', - 'nist_auth_level': '0'} - self.req.parseExtensionArgs(args, True) - self.failUnlessEqual(['http://foo','http://bar'], self.req.auth_policies) - self.failUnlessEqual('1970-01-01T00:00:00Z', self.req.auth_time) - self.failUnlessEqual(0, self.req.nist_auth_level) - - def test_parseExtensionArgs_nostrict_bogus(self): - args = {'auth_policies': 'http://foo http://bar', - 'auth_time': 'when the cows come home', - 'nist_auth_level': 'some'} - self.req.parseExtensionArgs(args) - self.failUnlessEqual(['http://foo','http://bar'], self.req.auth_policies) - self.failUnlessEqual(None, self.req.auth_time) - self.failUnlessEqual(None, self.req.nist_auth_level) - - def test_fromSuccessResponse(self): - openid_req_msg = Message.fromOpenIDArgs({ - 'mode': 'id_res', - 'ns': OPENID2_NS, - 'ns.pape': pape.ns_uri, - 'pape.auth_policies': ' '.join([pape.AUTH_MULTI_FACTOR, pape.AUTH_PHISHING_RESISTANT]), - 'pape.auth_time': '1970-01-01T00:00:00Z' - }) - signed_stuff = { - 'auth_policies': ' '.join([pape.AUTH_MULTI_FACTOR, pape.AUTH_PHISHING_RESISTANT]), - 'auth_time': '1970-01-01T00:00:00Z' - } - oid_req = DummySuccessResponse(openid_req_msg, signed_stuff) - req = pape.Response.fromSuccessResponse(oid_req) - self.failUnlessEqual([pape.AUTH_MULTI_FACTOR, pape.AUTH_PHISHING_RESISTANT], req.auth_policies) - self.failUnlessEqual('1970-01-01T00:00:00Z', req.auth_time) - - def test_fromSuccessResponseNoSignedArgs(self): - openid_req_msg = Message.fromOpenIDArgs({ - 'mode': 'id_res', - 'ns': OPENID2_NS, - 'ns.pape': pape.ns_uri, - 'pape.auth_policies': ' '.join([pape.AUTH_MULTI_FACTOR, pape.AUTH_PHISHING_RESISTANT]), - 'pape.auth_time': '1970-01-01T00:00:00Z' - }) - - signed_stuff = {} - - class NoSigningDummyResponse(DummySuccessResponse): - def getSignedNS(self, ns_uri): - return None - - oid_req = NoSigningDummyResponse(openid_req_msg, signed_stuff) - resp = pape.Response.fromSuccessResponse(oid_req) - self.failUnless(resp is None) diff --git a/askbot/deps/openid/test/test_pape_draft5.py b/askbot/deps/openid/test/test_pape_draft5.py deleted file mode 100644 index 35b1a316..00000000 --- a/askbot/deps/openid/test/test_pape_draft5.py +++ /dev/null @@ -1,441 +0,0 @@ - -from askbot.deps.openid.extensions.draft import pape5 as pape -from askbot.deps.openid.message import * -from askbot.deps.openid.server import server - -import warnings -warnings.filterwarnings('ignore', module=__name__, - message='"none" used as a policy URI') - -import unittest - -class PapeRequestTestCase(unittest.TestCase): - def setUp(self): - self.req = pape.Request() - - def test_construct(self): - self.failUnlessEqual([], self.req.preferred_auth_policies) - self.failUnlessEqual(None, self.req.max_auth_age) - self.failUnlessEqual('pape', self.req.ns_alias) - self.failIf(self.req.preferred_auth_level_types) - - bogus_levels = ['http://janrain.com/our_levels'] - req2 = pape.Request( - [pape.AUTH_MULTI_FACTOR], 1000, bogus_levels) - self.failUnlessEqual([pape.AUTH_MULTI_FACTOR], - req2.preferred_auth_policies) - self.failUnlessEqual(1000, req2.max_auth_age) - self.failUnlessEqual(bogus_levels, req2.preferred_auth_level_types) - - def test_addAuthLevel(self): - self.req.addAuthLevel('http://example.com/', 'example') - self.failUnlessEqual(['http://example.com/'], - self.req.preferred_auth_level_types) - self.failUnlessEqual('http://example.com/', - self.req.auth_level_aliases['example']) - - self.req.addAuthLevel('http://example.com/1', 'example1') - self.failUnlessEqual(['http://example.com/', 'http://example.com/1'], - self.req.preferred_auth_level_types) - - self.req.addAuthLevel('http://example.com/', 'exmpl') - self.failUnlessEqual(['http://example.com/', 'http://example.com/1'], - self.req.preferred_auth_level_types) - - self.req.addAuthLevel('http://example.com/', 'example') - self.failUnlessEqual(['http://example.com/', 'http://example.com/1'], - self.req.preferred_auth_level_types) - - self.failUnlessRaises(KeyError, - self.req.addAuthLevel, - 'http://example.com/2', 'example') - - # alias is None; we expect a new one to be generated. - uri = 'http://another.example.com/' - self.req.addAuthLevel(uri) - self.assert_(uri in self.req.auth_level_aliases.values()) - - # We don't expect a new alias to be generated if one already - # exists. - before_aliases = self.req.auth_level_aliases.keys() - self.req.addAuthLevel(uri) - after_aliases = self.req.auth_level_aliases.keys() - self.assertEqual(before_aliases, after_aliases) - - def test_add_policy_uri(self): - self.failUnlessEqual([], self.req.preferred_auth_policies) - self.req.addPolicyURI(pape.AUTH_MULTI_FACTOR) - self.failUnlessEqual([pape.AUTH_MULTI_FACTOR], - self.req.preferred_auth_policies) - self.req.addPolicyURI(pape.AUTH_MULTI_FACTOR) - self.failUnlessEqual([pape.AUTH_MULTI_FACTOR], - self.req.preferred_auth_policies) - self.req.addPolicyURI(pape.AUTH_PHISHING_RESISTANT) - self.failUnlessEqual([pape.AUTH_MULTI_FACTOR, - pape.AUTH_PHISHING_RESISTANT], - self.req.preferred_auth_policies) - self.req.addPolicyURI(pape.AUTH_MULTI_FACTOR) - self.failUnlessEqual([pape.AUTH_MULTI_FACTOR, - pape.AUTH_PHISHING_RESISTANT], - self.req.preferred_auth_policies) - - def test_getExtensionArgs(self): - self.failUnlessEqual({'preferred_auth_policies': ''}, - self.req.getExtensionArgs()) - self.req.addPolicyURI('http://uri') - self.failUnlessEqual( - {'preferred_auth_policies': 'http://uri'}, - self.req.getExtensionArgs()) - self.req.addPolicyURI('http://zig') - self.failUnlessEqual( - {'preferred_auth_policies': 'http://uri http://zig'}, - self.req.getExtensionArgs()) - self.req.max_auth_age = 789 - self.failUnlessEqual( - {'preferred_auth_policies': 'http://uri http://zig', - 'max_auth_age': '789'}, - self.req.getExtensionArgs()) - - def test_getExtensionArgsWithAuthLevels(self): - uri = 'http://example.com/auth_level' - alias = 'my_level' - self.req.addAuthLevel(uri, alias) - - uri2 = 'http://example.com/auth_level_2' - alias2 = 'my_level_2' - self.req.addAuthLevel(uri2, alias2) - - expected_args = { - ('auth_level.ns.%s' % alias): uri, - ('auth_level.ns.%s' % alias2): uri2, - 'preferred_auth_level_types': ' '.join([alias, alias2]), - 'preferred_auth_policies': '', - } - - self.failUnlessEqual(expected_args, self.req.getExtensionArgs()) - - def test_parseExtensionArgsWithAuthLevels(self): - uri = 'http://example.com/auth_level' - alias = 'my_level' - - uri2 = 'http://example.com/auth_level_2' - alias2 = 'my_level_2' - - request_args = { - ('auth_level.ns.%s' % alias): uri, - ('auth_level.ns.%s' % alias2): uri2, - 'preferred_auth_level_types': ' '.join([alias, alias2]), - 'preferred_auth_policies': '', - } - - # Check request object state - self.req.parseExtensionArgs(request_args, is_openid1=False, strict=False) - - expected_auth_levels = [uri, uri2] - - self.assertEqual(expected_auth_levels, - self.req.preferred_auth_level_types) - self.assertEqual(uri, self.req.auth_level_aliases[alias]) - self.assertEqual(uri2, self.req.auth_level_aliases[alias2]) - - def test_parseExtensionArgsWithAuthLevels_openID1(self): - request_args = { - 'preferred_auth_level_types':'nist jisa', - } - expected_auth_levels = [pape.LEVELS_NIST, pape.LEVELS_JISA] - self.req.parseExtensionArgs(request_args, is_openid1=True) - self.assertEqual(expected_auth_levels, - self.req.preferred_auth_level_types) - - self.req = pape.Request() - self.req.parseExtensionArgs(request_args, is_openid1=False) - self.assertEqual([], - self.req.preferred_auth_level_types) - - self.req = pape.Request() - self.failUnlessRaises(ValueError, - self.req.parseExtensionArgs, - request_args, is_openid1=False, strict=True) - - def test_parseExtensionArgs_ignoreBadAuthLevels(self): - request_args = {'preferred_auth_level_types':'monkeys'} - self.req.parseExtensionArgs(request_args, False) - self.assertEqual([], self.req.preferred_auth_level_types) - - def test_parseExtensionArgs_strictBadAuthLevels(self): - request_args = {'preferred_auth_level_types':'monkeys'} - self.failUnlessRaises(ValueError, self.req.parseExtensionArgs, - request_args, is_openid1=False, strict=True) - - def test_parseExtensionArgs(self): - args = {'preferred_auth_policies': 'http://foo http://bar', - 'max_auth_age': '9'} - self.req.parseExtensionArgs(args, False) - self.failUnlessEqual(9, self.req.max_auth_age) - self.failUnlessEqual(['http://foo','http://bar'], - self.req.preferred_auth_policies) - self.failUnlessEqual([], self.req.preferred_auth_level_types) - - def test_parseExtensionArgs_strict_bad_auth_age(self): - args = {'max_auth_age': 'not an int'} - self.assertRaises(ValueError, self.req.parseExtensionArgs, args, - is_openid1=False, strict=True) - - def test_parseExtensionArgs_empty(self): - self.req.parseExtensionArgs({}, False) - self.failUnlessEqual(None, self.req.max_auth_age) - self.failUnlessEqual([], self.req.preferred_auth_policies) - self.failUnlessEqual([], self.req.preferred_auth_level_types) - - def test_fromOpenIDRequest(self): - policy_uris = [pape.AUTH_MULTI_FACTOR, pape.AUTH_PHISHING_RESISTANT] - openid_req_msg = Message.fromOpenIDArgs({ - 'mode': 'checkid_setup', - 'ns': OPENID2_NS, - 'ns.pape': pape.ns_uri, - 'pape.preferred_auth_policies': ' '.join(policy_uris), - 'pape.max_auth_age': '5476' - }) - oid_req = server.OpenIDRequest() - oid_req.message = openid_req_msg - req = pape.Request.fromOpenIDRequest(oid_req) - self.failUnlessEqual(policy_uris, req.preferred_auth_policies) - self.failUnlessEqual(5476, req.max_auth_age) - - def test_fromOpenIDRequest_no_pape(self): - message = Message() - openid_req = server.OpenIDRequest() - openid_req.message = message - pape_req = pape.Request.fromOpenIDRequest(openid_req) - assert(pape_req is None) - - def test_preferred_types(self): - self.req.addPolicyURI(pape.AUTH_PHISHING_RESISTANT) - self.req.addPolicyURI(pape.AUTH_MULTI_FACTOR) - pt = self.req.preferredTypes([pape.AUTH_MULTI_FACTOR, - pape.AUTH_MULTI_FACTOR_PHYSICAL]) - self.failUnlessEqual([pape.AUTH_MULTI_FACTOR], pt) - -class DummySuccessResponse: - def __init__(self, message, signed_stuff): - self.message = message - self.signed_stuff = signed_stuff - - def isOpenID1(self): - return False - - def getSignedNS(self, ns_uri): - return self.signed_stuff - -class PapeResponseTestCase(unittest.TestCase): - def setUp(self): - self.resp = pape.Response() - - def test_construct(self): - self.failUnlessEqual([], self.resp.auth_policies) - self.failUnlessEqual(None, self.resp.auth_time) - self.failUnlessEqual('pape', self.resp.ns_alias) - self.failUnlessEqual(None, self.resp.nist_auth_level) - - req2 = pape.Response([pape.AUTH_MULTI_FACTOR], - "2004-12-11T10:30:44Z", {pape.LEVELS_NIST: 3}) - self.failUnlessEqual([pape.AUTH_MULTI_FACTOR], req2.auth_policies) - self.failUnlessEqual("2004-12-11T10:30:44Z", req2.auth_time) - self.failUnlessEqual(3, req2.nist_auth_level) - - def test_add_policy_uri(self): - self.failUnlessEqual([], self.resp.auth_policies) - self.resp.addPolicyURI(pape.AUTH_MULTI_FACTOR) - self.failUnlessEqual([pape.AUTH_MULTI_FACTOR], self.resp.auth_policies) - self.resp.addPolicyURI(pape.AUTH_MULTI_FACTOR) - self.failUnlessEqual([pape.AUTH_MULTI_FACTOR], self.resp.auth_policies) - self.resp.addPolicyURI(pape.AUTH_PHISHING_RESISTANT) - self.failUnlessEqual([pape.AUTH_MULTI_FACTOR, - pape.AUTH_PHISHING_RESISTANT], - self.resp.auth_policies) - self.resp.addPolicyURI(pape.AUTH_MULTI_FACTOR) - self.failUnlessEqual([pape.AUTH_MULTI_FACTOR, - pape.AUTH_PHISHING_RESISTANT], - self.resp.auth_policies) - - self.failUnlessRaises(RuntimeError, self.resp.addPolicyURI, - pape.AUTH_NONE) - - def test_getExtensionArgs(self): - self.failUnlessEqual({'auth_policies': pape.AUTH_NONE}, - self.resp.getExtensionArgs()) - self.resp.addPolicyURI('http://uri') - self.failUnlessEqual({'auth_policies': 'http://uri'}, - self.resp.getExtensionArgs()) - self.resp.addPolicyURI('http://zig') - self.failUnlessEqual({'auth_policies': 'http://uri http://zig'}, - self.resp.getExtensionArgs()) - self.resp.auth_time = "1776-07-04T14:43:12Z" - self.failUnlessEqual( - {'auth_policies': 'http://uri http://zig', - 'auth_time': "1776-07-04T14:43:12Z"}, - self.resp.getExtensionArgs()) - self.resp.setAuthLevel(pape.LEVELS_NIST, '3') - self.failUnlessEqual( - {'auth_policies': 'http://uri http://zig', - 'auth_time': "1776-07-04T14:43:12Z", - 'auth_level.nist': '3', - 'auth_level.ns.nist': pape.LEVELS_NIST}, - self.resp.getExtensionArgs()) - - def test_getExtensionArgs_error_auth_age(self): - self.resp.auth_time = "long ago" - self.failUnlessRaises(ValueError, self.resp.getExtensionArgs) - - def test_parseExtensionArgs(self): - args = {'auth_policies': 'http://foo http://bar', - 'auth_time': '1970-01-01T00:00:00Z'} - self.resp.parseExtensionArgs(args, is_openid1=False) - self.failUnlessEqual('1970-01-01T00:00:00Z', self.resp.auth_time) - self.failUnlessEqual(['http://foo','http://bar'], - self.resp.auth_policies) - - def test_parseExtensionArgs_valid_none(self): - args = {'auth_policies': pape.AUTH_NONE} - self.resp.parseExtensionArgs(args, is_openid1=False) - self.failUnlessEqual([], self.resp.auth_policies) - - def test_parseExtensionArgs_old_none(self): - args = {'auth_policies': 'none'} - self.resp.parseExtensionArgs(args, is_openid1=False) - self.failUnlessEqual([], self.resp.auth_policies) - - def test_parseExtensionArgs_old_none_strict(self): - args = {'auth_policies': 'none'} - self.failUnlessRaises( - ValueError, - self.resp.parseExtensionArgs, args, is_openid1=False, strict=True) - - def test_parseExtensionArgs_empty(self): - self.resp.parseExtensionArgs({}, is_openid1=False) - self.failUnlessEqual(None, self.resp.auth_time) - self.failUnlessEqual([], self.resp.auth_policies) - - def test_parseExtensionArgs_empty_strict(self): - self.failUnlessRaises( - ValueError, - self.resp.parseExtensionArgs, {}, is_openid1=False, strict=True) - - def test_parseExtensionArgs_ignore_superfluous_none(self): - policies = [pape.AUTH_NONE, pape.AUTH_MULTI_FACTOR_PHYSICAL] - - args = { - 'auth_policies': ' '.join(policies), - } - - self.resp.parseExtensionArgs(args, is_openid1=False, strict=False) - - self.assertEqual([pape.AUTH_MULTI_FACTOR_PHYSICAL], - self.resp.auth_policies) - - def test_parseExtensionArgs_none_strict(self): - policies = [pape.AUTH_NONE, pape.AUTH_MULTI_FACTOR_PHYSICAL] - - args = { - 'auth_policies': ' '.join(policies), - } - - self.failUnlessRaises(ValueError, self.resp.parseExtensionArgs, - args, is_openid1=False, strict=True) - - def test_parseExtensionArgs_strict_bogus1(self): - args = {'auth_policies': 'http://foo http://bar', - 'auth_time': 'yesterday'} - self.failUnlessRaises(ValueError, self.resp.parseExtensionArgs, - args, is_openid1=False, strict=True) - - def test_parseExtensionArgs_openid1_strict(self): - args = {'auth_level.nist': '0', - 'auth_policies': pape.AUTH_NONE, - } - self.resp.parseExtensionArgs(args, strict=True, is_openid1=True) - self.failUnlessEqual('0', self.resp.getAuthLevel(pape.LEVELS_NIST)) - self.failUnlessEqual([], self.resp.auth_policies) - - def test_parseExtensionArgs_strict_no_namespace_decl_openid2(self): - # Test the case where the namespace is not declared for an - # auth level. - args = {'auth_policies': pape.AUTH_NONE, - 'auth_level.nist': '0', - } - self.failUnlessRaises(ValueError, self.resp.parseExtensionArgs, - args, is_openid1=False, strict=True) - - def test_parseExtensionArgs_nostrict_no_namespace_decl_openid2(self): - # Test the case where the namespace is not declared for an - # auth level. - args = {'auth_policies': pape.AUTH_NONE, - 'auth_level.nist': '0', - } - self.resp.parseExtensionArgs(args, is_openid1=False, strict=False) - - # There is no namespace declaration for this auth level. - self.failUnlessRaises(KeyError, self.resp.getAuthLevel, - pape.LEVELS_NIST) - - def test_parseExtensionArgs_strict_good(self): - args = {'auth_policies': 'http://foo http://bar', - 'auth_time': '1970-01-01T00:00:00Z', - 'auth_level.nist': '0', - 'auth_level.ns.nist': pape.LEVELS_NIST} - self.resp.parseExtensionArgs(args, is_openid1=False, strict=True) - self.failUnlessEqual(['http://foo','http://bar'], - self.resp.auth_policies) - self.failUnlessEqual('1970-01-01T00:00:00Z', self.resp.auth_time) - self.failUnlessEqual(0, self.resp.nist_auth_level) - - def test_parseExtensionArgs_nostrict_bogus(self): - args = {'auth_policies': 'http://foo http://bar', - 'auth_time': 'when the cows come home', - 'nist_auth_level': 'some'} - self.resp.parseExtensionArgs(args, is_openid1=False) - self.failUnlessEqual(['http://foo','http://bar'], - self.resp.auth_policies) - self.failUnlessEqual(None, self.resp.auth_time) - self.failUnlessEqual(None, self.resp.nist_auth_level) - - def test_fromSuccessResponse(self): - policy_uris = [pape.AUTH_MULTI_FACTOR, pape.AUTH_PHISHING_RESISTANT] - openid_req_msg = Message.fromOpenIDArgs({ - 'mode': 'id_res', - 'ns': OPENID2_NS, - 'ns.pape': pape.ns_uri, - 'pape.auth_policies': ' '.join(policy_uris), - 'pape.auth_time': '1970-01-01T00:00:00Z' - }) - signed_stuff = { - 'auth_policies': ' '.join(policy_uris), - 'auth_time': '1970-01-01T00:00:00Z' - } - oid_req = DummySuccessResponse(openid_req_msg, signed_stuff) - req = pape.Response.fromSuccessResponse(oid_req) - self.failUnlessEqual(policy_uris, req.auth_policies) - self.failUnlessEqual('1970-01-01T00:00:00Z', req.auth_time) - - def test_fromSuccessResponseNoSignedArgs(self): - policy_uris = [pape.AUTH_MULTI_FACTOR, pape.AUTH_PHISHING_RESISTANT] - openid_req_msg = Message.fromOpenIDArgs({ - 'mode': 'id_res', - 'ns': OPENID2_NS, - 'ns.pape': pape.ns_uri, - 'pape.auth_policies': ' '.join(policy_uris), - 'pape.auth_time': '1970-01-01T00:00:00Z' - }) - - signed_stuff = {} - - class NoSigningDummyResponse(DummySuccessResponse): - def getSignedNS(self, ns_uri): - return None - - oid_req = NoSigningDummyResponse(openid_req_msg, signed_stuff) - resp = pape.Response.fromSuccessResponse(oid_req) - self.failUnless(resp is None) - -if __name__ == '__main__': - unittest.main() diff --git a/askbot/deps/openid/test/test_parsehtml.py b/askbot/deps/openid/test/test_parsehtml.py deleted file mode 100644 index bd1b35c6..00000000 --- a/askbot/deps/openid/test/test_parsehtml.py +++ /dev/null @@ -1,82 +0,0 @@ -from askbot.deps.openid.yadis.parsehtml import YadisHTMLParser, ParseDone -from HTMLParser import HTMLParseError - -import os.path, unittest, sys - -class _TestCase(unittest.TestCase): - reserved_values = ['None', 'EOF'] - - def __init__(self, filename, testname, expected, case): - self.filename = filename - self.testname = testname - self.expected = expected - self.case = case - unittest.TestCase.__init__(self) - - def runTest(self): - p = YadisHTMLParser() - try: - p.feed(self.case) - except ParseDone, why: - found = why[0] - - # make sure we protect outselves against accidental bogus - # test cases - assert found not in self.reserved_values - - # convert to a string - if found is None: - found = 'None' - - msg = "%r != %r for case %s" % (found, self.expected, self.case) - self.failUnlessEqual(found, self.expected, msg) - except HTMLParseError: - self.failUnless(self.expected == 'None', (self.case, self.expected)) - else: - self.failUnless(self.expected == 'EOF', (self.case, self.expected)) - - def shortDescription(self): - return "%s (%s<%s>)" % ( - self.testname, - self.__class__.__module__, - os.path.basename(self.filename)) - -def parseCases(data): - cases = [] - for chunk in data.split('\f\n'): - expected, case = chunk.split('\n', 1) - cases.append((expected, case)) - return cases - -def pyUnitTests(): - """Make a pyunit TestSuite from a file defining test cases.""" - s = unittest.TestSuite() - for (filename, test_num, expected, case) in getCases(): - s.addTest(_TestCase(filename, str(test_num), expected, case)) - return s - -def test(): - runner = unittest.TextTestRunner() - return runner.run(loadTests()) - -filenames = ['data/test1-parsehtml.txt'] - -default_test_files = [] -base = os.path.dirname(__file__) -for filename in filenames: - full_name = os.path.join(base, filename) - default_test_files.append(full_name) - -def getCases(test_files=default_test_files): - cases = [] - for filename in test_files: - test_num = 0 - data = file(filename).read() - for expected, case in parseCases(data): - test_num += 1 - cases.append((filename, test_num, expected, case)) - return cases - - -if __name__ == '__main__': - sys.exit(not test().wasSuccessful()) diff --git a/askbot/deps/openid/test/test_rpverify.py b/askbot/deps/openid/test/test_rpverify.py deleted file mode 100644 index 2837cb8d..00000000 --- a/askbot/deps/openid/test/test_rpverify.py +++ /dev/null @@ -1,246 +0,0 @@ -"""Unit tests for verification of return_to URLs for a realm -""" - -__all__ = ['TestBuildDiscoveryURL'] - -from askbot.deps.openid.yadis.discover import DiscoveryResult, DiscoveryFailure -from askbot.deps.openid.yadis import services -from askbot.deps.openid.server import trustroot -from askbot.deps.openid.test.support import CatchLogs -import unittest - -# Too many methods does not apply to unit test objects -#pylint:disable-msg=R0904 -class TestBuildDiscoveryURL(unittest.TestCase): - """Tests for building the discovery URL from a realm and a - return_to URL - """ - - def failUnlessDiscoURL(self, realm, expected_discovery_url): - """Build a discovery URL out of the realm and a return_to and - make sure that it matches the expected discovery URL - """ - realm_obj = trustroot.TrustRoot.parse(realm) - actual_discovery_url = realm_obj.buildDiscoveryURL() - self.failUnlessEqual(expected_discovery_url, actual_discovery_url) - - def test_trivial(self): - """There is no wildcard and the realm is the same as the return_to URL - """ - self.failUnlessDiscoURL('http://example.com/foo', - 'http://example.com/foo') - - def test_wildcard(self): - """There is a wildcard - """ - self.failUnlessDiscoURL('http://*.example.com/foo', - 'http://www.example.com/foo') - -class TestExtractReturnToURLs(unittest.TestCase): - disco_url = 'http://example.com/' - - def setUp(self): - self.original_discover = services.discover - services.discover = self.mockDiscover - self.data = None - - def tearDown(self): - services.discover = self.original_discover - - def mockDiscover(self, uri): - result = DiscoveryResult(uri) - result.response_text = self.data - result.normalized_uri = uri - return result - - def failUnlessFileHasReturnURLs(self, filename, expected_return_urls): - self.failUnlessXRDSHasReturnURLs(file(filename).read(), - expected_return_urls) - - def failUnlessXRDSHasReturnURLs(self, data, expected_return_urls): - self.data = data - actual_return_urls = list(trustroot.getAllowedReturnURLs( - self.disco_url)) - - self.failUnlessEqual(expected_return_urls, actual_return_urls) - - def failUnlessDiscoveryFailure(self, text): - self.data = text - self.failUnlessRaises( - DiscoveryFailure, trustroot.getAllowedReturnURLs, self.disco_url) - - def test_empty(self): - self.failUnlessDiscoveryFailure('') - - def test_badXML(self): - self.failUnlessDiscoveryFailure('>') - - def test_noEntries(self): - self.failUnlessXRDSHasReturnURLs('''\ -<?xml version="1.0" encoding="UTF-8"?> -<xrds:XRDS xmlns:xrds="xri://$xrds" - xmlns="xri://$xrd*($v*2.0)" - > - <XRD> - </XRD> -</xrds:XRDS> -''', []) - - def test_noReturnToEntries(self): - self.failUnlessXRDSHasReturnURLs('''\ -<?xml version="1.0" encoding="UTF-8"?> -<xrds:XRDS xmlns:xrds="xri://$xrds" - xmlns="xri://$xrd*($v*2.0)" - > - <XRD> - <Service priority="10"> - <Type>http://specs.openid.net/auth/2.0/server</Type> - <URI>http://www.myopenid.com/server</URI> - </Service> - </XRD> -</xrds:XRDS> -''', []) - - def test_oneEntry(self): - self.failUnlessXRDSHasReturnURLs('''\ -<?xml version="1.0" encoding="UTF-8"?> -<xrds:XRDS xmlns:xrds="xri://$xrds" - xmlns="xri://$xrd*($v*2.0)" - > - <XRD> - <Service> - <Type>http://specs.openid.net/auth/2.0/return_to</Type> - <URI>http://rp.example.com/return</URI> - </Service> - </XRD> -</xrds:XRDS> -''', ['http://rp.example.com/return']) - - def test_twoEntries(self): - self.failUnlessXRDSHasReturnURLs('''\ -<?xml version="1.0" encoding="UTF-8"?> -<xrds:XRDS xmlns:xrds="xri://$xrds" - xmlns="xri://$xrd*($v*2.0)" - > - <XRD> - <Service priority="0"> - <Type>http://specs.openid.net/auth/2.0/return_to</Type> - <URI>http://rp.example.com/return</URI> - </Service> - <Service priority="1"> - <Type>http://specs.openid.net/auth/2.0/return_to</Type> - <URI>http://other.rp.example.com/return</URI> - </Service> - </XRD> -</xrds:XRDS> -''', ['http://rp.example.com/return', - 'http://other.rp.example.com/return']) - - def test_twoEntries_withOther(self): - self.failUnlessXRDSHasReturnURLs('''\ -<?xml version="1.0" encoding="UTF-8"?> -<xrds:XRDS xmlns:xrds="xri://$xrds" - xmlns="xri://$xrd*($v*2.0)" - > - <XRD> - <Service priority="0"> - <Type>http://specs.openid.net/auth/2.0/return_to</Type> - <URI>http://rp.example.com/return</URI> - </Service> - <Service priority="1"> - <Type>http://specs.openid.net/auth/2.0/return_to</Type> - <URI>http://other.rp.example.com/return</URI> - </Service> - <Service priority="0"> - <Type>http://example.com/LOLCATS</Type> - <URI>http://example.com/invisible+uri</URI> - </Service> - </XRD> -</xrds:XRDS> -''', ['http://rp.example.com/return', - 'http://other.rp.example.com/return']) - - - -class TestReturnToMatches(unittest.TestCase): - def test_noEntries(self): - self.failIf(trustroot.returnToMatches([], 'anything')) - - def test_exactMatch(self): - r = 'http://example.com/return.to' - self.failUnless(trustroot.returnToMatches([r], r)) - - def test_garbageMatch(self): - r = 'http://example.com/return.to' - self.failUnless(trustroot.returnToMatches( - ['This is not a URL at all. In fact, it has characters, ' - 'like "<" that are not allowed in URLs', - r], - r)) - - def test_descendant(self): - r = 'http://example.com/return.to' - self.failUnless(trustroot.returnToMatches( - [r], - 'http://example.com/return.to/user:joe')) - - def test_wildcard(self): - self.failIf(trustroot.returnToMatches( - ['http://*.example.com/return.to'], - 'http://example.com/return.to')) - - def test_noMatch(self): - r = 'http://example.com/return.to' - self.failIf(trustroot.returnToMatches( - [r], - 'http://example.com/xss_exploit')) - -class TestVerifyReturnTo(unittest.TestCase, CatchLogs): - - def setUp(self): - CatchLogs.setUp(self) - - def tearDown(self): - CatchLogs.tearDown(self) - - def test_bogusRealm(self): - self.failIf(trustroot.verifyReturnTo('', 'http://example.com/')) - - def test_verifyWithDiscoveryCalled(self): - realm = 'http://*.example.com/' - return_to = 'http://www.example.com/foo' - - def vrfy(disco_url): - self.failUnlessEqual('http://www.example.com/', disco_url) - return [return_to] - - self.failUnless( - trustroot.verifyReturnTo(realm, return_to, _vrfy=vrfy)) - self.failUnlessLogEmpty() - - def test_verifyFailWithDiscoveryCalled(self): - realm = 'http://*.example.com/' - return_to = 'http://www.example.com/foo' - - def vrfy(disco_url): - self.failUnlessEqual('http://www.example.com/', disco_url) - return ['http://something-else.invalid/'] - - self.failIf( - trustroot.verifyReturnTo(realm, return_to, _vrfy=vrfy)) - self.failUnlessLogMatches("Failed to validate return_to") - - def test_verifyFailIfDiscoveryRedirects(self): - realm = 'http://*.example.com/' - return_to = 'http://www.example.com/foo' - - def vrfy(disco_url): - raise trustroot.RealmVerificationRedirected( - disco_url, "http://redirected.invalid") - - self.failIf( - trustroot.verifyReturnTo(realm, return_to, _vrfy=vrfy)) - self.failUnlessLogMatches("Attempting to verify") - -if __name__ == '__main__': - unittest.main() diff --git a/askbot/deps/openid/test/test_server.py b/askbot/deps/openid/test/test_server.py deleted file mode 100644 index c843f762..00000000 --- a/askbot/deps/openid/test/test_server.py +++ /dev/null @@ -1,2064 +0,0 @@ -"""Tests for openid.server. -""" -from askbot.deps.openid.server import server -from askbot.deps.openid import association, cryptutil, oidutil -from askbot.deps.openid.message import Message, OPENID_NS, OPENID2_NS, OPENID1_NS, \ - IDENTIFIER_SELECT, no_default, OPENID1_URL_LIMIT -from askbot.deps.openid.store import memstore -import cgi - -import unittest -import warnings - -from urlparse import urlparse - -# In general, if you edit or add tests here, try to move in the direction -# of testing smaller units. For testing the external interfaces, we'll be -# developing an implementation-agnostic testing suite. - -# for more, see /etc/ssh/moduli - -ALT_MODULUS = 0xCAADDDEC1667FC68B5FA15D53C4E1532DD24561A1A2D47A12C01ABEA1E00731F6921AAC40742311FDF9E634BB7131BEE1AF240261554389A910425E044E88C8359B010F5AD2B80E29CB1A5B027B19D9E01A6F63A6F45E5D7ED2FF6A2A0085050A7D0CF307C3DB51D2490355907B4427C23A98DF1EB8ABEF2BA209BB7AFFE86A7 -ALT_GEN = 5 - -class CatchLogs(object): - def setUp(self): - self.old_logger = oidutil.log - oidutil.log = self.gotLogMessage - self.messages = [] - - def gotLogMessage(self, message): - self.messages.append(message) - - def tearDown(self): - oidutil.log = self.old_logger - -class TestProtocolError(unittest.TestCase): - def test_browserWithReturnTo(self): - return_to = "http://rp.unittest/consumer" - # will be a ProtocolError raised by Decode or CheckIDRequest.answer - args = Message.fromPostArgs({ - 'openid.mode': 'monkeydance', - 'openid.identity': 'http://wagu.unittest/', - 'openid.return_to': return_to, - }) - e = server.ProtocolError(args, "plucky") - self.failUnless(e.hasReturnTo()) - expected_args = { - 'openid.mode': ['error'], - 'openid.error': ['plucky'], - } - - rt_base, result_args = e.encodeToURL().split('?', 1) - result_args = cgi.parse_qs(result_args) - self.failUnlessEqual(result_args, expected_args) - - def test_browserWithReturnTo_OpenID2_GET(self): - return_to = "http://rp.unittest/consumer" - # will be a ProtocolError raised by Decode or CheckIDRequest.answer - args = Message.fromPostArgs({ - 'openid.ns': OPENID2_NS, - 'openid.mode': 'monkeydance', - 'openid.identity': 'http://wagu.unittest/', - 'openid.claimed_id': 'http://wagu.unittest/', - 'openid.return_to': return_to, - }) - e = server.ProtocolError(args, "plucky") - self.failUnless(e.hasReturnTo()) - expected_args = { - 'openid.ns': [OPENID2_NS], - 'openid.mode': ['error'], - 'openid.error': ['plucky'], - } - - rt_base, result_args = e.encodeToURL().split('?', 1) - result_args = cgi.parse_qs(result_args) - self.failUnlessEqual(result_args, expected_args) - - def test_browserWithReturnTo_OpenID2_POST(self): - return_to = "http://rp.unittest/consumer" + ('x' * OPENID1_URL_LIMIT) - # will be a ProtocolError raised by Decode or CheckIDRequest.answer - args = Message.fromPostArgs({ - 'openid.ns': OPENID2_NS, - 'openid.mode': 'monkeydance', - 'openid.identity': 'http://wagu.unittest/', - 'openid.claimed_id': 'http://wagu.unittest/', - 'openid.return_to': return_to, - }) - e = server.ProtocolError(args, "plucky") - self.failUnless(e.hasReturnTo()) - expected_args = { - 'openid.ns': [OPENID2_NS], - 'openid.mode': ['error'], - 'openid.error': ['plucky'], - } - - self.failUnless(e.whichEncoding() == server.ENCODE_HTML_FORM) - self.failUnless(e.toFormMarkup() == e.toMessage().toFormMarkup( - args.getArg(OPENID_NS, 'return_to'))) - - def test_browserWithReturnTo_OpenID1_exceeds_limit(self): - return_to = "http://rp.unittest/consumer" + ('x' * OPENID1_URL_LIMIT) - # will be a ProtocolError raised by Decode or CheckIDRequest.answer - args = Message.fromPostArgs({ - 'openid.mode': 'monkeydance', - 'openid.identity': 'http://wagu.unittest/', - 'openid.return_to': return_to, - }) - e = server.ProtocolError(args, "plucky") - self.failUnless(e.hasReturnTo()) - expected_args = { - 'openid.mode': ['error'], - 'openid.error': ['plucky'], - } - - self.failUnless(e.whichEncoding() == server.ENCODE_URL) - - rt_base, result_args = e.encodeToURL().split('?', 1) - result_args = cgi.parse_qs(result_args) - self.failUnlessEqual(result_args, expected_args) - - def test_noReturnTo(self): - # will be a ProtocolError raised by Decode or CheckIDRequest.answer - args = Message.fromPostArgs({ - 'openid.mode': 'zebradance', - 'openid.identity': 'http://wagu.unittest/', - }) - e = server.ProtocolError(args, "waffles") - self.failIf(e.hasReturnTo()) - expected = """error:waffles -mode:error -""" - self.failUnlessEqual(e.encodeToKVForm(), expected) - - - def test_noMessage(self): - e = server.ProtocolError(None, "no moar pancakes") - self.failIf(e.hasReturnTo()) - self.failUnlessEqual(e.whichEncoding(), None) - - -class TestDecode(unittest.TestCase): - def setUp(self): - self.claimed_id = 'http://de.legating.de.coder.unittest/' - self.id_url = "http://decoder.am.unittest/" - self.rt_url = "http://rp.unittest/foobot/?qux=zam" - self.tr_url = "http://rp.unittest/" - self.assoc_handle = "{assoc}{handle}" - self.op_endpoint = 'http://endpoint.unittest/encode' - self.store = memstore.MemoryStore() - self.server = server.Server(self.store, self.op_endpoint) - self.decode = self.server.decoder.decode - self.decode = server.Decoder(self.server).decode - - def test_none(self): - args = {} - r = self.decode(args) - self.failUnlessEqual(r, None) - - def test_irrelevant(self): - args = { - 'pony': 'spotted', - 'sreg.mutant_power': 'decaffinator', - } - self.failUnlessRaises(server.ProtocolError, self.decode, args) - - def test_bad(self): - args = { - 'openid.mode': 'twos-compliment', - 'openid.pants': 'zippered', - } - self.failUnlessRaises(server.ProtocolError, self.decode, args) - - def test_dictOfLists(self): - args = { - 'openid.mode': ['checkid_setup'], - 'openid.identity': self.id_url, - 'openid.assoc_handle': self.assoc_handle, - 'openid.return_to': self.rt_url, - 'openid.trust_root': self.tr_url, - } - try: - result = self.decode(args) - except TypeError, err: - self.failUnless(str(err).find('values') != -1, err) - else: - self.fail("Expected TypeError, but got result %s" % (result,)) - - def test_checkidImmediate(self): - args = { - 'openid.mode': 'checkid_immediate', - 'openid.identity': self.id_url, - 'openid.assoc_handle': self.assoc_handle, - 'openid.return_to': self.rt_url, - 'openid.trust_root': self.tr_url, - # should be ignored - 'openid.some.extension': 'junk', - } - r = self.decode(args) - self.failUnless(isinstance(r, server.CheckIDRequest)) - self.failUnlessEqual(r.mode, "checkid_immediate") - self.failUnlessEqual(r.immediate, True) - self.failUnlessEqual(r.identity, self.id_url) - self.failUnlessEqual(r.trust_root, self.tr_url) - self.failUnlessEqual(r.return_to, self.rt_url) - self.failUnlessEqual(r.assoc_handle, self.assoc_handle) - - def test_checkidSetup(self): - args = { - 'openid.mode': 'checkid_setup', - 'openid.identity': self.id_url, - 'openid.assoc_handle': self.assoc_handle, - 'openid.return_to': self.rt_url, - 'openid.trust_root': self.tr_url, - } - r = self.decode(args) - self.failUnless(isinstance(r, server.CheckIDRequest)) - self.failUnlessEqual(r.mode, "checkid_setup") - self.failUnlessEqual(r.immediate, False) - self.failUnlessEqual(r.identity, self.id_url) - self.failUnlessEqual(r.trust_root, self.tr_url) - self.failUnlessEqual(r.return_to, self.rt_url) - - def test_checkidSetupOpenID2(self): - args = { - 'openid.ns': OPENID2_NS, - 'openid.mode': 'checkid_setup', - 'openid.identity': self.id_url, - 'openid.claimed_id': self.claimed_id, - 'openid.assoc_handle': self.assoc_handle, - 'openid.return_to': self.rt_url, - 'openid.realm': self.tr_url, - } - r = self.decode(args) - self.failUnless(isinstance(r, server.CheckIDRequest)) - self.failUnlessEqual(r.mode, "checkid_setup") - self.failUnlessEqual(r.immediate, False) - self.failUnlessEqual(r.identity, self.id_url) - self.failUnlessEqual(r.claimed_id, self.claimed_id) - self.failUnlessEqual(r.trust_root, self.tr_url) - self.failUnlessEqual(r.return_to, self.rt_url) - - def test_checkidSetupNoClaimedIDOpenID2(self): - args = { - 'openid.ns': OPENID2_NS, - 'openid.mode': 'checkid_setup', - 'openid.identity': self.id_url, - 'openid.assoc_handle': self.assoc_handle, - 'openid.return_to': self.rt_url, - 'openid.realm': self.tr_url, - } - self.failUnlessRaises(server.ProtocolError, self.decode, args) - - def test_checkidSetupNoIdentityOpenID2(self): - args = { - 'openid.ns': OPENID2_NS, - 'openid.mode': 'checkid_setup', - 'openid.assoc_handle': self.assoc_handle, - 'openid.return_to': self.rt_url, - 'openid.realm': self.tr_url, - } - r = self.decode(args) - self.failUnless(isinstance(r, server.CheckIDRequest)) - self.failUnlessEqual(r.mode, "checkid_setup") - self.failUnlessEqual(r.immediate, False) - self.failUnlessEqual(r.identity, None) - self.failUnlessEqual(r.trust_root, self.tr_url) - self.failUnlessEqual(r.return_to, self.rt_url) - - def test_checkidSetupNoReturnOpenID1(self): - """Make sure an OpenID 1 request cannot be decoded if it lacks - a return_to. - """ - args = { - 'openid.mode': 'checkid_setup', - 'openid.identity': self.id_url, - 'openid.assoc_handle': self.assoc_handle, - 'openid.trust_root': self.tr_url, - } - self.failUnlessRaises(server.ProtocolError, self.decode, args) - - def test_checkidSetupNoReturnOpenID2(self): - """Make sure an OpenID 2 request with no return_to can be - decoded, and make sure a response to such a request raises - NoReturnToError. - """ - args = { - 'openid.ns': OPENID2_NS, - 'openid.mode': 'checkid_setup', - 'openid.identity': self.id_url, - 'openid.claimed_id': self.id_url, - 'openid.assoc_handle': self.assoc_handle, - 'openid.realm': self.tr_url, - } - self.failUnless(isinstance(self.decode(args), server.CheckIDRequest)) - - req = self.decode(args) - self.assertRaises(server.NoReturnToError, req.answer, False) - self.assertRaises(server.NoReturnToError, req.encodeToURL, 'bogus') - self.assertRaises(server.NoReturnToError, req.getCancelURL) - - def test_checkidSetupRealmRequiredOpenID2(self): - """Make sure that an OpenID 2 request which lacks return_to - cannot be decoded if it lacks a realm. Spec: This value - (openid.realm) MUST be sent if openid.return_to is omitted. - """ - args = { - 'openid.ns': OPENID2_NS, - 'openid.mode': 'checkid_setup', - 'openid.identity': self.id_url, - 'openid.assoc_handle': self.assoc_handle, - } - self.failUnlessRaises(server.ProtocolError, self.decode, args) - - def test_checkidSetupBadReturn(self): - args = { - 'openid.mode': 'checkid_setup', - 'openid.identity': self.id_url, - 'openid.assoc_handle': self.assoc_handle, - 'openid.return_to': 'not a url', - } - try: - result = self.decode(args) - except server.ProtocolError, err: - self.failUnless(err.openid_message) - else: - self.fail("Expected ProtocolError, instead returned with %s" % - (result,)) - - def test_checkidSetupUntrustedReturn(self): - args = { - 'openid.mode': 'checkid_setup', - 'openid.identity': self.id_url, - 'openid.assoc_handle': self.assoc_handle, - 'openid.return_to': self.rt_url, - 'openid.trust_root': 'http://not-the-return-place.unittest/', - } - try: - result = self.decode(args) - except server.UntrustedReturnURL, err: - self.failUnless(err.openid_message) - else: - self.fail("Expected UntrustedReturnURL, instead returned with %s" % - (result,)) - - def test_checkAuth(self): - args = { - 'openid.mode': 'check_authentication', - 'openid.assoc_handle': '{dumb}{handle}', - 'openid.sig': 'sigblob', - 'openid.signed': 'identity,return_to,response_nonce,mode', - 'openid.identity': 'signedval1', - 'openid.return_to': 'signedval2', - 'openid.response_nonce': 'signedval3', - 'openid.baz': 'unsigned', - } - r = self.decode(args) - self.failUnless(isinstance(r, server.CheckAuthRequest)) - self.failUnlessEqual(r.mode, 'check_authentication') - self.failUnlessEqual(r.sig, 'sigblob') - - - def test_checkAuthMissingSignature(self): - args = { - 'openid.mode': 'check_authentication', - 'openid.assoc_handle': '{dumb}{handle}', - 'openid.signed': 'foo,bar,mode', - 'openid.foo': 'signedval1', - 'openid.bar': 'signedval2', - 'openid.baz': 'unsigned', - } - self.failUnlessRaises(server.ProtocolError, self.decode, args) - - - def test_checkAuthAndInvalidate(self): - args = { - 'openid.mode': 'check_authentication', - 'openid.assoc_handle': '{dumb}{handle}', - 'openid.invalidate_handle': '[[SMART_handle]]', - 'openid.sig': 'sigblob', - 'openid.signed': 'identity,return_to,response_nonce,mode', - 'openid.identity': 'signedval1', - 'openid.return_to': 'signedval2', - 'openid.response_nonce': 'signedval3', - 'openid.baz': 'unsigned', - } - r = self.decode(args) - self.failUnless(isinstance(r, server.CheckAuthRequest)) - self.failUnlessEqual(r.invalidate_handle, '[[SMART_handle]]') - - - def test_associateDH(self): - args = { - 'openid.mode': 'associate', - 'openid.session_type': 'DH-SHA1', - 'openid.dh_consumer_public': "Rzup9265tw==", - } - r = self.decode(args) - self.failUnless(isinstance(r, server.AssociateRequest)) - self.failUnlessEqual(r.mode, "associate") - self.failUnlessEqual(r.session.session_type, "DH-SHA1") - self.failUnlessEqual(r.assoc_type, "HMAC-SHA1") - self.failUnless(r.session.consumer_pubkey) - - def test_associateDHMissingKey(self): - """Trying DH assoc w/o public key""" - args = { - 'openid.mode': 'associate', - 'openid.session_type': 'DH-SHA1', - } - # Using DH-SHA1 without supplying dh_consumer_public is an error. - self.failUnlessRaises(server.ProtocolError, self.decode, args) - - - def test_associateDHpubKeyNotB64(self): - args = { - 'openid.mode': 'associate', - 'openid.session_type': 'DH-SHA1', - 'openid.dh_consumer_public': "donkeydonkeydonkey", - } - self.failUnlessRaises(server.ProtocolError, self.decode, args) - - - def test_associateDHModGen(self): - # test dh with non-default but valid values for dh_modulus and dh_gen - args = { - 'openid.mode': 'associate', - 'openid.session_type': 'DH-SHA1', - 'openid.dh_consumer_public': "Rzup9265tw==", - 'openid.dh_modulus': cryptutil.longToBase64(ALT_MODULUS), - 'openid.dh_gen': cryptutil.longToBase64(ALT_GEN) , - } - r = self.decode(args) - self.failUnless(isinstance(r, server.AssociateRequest)) - self.failUnlessEqual(r.mode, "associate") - self.failUnlessEqual(r.session.session_type, "DH-SHA1") - self.failUnlessEqual(r.assoc_type, "HMAC-SHA1") - self.failUnlessEqual(r.session.dh.modulus, ALT_MODULUS) - self.failUnlessEqual(r.session.dh.generator, ALT_GEN) - self.failUnless(r.session.consumer_pubkey) - - - def test_associateDHCorruptModGen(self): - # test dh with non-default but valid values for dh_modulus and dh_gen - args = { - 'openid.mode': 'associate', - 'openid.session_type': 'DH-SHA1', - 'openid.dh_consumer_public': "Rzup9265tw==", - 'openid.dh_modulus': 'pizza', - 'openid.dh_gen': 'gnocchi', - } - self.failUnlessRaises(server.ProtocolError, self.decode, args) - - - def test_associateDHMissingModGen(self): - # test dh with non-default but valid values for dh_modulus and dh_gen - args = { - 'openid.mode': 'associate', - 'openid.session_type': 'DH-SHA1', - 'openid.dh_consumer_public': "Rzup9265tw==", - 'openid.dh_modulus': 'pizza', - } - self.failUnlessRaises(server.ProtocolError, self.decode, args) - - -# def test_associateDHInvalidModGen(self): -# # test dh with properly encoded values that are not a valid -# # modulus/generator combination. -# args = { -# 'openid.mode': 'associate', -# 'openid.session_type': 'DH-SHA1', -# 'openid.dh_consumer_public': "Rzup9265tw==", -# 'openid.dh_modulus': cryptutil.longToBase64(9), -# 'openid.dh_gen': cryptutil.longToBase64(27) , -# } -# self.failUnlessRaises(server.ProtocolError, self.decode, args) -# test_associateDHInvalidModGen.todo = "low-priority feature" - - - def test_associateWeirdSession(self): - args = { - 'openid.mode': 'associate', - 'openid.session_type': 'FLCL6', - 'openid.dh_consumer_public': "YQ==\n", - } - self.failUnlessRaises(server.ProtocolError, self.decode, args) - - - def test_associatePlain(self): - args = { - 'openid.mode': 'associate', - } - r = self.decode(args) - self.failUnless(isinstance(r, server.AssociateRequest)) - self.failUnlessEqual(r.mode, "associate") - self.failUnlessEqual(r.session.session_type, "no-encryption") - self.failUnlessEqual(r.assoc_type, "HMAC-SHA1") - - def test_nomode(self): - args = { - 'openid.session_type': 'DH-SHA1', - 'openid.dh_consumer_public': "my public keeey", - } - self.failUnlessRaises(server.ProtocolError, self.decode, args) - - def test_invalidns(self): - args = {'openid.ns': 'Tuesday', - 'openid.mode': 'associate'} - - try: - r = self.decode(args) - except server.ProtocolError, err: - # Assert that the ProtocolError does have a Message attached - # to it, even though the request wasn't a well-formed Message. - self.failUnless(err.openid_message) - # The error message contains the bad openid.ns. - self.failUnless('Tuesday' in str(err), str(err)) - else: - self.fail("Expected ProtocolError but returned with %r" % (r,)) - - -class TestEncode(unittest.TestCase): - def setUp(self): - self.encoder = server.Encoder() - self.encode = self.encoder.encode - self.op_endpoint = 'http://endpoint.unittest/encode' - self.store = memstore.MemoryStore() - self.server = server.Server(self.store, self.op_endpoint) - - def test_id_res_OpenID2_GET(self): - """ - Check that when an OpenID 2 response does not exceed the - OpenID 1 message size, a GET response (i.e., redirect) is - issued. - """ - request = server.CheckIDRequest( - identity = 'http://bombom.unittest/', - trust_root = 'http://burr.unittest/', - return_to = 'http://burr.unittest/999', - immediate = False, - op_endpoint = self.server.op_endpoint, - ) - request.message = Message(OPENID2_NS) - response = server.OpenIDResponse(request) - response.fields = Message.fromOpenIDArgs({ - 'ns': OPENID2_NS, - 'mode': 'id_res', - 'identity': request.identity, - 'claimed_id': request.identity, - 'return_to': request.return_to, - }) - - self.failIf(response.renderAsForm()) - self.failUnless(response.whichEncoding() == server.ENCODE_URL) - webresponse = self.encode(response) - self.failUnless(webresponse.headers.has_key('location')) - - def test_id_res_OpenID2_POST(self): - """ - Check that when an OpenID 2 response exceeds the OpenID 1 - message size, a POST response (i.e., an HTML form) is - returned. - """ - request = server.CheckIDRequest( - identity = 'http://bombom.unittest/', - trust_root = 'http://burr.unittest/', - return_to = 'http://burr.unittest/999', - immediate = False, - op_endpoint = self.server.op_endpoint, - ) - request.message = Message(OPENID2_NS) - response = server.OpenIDResponse(request) - response.fields = Message.fromOpenIDArgs({ - 'ns': OPENID2_NS, - 'mode': 'id_res', - 'identity': request.identity, - 'claimed_id': request.identity, - 'return_to': 'x' * OPENID1_URL_LIMIT, - }) - - self.failUnless(response.renderAsForm()) - self.failUnless(len(response.encodeToURL()) > OPENID1_URL_LIMIT) - self.failUnless(response.whichEncoding() == server.ENCODE_HTML_FORM) - webresponse = self.encode(response) - self.failUnlessEqual(webresponse.body, response.toFormMarkup()) - - def test_toFormMarkup(self): - request = server.CheckIDRequest( - identity = 'http://bombom.unittest/', - trust_root = 'http://burr.unittest/', - return_to = 'http://burr.unittest/999', - immediate = False, - op_endpoint = self.server.op_endpoint, - ) - request.message = Message(OPENID2_NS) - response = server.OpenIDResponse(request) - response.fields = Message.fromOpenIDArgs({ - 'ns': OPENID2_NS, - 'mode': 'id_res', - 'identity': request.identity, - 'claimed_id': request.identity, - 'return_to': 'x' * OPENID1_URL_LIMIT, - }) - - form_markup = response.toFormMarkup({'foo':'bar'}) - self.failUnless(' foo="bar"' in form_markup) - - def test_toHTML(self): - request = server.CheckIDRequest( - identity = 'http://bombom.unittest/', - trust_root = 'http://burr.unittest/', - return_to = 'http://burr.unittest/999', - immediate = False, - op_endpoint = self.server.op_endpoint, - ) - request.message = Message(OPENID2_NS) - response = server.OpenIDResponse(request) - response.fields = Message.fromOpenIDArgs({ - 'ns': OPENID2_NS, - 'mode': 'id_res', - 'identity': request.identity, - 'claimed_id': request.identity, - 'return_to': 'x' * OPENID1_URL_LIMIT, - }) - html = response.toHTML() - self.failUnless('<html>' in html) - self.failUnless('</html>' in html) - self.failUnless('<body onload=' in html) - self.failUnless('<form' in html) - self.failUnless('http://bombom.unittest/' in html) - - def test_id_res_OpenID1_exceeds_limit(self): - """ - Check that when an OpenID 1 response exceeds the OpenID 1 - message size, a GET response is issued. Technically, this - shouldn't be permitted by the library, but this test is in - place to preserve the status quo for OpenID 1. - """ - request = server.CheckIDRequest( - identity = 'http://bombom.unittest/', - trust_root = 'http://burr.unittest/', - return_to = 'http://burr.unittest/999', - immediate = False, - op_endpoint = self.server.op_endpoint, - ) - request.message = Message(OPENID2_NS) - response = server.OpenIDResponse(request) - response.fields = Message.fromOpenIDArgs({ - 'mode': 'id_res', - 'identity': request.identity, - 'return_to': 'x' * OPENID1_URL_LIMIT, - }) - - self.failIf(response.renderAsForm()) - self.failUnless(len(response.encodeToURL()) > OPENID1_URL_LIMIT) - self.failUnless(response.whichEncoding() == server.ENCODE_URL) - webresponse = self.encode(response) - self.failUnlessEqual(webresponse.headers['location'], response.encodeToURL()) - - def test_id_res(self): - request = server.CheckIDRequest( - identity = 'http://bombom.unittest/', - trust_root = 'http://burr.unittest/', - return_to = 'http://burr.unittest/999', - immediate = False, - op_endpoint = self.server.op_endpoint, - ) - request.message = Message(OPENID2_NS) - response = server.OpenIDResponse(request) - response.fields = Message.fromOpenIDArgs({ - 'mode': 'id_res', - 'identity': request.identity, - 'return_to': request.return_to, - }) - webresponse = self.encode(response) - self.failUnlessEqual(webresponse.code, server.HTTP_REDIRECT) - self.failUnless(webresponse.headers.has_key('location')) - - location = webresponse.headers['location'] - self.failUnless(location.startswith(request.return_to), - "%s does not start with %s" % (location, - request.return_to)) - # argh. - q2 = dict(cgi.parse_qsl(urlparse(location)[4])) - expected = response.fields.toPostArgs() - self.failUnlessEqual(q2, expected) - - def test_cancel(self): - request = server.CheckIDRequest( - identity = 'http://bombom.unittest/', - trust_root = 'http://burr.unittest/', - return_to = 'http://burr.unittest/999', - immediate = False, - op_endpoint = self.server.op_endpoint, - ) - request.message = Message(OPENID2_NS) - response = server.OpenIDResponse(request) - response.fields = Message.fromOpenIDArgs({ - 'mode': 'cancel', - }) - webresponse = self.encode(response) - self.failUnlessEqual(webresponse.code, server.HTTP_REDIRECT) - self.failUnless(webresponse.headers.has_key('location')) - - def test_cancelToForm(self): - request = server.CheckIDRequest( - identity = 'http://bombom.unittest/', - trust_root = 'http://burr.unittest/', - return_to = 'http://burr.unittest/999', - immediate = False, - op_endpoint = self.server.op_endpoint, - ) - request.message = Message(OPENID2_NS) - response = server.OpenIDResponse(request) - response.fields = Message.fromOpenIDArgs({ - 'mode': 'cancel', - }) - form = response.toFormMarkup() - self.failUnless(form) - - def test_assocReply(self): - msg = Message(OPENID2_NS) - msg.setArg(OPENID2_NS, 'session_type', 'no-encryption') - request = server.AssociateRequest.fromMessage(msg) - response = server.OpenIDResponse(request) - response.fields = Message.fromPostArgs( - {'openid.assoc_handle': "every-zig"}) - webresponse = self.encode(response) - body = """assoc_handle:every-zig -""" - self.failUnlessEqual(webresponse.code, server.HTTP_OK) - self.failUnlessEqual(webresponse.headers, {}) - self.failUnlessEqual(webresponse.body, body) - - def test_checkauthReply(self): - request = server.CheckAuthRequest('a_sock_monkey', - 'siggggg', - []) - response = server.OpenIDResponse(request) - response.fields = Message.fromOpenIDArgs({ - 'is_valid': 'true', - 'invalidate_handle': 'xXxX:xXXx' - }) - body = """invalidate_handle:xXxX:xXXx -is_valid:true -""" - webresponse = self.encode(response) - self.failUnlessEqual(webresponse.code, server.HTTP_OK) - self.failUnlessEqual(webresponse.headers, {}) - self.failUnlessEqual(webresponse.body, body) - - def test_unencodableError(self): - args = Message.fromPostArgs({ - 'openid.identity': 'http://limu.unittest/', - }) - e = server.ProtocolError(args, "wet paint") - self.failUnlessRaises(server.EncodingError, self.encode, e) - - def test_encodableError(self): - args = Message.fromPostArgs({ - 'openid.mode': 'associate', - 'openid.identity': 'http://limu.unittest/', - }) - body="error:snoot\nmode:error\n" - webresponse = self.encode(server.ProtocolError(args, "snoot")) - self.failUnlessEqual(webresponse.code, server.HTTP_ERROR) - self.failUnlessEqual(webresponse.headers, {}) - self.failUnlessEqual(webresponse.body, body) - - - -class TestSigningEncode(unittest.TestCase): - def setUp(self): - self._dumb_key = server.Signatory._dumb_key - self._normal_key = server.Signatory._normal_key - self.store = memstore.MemoryStore() - self.server = server.Server(self.store, "http://signing.unittest/enc") - self.request = server.CheckIDRequest( - identity = 'http://bombom.unittest/', - trust_root = 'http://burr.unittest/', - return_to = 'http://burr.unittest/999', - immediate = False, - op_endpoint = self.server.op_endpoint, - ) - self.request.message = Message(OPENID2_NS) - self.response = server.OpenIDResponse(self.request) - self.response.fields = Message.fromOpenIDArgs({ - 'mode': 'id_res', - 'identity': self.request.identity, - 'return_to': self.request.return_to, - }) - self.signatory = server.Signatory(self.store) - self.encoder = server.SigningEncoder(self.signatory) - self.encode = self.encoder.encode - - def test_idres(self): - assoc_handle = '{bicycle}{shed}' - self.store.storeAssociation( - self._normal_key, - association.Association.fromExpiresIn(60, assoc_handle, - 'sekrit', 'HMAC-SHA1')) - self.request.assoc_handle = assoc_handle - webresponse = self.encode(self.response) - self.failUnlessEqual(webresponse.code, server.HTTP_REDIRECT) - self.failUnless(webresponse.headers.has_key('location')) - - location = webresponse.headers['location'] - query = cgi.parse_qs(urlparse(location)[4]) - self.failUnless('openid.sig' in query) - self.failUnless('openid.assoc_handle' in query) - self.failUnless('openid.signed' in query) - - def test_idresDumb(self): - webresponse = self.encode(self.response) - self.failUnlessEqual(webresponse.code, server.HTTP_REDIRECT) - self.failUnless(webresponse.headers.has_key('location')) - - location = webresponse.headers['location'] - query = cgi.parse_qs(urlparse(location)[4]) - self.failUnless('openid.sig' in query) - self.failUnless('openid.assoc_handle' in query) - self.failUnless('openid.signed' in query) - - def test_forgotStore(self): - self.encoder.signatory = None - self.failUnlessRaises(ValueError, self.encode, self.response) - - def test_cancel(self): - request = server.CheckIDRequest( - identity = 'http://bombom.unittest/', - trust_root = 'http://burr.unittest/', - return_to = 'http://burr.unittest/999', - immediate = False, - op_endpoint = self.server.op_endpoint, - ) - request.message = Message(OPENID2_NS) - response = server.OpenIDResponse(request) - response.fields.setArg(OPENID_NS, 'mode', 'cancel') - webresponse = self.encode(response) - self.failUnlessEqual(webresponse.code, server.HTTP_REDIRECT) - self.failUnless(webresponse.headers.has_key('location')) - location = webresponse.headers['location'] - query = cgi.parse_qs(urlparse(location)[4]) - self.failIf('openid.sig' in query, response.fields.toPostArgs()) - - def test_assocReply(self): - msg = Message(OPENID2_NS) - msg.setArg(OPENID2_NS, 'session_type', 'no-encryption') - request = server.AssociateRequest.fromMessage(msg) - response = server.OpenIDResponse(request) - response.fields = Message.fromOpenIDArgs({'assoc_handle': "every-zig"}) - webresponse = self.encode(response) - body = """assoc_handle:every-zig -""" - self.failUnlessEqual(webresponse.code, server.HTTP_OK) - self.failUnlessEqual(webresponse.headers, {}) - self.failUnlessEqual(webresponse.body, body) - - def test_alreadySigned(self): - self.response.fields.setArg(OPENID_NS, 'sig', 'priorSig==') - self.failUnlessRaises(server.AlreadySigned, self.encode, self.response) - -class TestCheckID(unittest.TestCase): - def setUp(self): - self.op_endpoint = 'http://endpoint.unittest/' - self.store = memstore.MemoryStore() - self.server = server.Server(self.store, self.op_endpoint) - self.request = server.CheckIDRequest( - identity = 'http://bambam.unittest/', - trust_root = 'http://bar.unittest/', - return_to = 'http://bar.unittest/999', - immediate = False, - op_endpoint = self.server.op_endpoint, - ) - self.request.message = Message(OPENID2_NS) - - def test_trustRootInvalid(self): - self.request.trust_root = "http://foo.unittest/17" - self.request.return_to = "http://foo.unittest/39" - self.failIf(self.request.trustRootValid()) - - def test_trustRootValid(self): - self.request.trust_root = "http://foo.unittest/" - self.request.return_to = "http://foo.unittest/39" - self.failUnless(self.request.trustRootValid()) - - def test_malformedTrustRoot(self): - self.request.trust_root = "invalid://trust*root/" - self.request.return_to = "http://foo.unittest/39" - sentinel = object() - self.request.message = sentinel - try: - result = self.request.trustRootValid() - except server.MalformedTrustRoot, why: - self.failUnless(sentinel is why.openid_message) - else: - self.fail('Expected MalformedTrustRoot exception. Got %r' - % (result,)) - - def test_trustRootValidNoReturnTo(self): - request = server.CheckIDRequest( - identity = 'http://bambam.unittest/', - trust_root = 'http://bar.unittest/', - return_to = None, - immediate = False, - op_endpoint = self.server.op_endpoint, - ) - - self.failUnless(request.trustRootValid()) - - def test_returnToVerified_callsVerify(self): - """Make sure that verifyReturnTo is calling the trustroot - function verifyReturnTo - """ - def withVerifyReturnTo(new_verify, callable): - old_verify = server.verifyReturnTo - try: - server.verifyReturnTo = new_verify - return callable() - finally: - server.verifyReturnTo = old_verify - - # Ensure that exceptions are passed through - sentinel = Exception() - def vrfyExc(trust_root, return_to): - self.failUnlessEqual(self.request.trust_root, trust_root) - self.failUnlessEqual(self.request.return_to, return_to) - raise sentinel - - try: - withVerifyReturnTo(vrfyExc, self.request.returnToVerified) - except Exception, e: - self.failUnless(e is sentinel, e) - - # Ensure that True and False are passed through unchanged - def constVerify(val): - def verify(trust_root, return_to): - self.failUnlessEqual(self.request.trust_root, trust_root) - self.failUnlessEqual(self.request.return_to, return_to) - return val - return verify - - for val in [True, False]: - self.failUnlessEqual( - val, - withVerifyReturnTo(constVerify(val), - self.request.returnToVerified)) - - def _expectAnswer(self, answer, identity=None, claimed_id=None): - expected_list = [ - ('mode', 'id_res'), - ('return_to', self.request.return_to), - ('op_endpoint', self.op_endpoint), - ] - if identity: - expected_list.append(('identity', identity)) - if claimed_id: - expected_list.append(('claimed_id', claimed_id)) - else: - expected_list.append(('claimed_id', identity)) - - for k, expected in expected_list: - actual = answer.fields.getArg(OPENID_NS, k) - self.failUnlessEqual(actual, expected, "%s: expected %s, got %s" % (k, expected, actual)) - - self.failUnless(answer.fields.hasKey(OPENID_NS, 'response_nonce')) - self.failUnless(answer.fields.getOpenIDNamespace() == OPENID2_NS) - - # One for nonce, one for ns - self.failUnlessEqual(len(answer.fields.toPostArgs()), - len(expected_list) + 2, - answer.fields.toPostArgs()) - - def test_answerAllow(self): - """Check the fields specified by "Positive Assertions" - - including mode=id_res, identity, claimed_id, op_endpoint, return_to - """ - answer = self.request.answer(True) - self.failUnlessEqual(answer.request, self.request) - self._expectAnswer(answer, self.request.identity) - - def test_answerAllowDelegatedIdentity(self): - self.request.claimed_id = 'http://delegating.unittest/' - answer = self.request.answer(True) - self._expectAnswer(answer, self.request.identity, - self.request.claimed_id) - - def test_answerAllowDelegatedIdentity2(self): - # This time with the identity argument explicitly passed in to - # answer() - self.request.claimed_id = 'http://delegating.unittest/' - answer = self.request.answer(True, identity='http://bambam.unittest/') - self._expectAnswer(answer, self.request.identity, - self.request.claimed_id) - - def test_answerAllowWithoutIdentityReally(self): - self.request.identity = None - answer = self.request.answer(True) - self.failUnlessEqual(answer.request, self.request) - self._expectAnswer(answer) - - def test_answerAllowAnonymousFail(self): - self.request.identity = None - # XXX - Check on this, I think this behavior is legal in OpenID 2.0? - self.failUnlessRaises( - ValueError, self.request.answer, True, identity="=V") - - def test_answerAllowWithIdentity(self): - self.request.identity = IDENTIFIER_SELECT - selected_id = 'http://anon.unittest/9861' - answer = self.request.answer(True, identity=selected_id) - self._expectAnswer(answer, selected_id) - - def test_answerAllowWithDelegatedIdentityOpenID2(self): - """Answer an IDENTIFIER_SELECT case with a delegated identifier. - """ - # claimed_id delegates to selected_id here. - self.request.identity = IDENTIFIER_SELECT - selected_id = 'http://anon.unittest/9861' - claimed_id = 'http://monkeyhat.unittest/' - answer = self.request.answer(True, identity=selected_id, - claimed_id=claimed_id) - self._expectAnswer(answer, selected_id, claimed_id) - - def test_answerAllowWithDelegatedIdentityOpenID1(self): - """claimed_id parameter doesn't exist in OpenID 1. - """ - self.request.message = Message(OPENID1_NS) - # claimed_id delegates to selected_id here. - self.request.identity = IDENTIFIER_SELECT - selected_id = 'http://anon.unittest/9861' - claimed_id = 'http://monkeyhat.unittest/' - self.failUnlessRaises(server.VersionError, - self.request.answer, True, - identity=selected_id, - claimed_id=claimed_id) - - def test_answerAllowWithAnotherIdentity(self): - # XXX - Check on this, I think this behavior is legal in OpenID 2.0? - self.failUnlessRaises(ValueError, self.request.answer, True, - identity="http://pebbles.unittest/") - - def test_answerAllowWithIdentityNormalization(self): - # The RP has sent us a non-normalized value for openid.identity, - # and the library user is passing an explicit value for identity - # to CheckIDRequest.answer. - non_normalized = 'http://bambam.unittest' - normalized = non_normalized + '/' - - self.request.identity = non_normalized - self.request.claimed_id = non_normalized - - answer = self.request.answer(True, identity=normalized) - - # Expect the values that were sent in the request, even though - # they're not normalized. - self._expectAnswer(answer, identity=non_normalized, - claimed_id=non_normalized) - - def test_answerAllowNoIdentityOpenID1(self): - self.request.message = Message(OPENID1_NS) - self.request.identity = None - self.failUnlessRaises(ValueError, self.request.answer, True, - identity=None) - - def test_answerAllowForgotEndpoint(self): - self.request.op_endpoint = None - self.failUnlessRaises(RuntimeError, self.request.answer, True) - - def test_checkIDWithNoIdentityOpenID1(self): - msg = Message(OPENID1_NS) - msg.setArg(OPENID_NS, 'return_to', 'bogus') - msg.setArg(OPENID_NS, 'trust_root', 'bogus') - msg.setArg(OPENID_NS, 'mode', 'checkid_setup') - msg.setArg(OPENID_NS, 'assoc_handle', 'bogus') - - self.failUnlessRaises(server.ProtocolError, - server.CheckIDRequest.fromMessage, - msg, self.server) - - def test_fromMessageClaimedIDWithoutIdentityOpenID2(self): - name = 'https://example.myopenid.com' - - msg = Message(OPENID2_NS) - msg.setArg(OPENID_NS, 'mode', 'checkid_setup') - msg.setArg(OPENID_NS, 'return_to', 'http://invalid:8000/rt') - msg.setArg(OPENID_NS, 'claimed_id', name) - - self.failUnlessRaises(server.ProtocolError, - server.CheckIDRequest.fromMessage, - msg, self.server) - - def test_fromMessageIdentityWithoutClaimedIDOpenID2(self): - name = 'https://example.myopenid.com' - - msg = Message(OPENID2_NS) - msg.setArg(OPENID_NS, 'mode', 'checkid_setup') - msg.setArg(OPENID_NS, 'return_to', 'http://invalid:8000/rt') - msg.setArg(OPENID_NS, 'identity', name) - - self.failUnlessRaises(server.ProtocolError, - server.CheckIDRequest.fromMessage, - msg, self.server) - - def test_trustRootOpenID1(self): - """Ignore openid.realm in OpenID 1""" - msg = Message(OPENID1_NS) - msg.setArg(OPENID_NS, 'mode', 'checkid_setup') - msg.setArg(OPENID_NS, 'trust_root', 'http://real_trust_root/') - msg.setArg(OPENID_NS, 'realm', 'http://fake_trust_root/') - msg.setArg(OPENID_NS, 'return_to', 'http://real_trust_root/foo') - msg.setArg(OPENID_NS, 'assoc_handle', 'bogus') - msg.setArg(OPENID_NS, 'identity', 'george') - - result = server.CheckIDRequest.fromMessage(msg, self.server.op_endpoint) - - self.failUnless(result.trust_root == 'http://real_trust_root/') - - def test_trustRootOpenID2(self): - """Ignore openid.trust_root in OpenID 2""" - msg = Message(OPENID2_NS) - msg.setArg(OPENID_NS, 'mode', 'checkid_setup') - msg.setArg(OPENID_NS, 'realm', 'http://real_trust_root/') - msg.setArg(OPENID_NS, 'trust_root', 'http://fake_trust_root/') - msg.setArg(OPENID_NS, 'return_to', 'http://real_trust_root/foo') - msg.setArg(OPENID_NS, 'assoc_handle', 'bogus') - msg.setArg(OPENID_NS, 'identity', 'george') - msg.setArg(OPENID_NS, 'claimed_id', 'george') - - result = server.CheckIDRequest.fromMessage(msg, self.server.op_endpoint) - - self.failUnless(result.trust_root == 'http://real_trust_root/') - - def test_answerAllowNoTrustRoot(self): - self.request.trust_root = None - answer = self.request.answer(True) - self.failUnlessEqual(answer.request, self.request) - self._expectAnswer(answer, self.request.identity) - - def test_fromMessageWithoutTrustRoot(self): - msg = Message(OPENID2_NS) - msg.setArg(OPENID_NS, 'mode', 'checkid_setup') - msg.setArg(OPENID_NS, 'return_to', 'http://real_trust_root/foo') - msg.setArg(OPENID_NS, 'assoc_handle', 'bogus') - msg.setArg(OPENID_NS, 'identity', 'george') - msg.setArg(OPENID_NS, 'claimed_id', 'george') - - result = server.CheckIDRequest.fromMessage(msg, self.server.op_endpoint) - - self.failUnlessEqual(result.trust_root, 'http://real_trust_root/foo') - - def test_fromMessageWithEmptyTrustRoot(self): - return_to = u'http://someplace.invalid/?go=thing' - msg = Message.fromPostArgs({ - u'openid.assoc_handle': u'{blah}{blah}{OZivdQ==}', - u'openid.claimed_id': u'http://delegated.invalid/', - u'openid.identity': u'http://op-local.example.com/', - u'openid.mode': u'checkid_setup', - u'openid.ns': u'http://openid.net/signon/1.0', - u'openid.return_to': return_to, - u'openid.trust_root': u''}) - - result = server.CheckIDRequest.fromMessage(msg, self.server.op_endpoint) - - self.failUnlessEqual(result.trust_root, return_to) - - def test_fromMessageWithoutTrustRootOrReturnTo(self): - msg = Message(OPENID2_NS) - msg.setArg(OPENID_NS, 'mode', 'checkid_setup') - msg.setArg(OPENID_NS, 'assoc_handle', 'bogus') - msg.setArg(OPENID_NS, 'identity', 'george') - msg.setArg(OPENID_NS, 'claimed_id', 'george') - - self.failUnlessRaises(server.ProtocolError, - server.CheckIDRequest.fromMessage, - msg, self.server.op_endpoint) - - def test_answerAllowNoEndpointOpenID1(self): - """Test .allow() with an OpenID 1.x Message on a CheckIDRequest - built without an op_endpoint parameter. - """ - identity = 'http://bambam.unittest/' - reqmessage = Message.fromOpenIDArgs({ - 'identity': identity, - 'trust_root': 'http://bar.unittest/', - 'return_to': 'http://bar.unittest/999', - }) - self.request = server.CheckIDRequest.fromMessage(reqmessage, None) - answer = self.request.answer(True) - - expected_list = [ - ('mode', 'id_res'), - ('return_to', self.request.return_to), - ('identity', identity), - ] - - for k, expected in expected_list: - actual = answer.fields.getArg(OPENID_NS, k) - self.failUnlessEqual( - expected, actual, - "%s: expected %s, got %s" % (k, expected, actual)) - - self.failUnless(answer.fields.hasKey(OPENID_NS, 'response_nonce')) - self.failUnlessEqual(answer.fields.getOpenIDNamespace(), OPENID1_NS) - self.failUnless(answer.fields.namespaces.isImplicit(OPENID1_NS)) - - # One for nonce (OpenID v1 namespace is implicit) - self.failUnlessEqual(len(answer.fields.toPostArgs()), - len(expected_list) + 1, - answer.fields.toPostArgs()) - - def test_answerImmediateDenyOpenID2(self): - """Look for mode=setup_needed in checkid_immediate negative - response in OpenID 2 case. - - See specification Responding to Authentication Requests / - Negative Assertions / In Response to Immediate Requests. - """ - self.request.mode = 'checkid_immediate' - self.request.immediate = True - self.request.claimed_id = 'http://claimed-id.test/' - server_url = "http://setup-url.unittest/" - # crappiting setup_url, you dirty my interface with your presence! - answer = self.request.answer(False, server_url=server_url) - self.failUnlessEqual(answer.request, self.request) - self.failUnlessEqual(len(answer.fields.toPostArgs()), 3, answer.fields) - self.failUnlessEqual(answer.fields.getOpenIDNamespace(), OPENID2_NS) - self.failUnlessEqual(answer.fields.getArg(OPENID_NS, 'mode'), - 'setup_needed') - - usu = answer.fields.getArg(OPENID_NS, 'user_setup_url') - expected_substr = 'openid.claimed_id=http%3A%2F%2Fclaimed-id.test%2F' - self.failUnless(expected_substr in usu, usu) - - def test_answerImmediateDenyOpenID1(self): - """Look for user_setup_url in checkid_immediate negative - response in OpenID 1 case.""" - self.request.message = Message(OPENID1_NS) - self.request.mode = 'checkid_immediate' - self.request.immediate = True - server_url = "http://setup-url.unittest/" - # crappiting setup_url, you dirty my interface with your presence! - answer = self.request.answer(False, server_url=server_url) - self.failUnlessEqual(answer.request, self.request) - self.failUnlessEqual(len(answer.fields.toPostArgs()), 2, answer.fields) - self.failUnlessEqual(answer.fields.getOpenIDNamespace(), OPENID1_NS) - self.failUnless(answer.fields.namespaces.isImplicit(OPENID1_NS)) - self.failUnlessEqual(answer.fields.getArg(OPENID_NS, 'mode'), 'id_res') - self.failUnless(answer.fields.getArg( - OPENID_NS, 'user_setup_url', '').startswith(server_url)) - - def test_answerSetupDeny(self): - answer = self.request.answer(False) - self.failUnlessEqual(answer.fields.getArgs(OPENID_NS), { - 'mode': 'cancel', - }) - - def test_encodeToURL(self): - server_url = 'http://openid-server.unittest/' - result = self.request.encodeToURL(server_url) - - # How to check? How about a round-trip test. - base, result_args = result.split('?', 1) - result_args = dict(cgi.parse_qsl(result_args)) - message = Message.fromPostArgs(result_args) - rebuilt_request = server.CheckIDRequest.fromMessage(message, - self.server.op_endpoint) - # argh, lousy hack - self.request.message = message - self.failUnlessEqual(rebuilt_request.__dict__, self.request.__dict__) - - def test_getCancelURL(self): - url = self.request.getCancelURL() - rt, query_string = url.split('?') - self.failUnlessEqual(self.request.return_to, rt) - query = dict(cgi.parse_qsl(query_string)) - self.failUnlessEqual(query, {'openid.mode':'cancel', - 'openid.ns':OPENID2_NS}) - - def test_getCancelURLimmed(self): - self.request.mode = 'checkid_immediate' - self.request.immediate = True - self.failUnlessRaises(ValueError, self.request.getCancelURL) - - - -class TestCheckIDExtension(unittest.TestCase): - - def setUp(self): - self.op_endpoint = 'http://endpoint.unittest/ext' - self.store = memstore.MemoryStore() - self.server = server.Server(self.store, self.op_endpoint) - self.request = server.CheckIDRequest( - identity = 'http://bambam.unittest/', - trust_root = 'http://bar.unittest/', - return_to = 'http://bar.unittest/999', - immediate = False, - op_endpoint = self.server.op_endpoint, - ) - self.request.message = Message(OPENID2_NS) - self.response = server.OpenIDResponse(self.request) - self.response.fields.setArg(OPENID_NS, 'mode', 'id_res') - self.response.fields.setArg(OPENID_NS, 'blue', 'star') - - - def test_addField(self): - namespace = 'something:' - self.response.fields.setArg(namespace, 'bright', 'potato') - self.failUnlessEqual(self.response.fields.getArgs(OPENID_NS), - {'blue': 'star', - 'mode': 'id_res', - }) - - self.failUnlessEqual(self.response.fields.getArgs(namespace), - {'bright':'potato'}) - - - def test_addFields(self): - namespace = 'mi5:' - args = {'tangy': 'suspenders', - 'bravo': 'inclusion'} - self.response.fields.updateArgs(namespace, args) - self.failUnlessEqual(self.response.fields.getArgs(OPENID_NS), - {'blue': 'star', - 'mode': 'id_res', - }) - self.failUnlessEqual(self.response.fields.getArgs(namespace), args) - - - -class MockSignatory(object): - isValid = True - - def __init__(self, assoc): - self.assocs = [assoc] - - def verify(self, assoc_handle, message): - assert message.hasKey(OPENID_NS, "sig") - if (True, assoc_handle) in self.assocs: - return self.isValid - else: - return False - - def getAssociation(self, assoc_handle, dumb): - if (dumb, assoc_handle) in self.assocs: - # This isn't a valid implementation for many uses of this - # function, mind you. - return True - else: - return None - - def invalidate(self, assoc_handle, dumb): - if (dumb, assoc_handle) in self.assocs: - self.assocs.remove((dumb, assoc_handle)) - - -class TestCheckAuth(unittest.TestCase): - def setUp(self): - self.assoc_handle = 'mooooooooo' - self.message = Message.fromPostArgs({ - 'openid.sig': 'signarture', - 'one': 'alpha', - 'two': 'beta', - }) - self.request = server.CheckAuthRequest( - self.assoc_handle, self.message) - - self.signatory = MockSignatory((True, self.assoc_handle)) - - def test_valid(self): - r = self.request.answer(self.signatory) - self.failUnlessEqual(r.fields.getArgs(OPENID_NS), {'is_valid': 'true'}) - self.failUnlessEqual(r.request, self.request) - - def test_invalid(self): - self.signatory.isValid = False - r = self.request.answer(self.signatory) - self.failUnlessEqual(r.fields.getArgs(OPENID_NS), - {'is_valid': 'false'}) - - def test_replay(self): - """Don't validate the same response twice. - - From "Checking the Nonce":: - - When using "check_authentication", the OP MUST ensure that an - assertion has not yet been accepted with the same value for - "openid.response_nonce". - - In this implementation, the assoc_handle is only valid once. And - nonces are a signed component of the message, so they can't be used - with another handle without breaking the sig. - """ - r = self.request.answer(self.signatory) - r = self.request.answer(self.signatory) - self.failUnlessEqual(r.fields.getArgs(OPENID_NS), - {'is_valid': 'false'}) - - def test_invalidatehandle(self): - self.request.invalidate_handle = "bogusHandle" - r = self.request.answer(self.signatory) - self.failUnlessEqual(r.fields.getArgs(OPENID_NS), - {'is_valid': 'true', - 'invalidate_handle': "bogusHandle"}) - self.failUnlessEqual(r.request, self.request) - - def test_invalidatehandleNo(self): - assoc_handle = 'goodhandle' - self.signatory.assocs.append((False, 'goodhandle')) - self.request.invalidate_handle = assoc_handle - r = self.request.answer(self.signatory) - self.failUnlessEqual(r.fields.getArgs(OPENID_NS), {'is_valid': 'true'}) - - -class TestAssociate(unittest.TestCase): - # TODO: test DH with non-default values for modulus and gen. - # (important to do because we actually had it broken for a while.) - - def setUp(self): - self.request = server.AssociateRequest.fromMessage( - Message.fromPostArgs({})) - self.store = memstore.MemoryStore() - self.signatory = server.Signatory(self.store) - - def test_dhSHA1(self): - self.assoc = self.signatory.createAssociation(dumb=False, assoc_type='HMAC-SHA1') - from askbot.deps.openid.dh import DiffieHellman - from askbot.deps.openid.server.server import DiffieHellmanSHA1ServerSession - consumer_dh = DiffieHellman.fromDefaults() - cpub = consumer_dh.public - server_dh = DiffieHellman.fromDefaults() - session = DiffieHellmanSHA1ServerSession(server_dh, cpub) - self.request = server.AssociateRequest(session, 'HMAC-SHA1') - response = self.request.answer(self.assoc) - rfg = lambda f: response.fields.getArg(OPENID_NS, f) - self.failUnlessEqual(rfg("assoc_type"), "HMAC-SHA1") - self.failUnlessEqual(rfg("assoc_handle"), self.assoc.handle) - self.failIf(rfg("mac_key")) - self.failUnlessEqual(rfg("session_type"), "DH-SHA1") - self.failUnless(rfg("enc_mac_key")) - self.failUnless(rfg("dh_server_public")) - - enc_key = rfg("enc_mac_key").decode('base64') - spub = cryptutil.base64ToLong(rfg("dh_server_public")) - secret = consumer_dh.xorSecret(spub, enc_key, cryptutil.sha1) - self.failUnlessEqual(secret, self.assoc.secret) - - - if not cryptutil.SHA256_AVAILABLE: - warnings.warn("Not running SHA256 tests.") - else: - def test_dhSHA256(self): - self.assoc = self.signatory.createAssociation( - dumb=False, assoc_type='HMAC-SHA256') - from askbot.deps.openid.dh import DiffieHellman - from askbot.deps.openid.server.server import DiffieHellmanSHA256ServerSession - consumer_dh = DiffieHellman.fromDefaults() - cpub = consumer_dh.public - server_dh = DiffieHellman.fromDefaults() - session = DiffieHellmanSHA256ServerSession(server_dh, cpub) - self.request = server.AssociateRequest(session, 'HMAC-SHA256') - response = self.request.answer(self.assoc) - rfg = lambda f: response.fields.getArg(OPENID_NS, f) - self.failUnlessEqual(rfg("assoc_type"), "HMAC-SHA256") - self.failUnlessEqual(rfg("assoc_handle"), self.assoc.handle) - self.failIf(rfg("mac_key")) - self.failUnlessEqual(rfg("session_type"), "DH-SHA256") - self.failUnless(rfg("enc_mac_key")) - self.failUnless(rfg("dh_server_public")) - - enc_key = rfg("enc_mac_key").decode('base64') - spub = cryptutil.base64ToLong(rfg("dh_server_public")) - secret = consumer_dh.xorSecret(spub, enc_key, cryptutil.sha256) - self.failUnlessEqual(secret, self.assoc.secret) - - def test_protoError256(self): - from askbot.deps.openid.consumer.consumer import \ - DiffieHellmanSHA256ConsumerSession - - s256_session = DiffieHellmanSHA256ConsumerSession() - - invalid_s256 = {'openid.assoc_type':'HMAC-SHA1', - 'openid.session_type':'DH-SHA256',} - invalid_s256.update(s256_session.getRequest()) - - invalid_s256_2 = {'openid.assoc_type':'MONKEY-PIRATE', - 'openid.session_type':'DH-SHA256',} - invalid_s256_2.update(s256_session.getRequest()) - - bad_request_argss = [ - invalid_s256, - invalid_s256_2, - ] - - for request_args in bad_request_argss: - message = Message.fromPostArgs(request_args) - self.failUnlessRaises(server.ProtocolError, - server.AssociateRequest.fromMessage, - message) - - def test_protoError(self): - from askbot.deps.openid.consumer.consumer import DiffieHellmanSHA1ConsumerSession - - s1_session = DiffieHellmanSHA1ConsumerSession() - - invalid_s1 = {'openid.assoc_type':'HMAC-SHA256', - 'openid.session_type':'DH-SHA1',} - invalid_s1.update(s1_session.getRequest()) - - invalid_s1_2 = {'openid.assoc_type':'ROBOT-NINJA', - 'openid.session_type':'DH-SHA1',} - invalid_s1_2.update(s1_session.getRequest()) - - bad_request_argss = [ - {'openid.assoc_type':'Wha?'}, - invalid_s1, - invalid_s1_2, - ] - - for request_args in bad_request_argss: - message = Message.fromPostArgs(request_args) - self.failUnlessRaises(server.ProtocolError, - server.AssociateRequest.fromMessage, - message) - - def test_protoErrorFields(self): - - contact = 'user@example.invalid' - reference = 'Trac ticket number MAX_INT' - error = 'poltergeist' - - openid1_args = { - 'openid.identitiy': 'invalid', - 'openid.mode': 'checkid_setup', - } - - openid2_args = dict(openid1_args) - openid2_args.update({'openid.ns': OPENID2_NS}) - - # Check presence of optional fields in both protocol versions - - openid1_msg = Message.fromPostArgs(openid1_args) - p = server.ProtocolError(openid1_msg, error, - contact=contact, reference=reference) - reply = p.toMessage() - - self.failUnlessEqual(reply.getArg(OPENID_NS, 'reference'), reference) - self.failUnlessEqual(reply.getArg(OPENID_NS, 'contact'), contact) - - openid2_msg = Message.fromPostArgs(openid2_args) - p = server.ProtocolError(openid2_msg, error, - contact=contact, reference=reference) - reply = p.toMessage() - - self.failUnlessEqual(reply.getArg(OPENID_NS, 'reference'), reference) - self.failUnlessEqual(reply.getArg(OPENID_NS, 'contact'), contact) - - def failUnlessExpiresInMatches(self, msg, expected_expires_in): - expires_in_str = msg.getArg(OPENID_NS, 'expires_in', no_default) - expires_in = int(expires_in_str) - - # Slop is necessary because the tests can sometimes get run - # right on a second boundary - slop = 1 # second - difference = expected_expires_in - expires_in - - error_message = ('"expires_in" value not within %s of expected: ' - 'expected=%s, actual=%s' % - (slop, expected_expires_in, expires_in)) - self.failUnless(0 <= difference <= slop, error_message) - - def test_plaintext(self): - self.assoc = self.signatory.createAssociation(dumb=False, assoc_type='HMAC-SHA1') - response = self.request.answer(self.assoc) - rfg = lambda f: response.fields.getArg(OPENID_NS, f) - - self.failUnlessEqual(rfg("assoc_type"), "HMAC-SHA1") - self.failUnlessEqual(rfg("assoc_handle"), self.assoc.handle) - - self.failUnlessExpiresInMatches( - response.fields, self.signatory.SECRET_LIFETIME) - - self.failUnlessEqual( - rfg("mac_key"), oidutil.toBase64(self.assoc.secret)) - self.failIf(rfg("session_type")) - self.failIf(rfg("enc_mac_key")) - self.failIf(rfg("dh_server_public")) - - def test_plaintext_v2(self): - # The main difference between this and the v1 test is that - # session_type is always returned in v2. - args = { - 'openid.ns': OPENID2_NS, - 'openid.mode': 'associate', - 'openid.assoc_type': 'HMAC-SHA1', - 'openid.session_type': 'no-encryption', - } - self.request = server.AssociateRequest.fromMessage( - Message.fromPostArgs(args)) - - self.failIf(self.request.message.isOpenID1()) - - self.assoc = self.signatory.createAssociation( - dumb=False, assoc_type='HMAC-SHA1') - response = self.request.answer(self.assoc) - rfg = lambda f: response.fields.getArg(OPENID_NS, f) - - self.failUnlessEqual(rfg("assoc_type"), "HMAC-SHA1") - self.failUnlessEqual(rfg("assoc_handle"), self.assoc.handle) - - self.failUnlessExpiresInMatches( - response.fields, self.signatory.SECRET_LIFETIME) - - self.failUnlessEqual( - rfg("mac_key"), oidutil.toBase64(self.assoc.secret)) - - self.failUnlessEqual(rfg("session_type"), "no-encryption") - self.failIf(rfg("enc_mac_key")) - self.failIf(rfg("dh_server_public")) - - def test_plaintext256(self): - self.assoc = self.signatory.createAssociation(dumb=False, assoc_type='HMAC-SHA256') - response = self.request.answer(self.assoc) - rfg = lambda f: response.fields.getArg(OPENID_NS, f) - - self.failUnlessEqual(rfg("assoc_type"), "HMAC-SHA1") - self.failUnlessEqual(rfg("assoc_handle"), self.assoc.handle) - - self.failUnlessExpiresInMatches( - response.fields, self.signatory.SECRET_LIFETIME) - - self.failUnlessEqual( - rfg("mac_key"), oidutil.toBase64(self.assoc.secret)) - self.failIf(rfg("session_type")) - self.failIf(rfg("enc_mac_key")) - self.failIf(rfg("dh_server_public")) - - def test_unsupportedPrefer(self): - allowed_assoc = 'COLD-PET-RAT' - allowed_sess = 'FROG-BONES' - message = 'This is a unit test' - - # Set an OpenID 2 message so answerUnsupported doesn't raise - # ProtocolError. - self.request.message = Message(OPENID2_NS) - - response = self.request.answerUnsupported( - message=message, - preferred_session_type=allowed_sess, - preferred_association_type=allowed_assoc, - ) - rfg = lambda f: response.fields.getArg(OPENID_NS, f) - self.failUnlessEqual(rfg('error_code'), 'unsupported-type') - self.failUnlessEqual(rfg('assoc_type'), allowed_assoc) - self.failUnlessEqual(rfg('error'), message) - self.failUnlessEqual(rfg('session_type'), allowed_sess) - - def test_unsupported(self): - message = 'This is a unit test' - - # Set an OpenID 2 message so answerUnsupported doesn't raise - # ProtocolError. - self.request.message = Message(OPENID2_NS) - - response = self.request.answerUnsupported(message) - rfg = lambda f: response.fields.getArg(OPENID_NS, f) - self.failUnlessEqual(rfg('error_code'), 'unsupported-type') - self.failUnlessEqual(rfg('assoc_type'), None) - self.failUnlessEqual(rfg('error'), message) - self.failUnlessEqual(rfg('session_type'), None) - -class Counter(object): - def __init__(self): - self.count = 0 - - def inc(self): - self.count += 1 - -class TestServer(unittest.TestCase, CatchLogs): - def setUp(self): - self.store = memstore.MemoryStore() - self.server = server.Server(self.store, "http://server.unittest/endpt") - CatchLogs.setUp(self) - - def test_dispatch(self): - monkeycalled = Counter() - def monkeyDo(request): - monkeycalled.inc() - r = server.OpenIDResponse(request) - return r - self.server.openid_monkeymode = monkeyDo - request = server.OpenIDRequest() - request.mode = "monkeymode" - request.namespace = OPENID1_NS - webresult = self.server.handleRequest(request) - self.failUnlessEqual(monkeycalled.count, 1) - - def test_associate(self): - request = server.AssociateRequest.fromMessage(Message.fromPostArgs({})) - response = self.server.openid_associate(request) - self.failUnless(response.fields.hasKey(OPENID_NS, "assoc_handle"), - "No assoc_handle here: %s" % (response.fields,)) - - def test_associate2(self): - """Associate when the server has no allowed association types - - Gives back an error with error_code and no fallback session or - assoc types.""" - self.server.negotiator.setAllowedTypes([]) - - # Set an OpenID 2 message so answerUnsupported doesn't raise - # ProtocolError. - msg = Message.fromPostArgs({ - 'openid.ns': OPENID2_NS, - 'openid.session_type': 'no-encryption', - }) - - request = server.AssociateRequest.fromMessage(msg) - - response = self.server.openid_associate(request) - self.failUnless(response.fields.hasKey(OPENID_NS, "error")) - self.failUnless(response.fields.hasKey(OPENID_NS, "error_code")) - self.failIf(response.fields.hasKey(OPENID_NS, "assoc_handle")) - self.failIf(response.fields.hasKey(OPENID_NS, "assoc_type")) - self.failIf(response.fields.hasKey(OPENID_NS, "session_type")) - - def test_associate3(self): - """Request an assoc type that is not supported when there are - supported types. - - Should give back an error message with a fallback type. - """ - self.server.negotiator.setAllowedTypes([('HMAC-SHA256', 'DH-SHA256')]) - - msg = Message.fromPostArgs({ - 'openid.ns': OPENID2_NS, - 'openid.session_type': 'no-encryption', - }) - - request = server.AssociateRequest.fromMessage(msg) - response = self.server.openid_associate(request) - - self.failUnless(response.fields.hasKey(OPENID_NS, "error")) - self.failUnless(response.fields.hasKey(OPENID_NS, "error_code")) - self.failIf(response.fields.hasKey(OPENID_NS, "assoc_handle")) - self.failUnlessEqual(response.fields.getArg(OPENID_NS, "assoc_type"), - 'HMAC-SHA256') - self.failUnlessEqual(response.fields.getArg(OPENID_NS, "session_type"), - 'DH-SHA256') - - if not cryptutil.SHA256_AVAILABLE: - warnings.warn("Not running SHA256 tests.") - else: - def test_associate4(self): - """DH-SHA256 association session""" - self.server.negotiator.setAllowedTypes( - [('HMAC-SHA256', 'DH-SHA256')]) - query = { - 'openid.dh_consumer_public': - 'ALZgnx8N5Lgd7pCj8K86T/DDMFjJXSss1SKoLmxE72kJTzOtG6I2PaYrHX' - 'xku4jMQWSsGfLJxwCZ6280uYjUST/9NWmuAfcrBfmDHIBc3H8xh6RBnlXJ' - '1WxJY3jHd5k1/ZReyRZOxZTKdF/dnIqwF8ZXUwI6peV0TyS/K1fOfF/s', - 'openid.assoc_type': 'HMAC-SHA256', - 'openid.session_type': 'DH-SHA256', - } - message = Message.fromPostArgs(query) - request = server.AssociateRequest.fromMessage(message) - response = self.server.openid_associate(request) - self.failUnless(response.fields.hasKey(OPENID_NS, "assoc_handle")) - - def test_missingSessionTypeOpenID2(self): - """Make sure session_type is required in OpenID 2""" - msg = Message.fromPostArgs({ - 'openid.ns': OPENID2_NS, - }) - - self.assertRaises(server.ProtocolError, - server.AssociateRequest.fromMessage, msg) - - def test_checkAuth(self): - request = server.CheckAuthRequest('arrrrrf', '0x3999', []) - response = self.server.openid_check_authentication(request) - self.failUnless(response.fields.hasKey(OPENID_NS, "is_valid")) - -class TestSignatory(unittest.TestCase, CatchLogs): - def setUp(self): - self.store = memstore.MemoryStore() - self.signatory = server.Signatory(self.store) - self._dumb_key = self.signatory._dumb_key - self._normal_key = self.signatory._normal_key - CatchLogs.setUp(self) - - def test_sign(self): - request = server.OpenIDRequest() - assoc_handle = '{assoc}{lookatme}' - self.store.storeAssociation( - self._normal_key, - association.Association.fromExpiresIn(60, assoc_handle, - 'sekrit', 'HMAC-SHA1')) - request.assoc_handle = assoc_handle - request.namespace = OPENID1_NS - response = server.OpenIDResponse(request) - response.fields = Message.fromOpenIDArgs({ - 'foo': 'amsigned', - 'bar': 'notsigned', - 'azu': 'alsosigned', - }) - sresponse = self.signatory.sign(response) - self.failUnlessEqual( - sresponse.fields.getArg(OPENID_NS, 'assoc_handle'), - assoc_handle) - self.failUnlessEqual(sresponse.fields.getArg(OPENID_NS, 'signed'), - 'assoc_handle,azu,bar,foo,signed') - self.failUnless(sresponse.fields.getArg(OPENID_NS, 'sig')) - self.failIf(self.messages, self.messages) - - def test_signDumb(self): - request = server.OpenIDRequest() - request.assoc_handle = None - request.namespace = OPENID2_NS - response = server.OpenIDResponse(request) - response.fields = Message.fromOpenIDArgs({ - 'foo': 'amsigned', - 'bar': 'notsigned', - 'azu': 'alsosigned', - 'ns':OPENID2_NS, - }) - sresponse = self.signatory.sign(response) - assoc_handle = sresponse.fields.getArg(OPENID_NS, 'assoc_handle') - self.failUnless(assoc_handle) - assoc = self.signatory.getAssociation(assoc_handle, dumb=True) - self.failUnless(assoc) - self.failUnlessEqual(sresponse.fields.getArg(OPENID_NS, 'signed'), - 'assoc_handle,azu,bar,foo,ns,signed') - self.failUnless(sresponse.fields.getArg(OPENID_NS, 'sig')) - self.failIf(self.messages, self.messages) - - def test_signExpired(self): - """Sign a response to a message with an expired handle (using invalidate_handle). - - From "Verifying with an Association":: - - If an authentication request included an association handle for an - association between the OP and the Relying party, and the OP no - longer wishes to use that handle (because it has expired or the - secret has been compromised, for instance), the OP will send a - response that must be verified directly with the OP, as specified - in Section 11.3.2. In that instance, the OP will include the field - "openid.invalidate_handle" set to the association handle that the - Relying Party included with the original request. - """ - request = server.OpenIDRequest() - request.namespace = OPENID2_NS - assoc_handle = '{assoc}{lookatme}' - self.store.storeAssociation( - self._normal_key, - association.Association.fromExpiresIn(-10, assoc_handle, - 'sekrit', 'HMAC-SHA1')) - self.failUnless(self.store.getAssociation(self._normal_key, assoc_handle)) - - request.assoc_handle = assoc_handle - response = server.OpenIDResponse(request) - response.fields = Message.fromOpenIDArgs({ - 'foo': 'amsigned', - 'bar': 'notsigned', - 'azu': 'alsosigned', - }) - sresponse = self.signatory.sign(response) - - new_assoc_handle = sresponse.fields.getArg(OPENID_NS, 'assoc_handle') - self.failUnless(new_assoc_handle) - self.failIfEqual(new_assoc_handle, assoc_handle) - - self.failUnlessEqual( - sresponse.fields.getArg(OPENID_NS, 'invalidate_handle'), - assoc_handle) - - self.failUnlessEqual(sresponse.fields.getArg(OPENID_NS, 'signed'), - 'assoc_handle,azu,bar,foo,invalidate_handle,signed') - self.failUnless(sresponse.fields.getArg(OPENID_NS, 'sig')) - - # make sure the expired association is gone - self.failIf(self.store.getAssociation(self._normal_key, assoc_handle), - "expired association is still retrievable.") - - # make sure the new key is a dumb mode association - self.failUnless(self.store.getAssociation(self._dumb_key, new_assoc_handle)) - self.failIf(self.store.getAssociation(self._normal_key, new_assoc_handle)) - self.failUnless(self.messages) - - - def test_signInvalidHandle(self): - request = server.OpenIDRequest() - request.namespace = OPENID2_NS - assoc_handle = '{bogus-assoc}{notvalid}' - - request.assoc_handle = assoc_handle - response = server.OpenIDResponse(request) - response.fields = Message.fromOpenIDArgs({ - 'foo': 'amsigned', - 'bar': 'notsigned', - 'azu': 'alsosigned', - }) - sresponse = self.signatory.sign(response) - - new_assoc_handle = sresponse.fields.getArg(OPENID_NS, 'assoc_handle') - self.failUnless(new_assoc_handle) - self.failIfEqual(new_assoc_handle, assoc_handle) - - self.failUnlessEqual( - sresponse.fields.getArg(OPENID_NS, 'invalidate_handle'), - assoc_handle) - - self.failUnlessEqual( - sresponse.fields.getArg(OPENID_NS, 'signed'), 'assoc_handle,azu,bar,foo,invalidate_handle,signed') - self.failUnless(sresponse.fields.getArg(OPENID_NS, 'sig')) - - # make sure the new key is a dumb mode association - self.failUnless(self.store.getAssociation(self._dumb_key, new_assoc_handle)) - self.failIf(self.store.getAssociation(self._normal_key, new_assoc_handle)) - self.failIf(self.messages, self.messages) - - - def test_verify(self): - assoc_handle = '{vroom}{zoom}' - assoc = association.Association.fromExpiresIn( - 60, assoc_handle, 'sekrit', 'HMAC-SHA1') - - self.store.storeAssociation(self._dumb_key, assoc) - - signed = Message.fromPostArgs({ - 'openid.foo': 'bar', - 'openid.apple': 'orange', - 'openid.assoc_handle': assoc_handle, - 'openid.signed': 'apple,assoc_handle,foo,signed', - 'openid.sig': 'uXoT1qm62/BB09Xbj98TQ8mlBco=', - }) - - verified = self.signatory.verify(assoc_handle, signed) - self.failIf(self.messages, self.messages) - self.failUnless(verified) - - - def test_verifyBadSig(self): - assoc_handle = '{vroom}{zoom}' - assoc = association.Association.fromExpiresIn( - 60, assoc_handle, 'sekrit', 'HMAC-SHA1') - - self.store.storeAssociation(self._dumb_key, assoc) - - signed = Message.fromPostArgs({ - 'openid.foo': 'bar', - 'openid.apple': 'orange', - 'openid.assoc_handle': assoc_handle, - 'openid.signed': 'apple,assoc_handle,foo,signed', - 'openid.sig': 'uXoT1qm62/BB09Xbj98TQ8mlBco='.encode('rot13'), - }) - - verified = self.signatory.verify(assoc_handle, signed) - self.failIf(self.messages, self.messages) - self.failIf(verified) - - def test_verifyBadHandle(self): - assoc_handle = '{vroom}{zoom}' - signed = Message.fromPostArgs({ - 'foo': 'bar', - 'apple': 'orange', - 'openid.sig': "Ylu0KcIR7PvNegB/K41KpnRgJl0=", - }) - - verified = self.signatory.verify(assoc_handle, signed) - self.failIf(verified) - self.failUnless(self.messages) - - - def test_verifyAssocMismatch(self): - """Attempt to validate sign-all message with a signed-list assoc.""" - assoc_handle = '{vroom}{zoom}' - assoc = association.Association.fromExpiresIn( - 60, assoc_handle, 'sekrit', 'HMAC-SHA1') - - self.store.storeAssociation(self._dumb_key, assoc) - - signed = Message.fromPostArgs({ - 'foo': 'bar', - 'apple': 'orange', - 'openid.sig': "d71xlHtqnq98DonoSgoK/nD+QRM=", - }) - - verified = self.signatory.verify(assoc_handle, signed) - self.failIf(verified) - self.failUnless(self.messages) - - def test_getAssoc(self): - assoc_handle = self.makeAssoc(dumb=True) - assoc = self.signatory.getAssociation(assoc_handle, True) - self.failUnless(assoc) - self.failUnlessEqual(assoc.handle, assoc_handle) - self.failIf(self.messages, self.messages) - - def test_getAssocExpired(self): - assoc_handle = self.makeAssoc(dumb=True, lifetime=-10) - assoc = self.signatory.getAssociation(assoc_handle, True) - self.failIf(assoc, assoc) - self.failUnless(self.messages) - - def test_getAssocInvalid(self): - ah = 'no-such-handle' - self.failUnlessEqual( - self.signatory.getAssociation(ah, dumb=False), None) - self.failIf(self.messages, self.messages) - - def test_getAssocDumbVsNormal(self): - """getAssociation(dumb=False) cannot get a dumb assoc""" - assoc_handle = self.makeAssoc(dumb=True) - self.failUnlessEqual( - self.signatory.getAssociation(assoc_handle, dumb=False), None) - self.failIf(self.messages, self.messages) - - def test_getAssocNormalVsDumb(self): - """getAssociation(dumb=True) cannot get a shared assoc - - From "Verifying Directly with the OpenID Provider":: - - An OP MUST NOT verify signatures for associations that have shared - MAC keys. - """ - assoc_handle = self.makeAssoc(dumb=False) - self.failUnlessEqual( - self.signatory.getAssociation(assoc_handle, dumb=True), None) - self.failIf(self.messages, self.messages) - - def test_createAssociation(self): - assoc = self.signatory.createAssociation(dumb=False) - self.failUnless(self.signatory.getAssociation(assoc.handle, dumb=False)) - self.failIf(self.messages, self.messages) - - def makeAssoc(self, dumb, lifetime=60): - assoc_handle = '{bling}' - assoc = association.Association.fromExpiresIn(lifetime, assoc_handle, - 'sekrit', 'HMAC-SHA1') - - self.store.storeAssociation((dumb and self._dumb_key) or self._normal_key, assoc) - return assoc_handle - - def test_invalidate(self): - assoc_handle = '-squash-' - assoc = association.Association.fromExpiresIn(60, assoc_handle, - 'sekrit', 'HMAC-SHA1') - - self.store.storeAssociation(self._dumb_key, assoc) - assoc = self.signatory.getAssociation(assoc_handle, dumb=True) - self.failUnless(assoc) - assoc = self.signatory.getAssociation(assoc_handle, dumb=True) - self.failUnless(assoc) - self.signatory.invalidate(assoc_handle, dumb=True) - assoc = self.signatory.getAssociation(assoc_handle, dumb=True) - self.failIf(assoc) - self.failIf(self.messages, self.messages) - - - -if __name__ == '__main__': - unittest.main() diff --git a/askbot/deps/openid/test/test_services.py b/askbot/deps/openid/test/test_services.py deleted file mode 100644 index 9459cc24..00000000 --- a/askbot/deps/openid/test/test_services.py +++ /dev/null @@ -1,23 +0,0 @@ -import unittest - -from askbot.deps.openid.yadis import services -from askbot.deps.openid.yadis.discover import DiscoveryFailure, DiscoveryResult - - -class TestGetServiceEndpoints(unittest.TestCase): - def setUp(self): - self.orig_discover = services.discover - services.discover = self.discover - - def tearDown(self): - services.discover = self.orig_discover - - def discover(self, input_url): - result = DiscoveryResult(input_url) - result.response_text = "This is not XRDS text." - return result - - def test_catchXRDSError(self): - self.failUnlessRaises(DiscoveryFailure, - services.getServiceEndpoints, - "http://example.invalid/sometest") diff --git a/askbot/deps/openid/test/test_sreg.py b/askbot/deps/openid/test/test_sreg.py deleted file mode 100644 index 5c3af027..00000000 --- a/askbot/deps/openid/test/test_sreg.py +++ /dev/null @@ -1,484 +0,0 @@ -from askbot.deps.openid.extensions import sreg -from askbot.deps.openid.message import NamespaceMap, Message, registerNamespaceAlias -from askbot.deps.openid.server.server import OpenIDRequest, OpenIDResponse - -import unittest - -class SRegURITest(unittest.TestCase): - def test_is11(self): - self.failUnlessEqual(sreg.ns_uri_1_1, sreg.ns_uri) - -class CheckFieldNameTest(unittest.TestCase): - def test_goodNamePasses(self): - for field_name in sreg.data_fields: - sreg.checkFieldName(field_name) - - def test_badNameFails(self): - self.failUnlessRaises(ValueError, sreg.checkFieldName, 'INVALID') - - def test_badTypeFails(self): - self.failUnlessRaises(ValueError, sreg.checkFieldName, None) - -# For supportsSReg test -class FakeEndpoint(object): - def __init__(self, supported): - self.supported = supported - self.checked_uris = [] - - def usesExtension(self, namespace_uri): - self.checked_uris.append(namespace_uri) - return namespace_uri in self.supported - -class SupportsSRegTest(unittest.TestCase): - def test_unsupported(self): - endpoint = FakeEndpoint([]) - self.failIf(sreg.supportsSReg(endpoint)) - self.failUnlessEqual([sreg.ns_uri_1_1, sreg.ns_uri_1_0], - endpoint.checked_uris) - - def test_supported_1_1(self): - endpoint = FakeEndpoint([sreg.ns_uri_1_1]) - self.failUnless(sreg.supportsSReg(endpoint)) - self.failUnlessEqual([sreg.ns_uri_1_1], endpoint.checked_uris) - - def test_supported_1_0(self): - endpoint = FakeEndpoint([sreg.ns_uri_1_0]) - self.failUnless(sreg.supportsSReg(endpoint)) - self.failUnlessEqual([sreg.ns_uri_1_1, sreg.ns_uri_1_0], - endpoint.checked_uris) - -class FakeMessage(object): - def __init__(self): - self.openid1 = False - self.namespaces = NamespaceMap() - - def isOpenID1(self): - return self.openid1 - -class GetNSTest(unittest.TestCase): - def setUp(self): - self.msg = FakeMessage() - - def test_openID2Empty(self): - ns_uri = sreg.getSRegNS(self.msg) - self.failUnlessEqual(self.msg.namespaces.getAlias(ns_uri), 'sreg') - self.failUnlessEqual(sreg.ns_uri, ns_uri) - - def test_openID1Empty(self): - self.msg.openid1 = True - ns_uri = sreg.getSRegNS(self.msg) - self.failUnlessEqual(self.msg.namespaces.getAlias(ns_uri), 'sreg') - self.failUnlessEqual(sreg.ns_uri, ns_uri) - - def test_openID1Defined_1_0(self): - self.msg.openid1 = True - self.msg.namespaces.add(sreg.ns_uri_1_0) - ns_uri = sreg.getSRegNS(self.msg) - self.failUnlessEqual(sreg.ns_uri_1_0, ns_uri) - - def test_openID1Defined_1_0_overrideAlias(self): - for openid_version in [True, False]: - for sreg_version in [sreg.ns_uri_1_0, sreg.ns_uri_1_1]: - for alias in ['sreg', 'bogus']: - self.setUp() - - self.msg.openid1 = openid_version - self.msg.namespaces.addAlias(sreg_version, alias) - ns_uri = sreg.getSRegNS(self.msg) - self.failUnlessEqual(self.msg.namespaces.getAlias(ns_uri), alias) - self.failUnlessEqual(sreg_version, ns_uri) - - def test_openID1DefinedBadly(self): - self.msg.openid1 = True - self.msg.namespaces.addAlias('http://invalid/', 'sreg') - self.failUnlessRaises(sreg.SRegNamespaceError, - sreg.getSRegNS, self.msg) - - def test_openID2DefinedBadly(self): - self.msg.openid1 = False - self.msg.namespaces.addAlias('http://invalid/', 'sreg') - self.failUnlessRaises(sreg.SRegNamespaceError, - sreg.getSRegNS, self.msg) - - def test_openID2Defined_1_0(self): - self.msg.namespaces.add(sreg.ns_uri_1_0) - ns_uri = sreg.getSRegNS(self.msg) - self.failUnlessEqual(sreg.ns_uri_1_0, ns_uri) - - def test_openID1_sregNSfromArgs(self): - args = { - 'sreg.optional': 'nickname', - 'sreg.required': 'dob', - } - - m = Message.fromOpenIDArgs(args) - - self.failUnless(m.getArg(sreg.ns_uri_1_1, 'optional') == 'nickname') - self.failUnless(m.getArg(sreg.ns_uri_1_1, 'required') == 'dob') - -class SRegRequestTest(unittest.TestCase): - def test_constructEmpty(self): - req = sreg.SRegRequest() - self.failUnlessEqual([], req.optional) - self.failUnlessEqual([], req.required) - self.failUnlessEqual(None, req.policy_url) - self.failUnlessEqual(sreg.ns_uri, req.ns_uri) - - def test_constructFields(self): - req = sreg.SRegRequest( - ['nickname'], - ['gender'], - 'http://policy', - 'http://sreg.ns_uri') - self.failUnlessEqual(['gender'], req.optional) - self.failUnlessEqual(['nickname'], req.required) - self.failUnlessEqual('http://policy', req.policy_url) - self.failUnlessEqual('http://sreg.ns_uri', req.ns_uri) - - def test_constructBadFields(self): - self.failUnlessRaises( - ValueError, - sreg.SRegRequest, ['elvis']) - - def test_fromOpenIDRequest(self): - args = {} - ns_sentinel = object() - args_sentinel = object() - - class FakeMessage(object): - copied = False - - def __init__(self): - self.message = Message() - - def getArgs(msg_self, ns_uri): - self.failUnlessEqual(ns_sentinel, ns_uri) - return args_sentinel - - def copy(msg_self): - msg_self.copied = True - return msg_self - - class TestingReq(sreg.SRegRequest): - def _getSRegNS(req_self, unused): - return ns_sentinel - - def parseExtensionArgs(req_self, args): - self.failUnlessEqual(args_sentinel, args) - - openid_req = OpenIDRequest() - - msg = FakeMessage() - openid_req.message = msg - - req = TestingReq.fromOpenIDRequest(openid_req) - self.failUnless(type(req) is TestingReq) - self.failUnless(msg.copied) - - def test_parseExtensionArgs_empty(self): - req = sreg.SRegRequest() - results = req.parseExtensionArgs({}) - self.failUnlessEqual(None, results) - - def test_parseExtensionArgs_extraIgnored(self): - req = sreg.SRegRequest() - req.parseExtensionArgs({'janrain':'inc'}) - - def test_parseExtensionArgs_nonStrict(self): - req = sreg.SRegRequest() - req.parseExtensionArgs({'required':'beans'}) - self.failUnlessEqual([], req.required) - - def test_parseExtensionArgs_strict(self): - req = sreg.SRegRequest() - self.failUnlessRaises( - ValueError, - req.parseExtensionArgs, {'required':'beans'}, strict=True) - - def test_parseExtensionArgs_policy(self): - req = sreg.SRegRequest() - req.parseExtensionArgs({'policy_url':'http://policy'}, strict=True) - self.failUnlessEqual('http://policy', req.policy_url) - - def test_parseExtensionArgs_requiredEmpty(self): - req = sreg.SRegRequest() - req.parseExtensionArgs({'required':''}, strict=True) - self.failUnlessEqual([], req.required) - - def test_parseExtensionArgs_optionalEmpty(self): - req = sreg.SRegRequest() - req.parseExtensionArgs({'optional':''}, strict=True) - self.failUnlessEqual([], req.optional) - - def test_parseExtensionArgs_optionalSingle(self): - req = sreg.SRegRequest() - req.parseExtensionArgs({'optional':'nickname'}, strict=True) - self.failUnlessEqual(['nickname'], req.optional) - - def test_parseExtensionArgs_optionalList(self): - req = sreg.SRegRequest() - req.parseExtensionArgs({'optional':'nickname,email'}, strict=True) - self.failUnlessEqual(['nickname','email'], req.optional) - - def test_parseExtensionArgs_optionalListBadNonStrict(self): - req = sreg.SRegRequest() - req.parseExtensionArgs({'optional':'nickname,email,beer'}) - self.failUnlessEqual(['nickname','email'], req.optional) - - def test_parseExtensionArgs_optionalListBadStrict(self): - req = sreg.SRegRequest() - self.failUnlessRaises( - ValueError, - req.parseExtensionArgs, {'optional':'nickname,email,beer'}, - strict=True) - - def test_parseExtensionArgs_bothNonStrict(self): - req = sreg.SRegRequest() - req.parseExtensionArgs({'optional':'nickname', - 'required':'nickname'}) - self.failUnlessEqual([], req.optional) - self.failUnlessEqual(['nickname'], req.required) - - def test_parseExtensionArgs_bothStrict(self): - req = sreg.SRegRequest() - self.failUnlessRaises( - ValueError, - req.parseExtensionArgs, - {'optional':'nickname', - 'required':'nickname'}, - strict=True) - - def test_parseExtensionArgs_bothList(self): - req = sreg.SRegRequest() - req.parseExtensionArgs({'optional':'nickname,email', - 'required':'country,postcode'}, strict=True) - self.failUnlessEqual(['nickname','email'], req.optional) - self.failUnlessEqual(['country','postcode'], req.required) - - def test_allRequestedFields(self): - req = sreg.SRegRequest() - self.failUnlessEqual([], req.allRequestedFields()) - req.requestField('nickname') - self.failUnlessEqual(['nickname'], req.allRequestedFields()) - req.requestField('gender', required=True) - requested = req.allRequestedFields() - requested.sort() - self.failUnlessEqual(['gender', 'nickname'], requested) - - def test_wereFieldsRequested(self): - req = sreg.SRegRequest() - self.failIf(req.wereFieldsRequested()) - req.requestField('gender') - self.failUnless(req.wereFieldsRequested()) - - def test_contains(self): - req = sreg.SRegRequest() - for field_name in sreg.data_fields: - self.failIf(field_name in req) - - self.failIf('something else' in req) - - req.requestField('nickname') - for field_name in sreg.data_fields: - if field_name == 'nickname': - self.failUnless(field_name in req) - else: - self.failIf(field_name in req) - - def test_requestField_bogus(self): - req = sreg.SRegRequest() - self.failUnlessRaises( - ValueError, - req.requestField, 'something else') - - self.failUnlessRaises( - ValueError, - req.requestField, 'something else', strict=True) - - def test_requestField(self): - # Add all of the fields, one at a time - req = sreg.SRegRequest() - fields = list(sreg.data_fields) - for field_name in fields: - req.requestField(field_name) - - self.failUnlessEqual(fields, req.optional) - self.failUnlessEqual([], req.required) - - # By default, adding the same fields over again has no effect - for field_name in fields: - req.requestField(field_name) - - self.failUnlessEqual(fields, req.optional) - self.failUnlessEqual([], req.required) - - # Requesting a field as required overrides requesting it as optional - expected = list(fields) - overridden = expected.pop(0) - req.requestField(overridden, required=True) - self.failUnlessEqual(expected, req.optional) - self.failUnlessEqual([overridden], req.required) - - # Requesting a field as required overrides requesting it as optional - for field_name in fields: - req.requestField(field_name, required=True) - - self.failUnlessEqual([], req.optional) - self.failUnlessEqual(fields, req.required) - - # Requesting it as optional does not downgrade it to optional - for field_name in fields: - req.requestField(field_name) - - self.failUnlessEqual([], req.optional) - self.failUnlessEqual(fields, req.required) - - def test_requestFields_type(self): - req = sreg.SRegRequest() - self.failUnlessRaises(TypeError, req.requestFields, 'nickname') - - def test_requestFields(self): - # Add all of the fields - req = sreg.SRegRequest() - - fields = list(sreg.data_fields) - req.requestFields(fields) - - self.failUnlessEqual(fields, req.optional) - self.failUnlessEqual([], req.required) - - # By default, adding the same fields over again has no effect - req.requestFields(fields) - - self.failUnlessEqual(fields, req.optional) - self.failUnlessEqual([], req.required) - - # Requesting a field as required overrides requesting it as optional - expected = list(fields) - overridden = expected.pop(0) - req.requestFields([overridden], required=True) - self.failUnlessEqual(expected, req.optional) - self.failUnlessEqual([overridden], req.required) - - # Requesting a field as required overrides requesting it as optional - req.requestFields(fields, required=True) - - self.failUnlessEqual([], req.optional) - self.failUnlessEqual(fields, req.required) - - # Requesting it as optional does not downgrade it to optional - req.requestFields(fields) - - self.failUnlessEqual([], req.optional) - self.failUnlessEqual(fields, req.required) - - def test_getExtensionArgs(self): - req = sreg.SRegRequest() - self.failUnlessEqual({}, req.getExtensionArgs()) - - req.requestField('nickname') - self.failUnlessEqual({'optional':'nickname'}, req.getExtensionArgs()) - - req.requestField('email') - self.failUnlessEqual({'optional':'nickname,email'}, - req.getExtensionArgs()) - - req.requestField('gender', required=True) - self.failUnlessEqual({'optional':'nickname,email', - 'required':'gender'}, - req.getExtensionArgs()) - - req.requestField('postcode', required=True) - self.failUnlessEqual({'optional':'nickname,email', - 'required':'gender,postcode'}, - req.getExtensionArgs()) - - req.policy_url = 'http://policy.invalid/' - self.failUnlessEqual({'optional':'nickname,email', - 'required':'gender,postcode', - 'policy_url':'http://policy.invalid/'}, - req.getExtensionArgs()) - -data = { - 'nickname':'linusaur', - 'postcode':'12345', - 'country':'US', - 'gender':'M', - 'fullname':'Leonhard Euler', - 'email':'president@whitehouse.gov', - 'dob':'0000-00-00', - 'language':'en-us', - } - -class DummySuccessResponse(object): - def __init__(self, message, signed_stuff): - self.message = message - self.signed_stuff = signed_stuff - - def getSignedNS(self, ns_uri): - return self.signed_stuff - -class SRegResponseTest(unittest.TestCase): - def test_construct(self): - resp = sreg.SRegResponse(data) - - self.failUnless(resp) - - empty_resp = sreg.SRegResponse({}) - self.failIf(empty_resp) - - # XXX: finish this test - - def test_fromSuccessResponse_signed(self): - message = Message.fromOpenIDArgs({ - 'sreg.nickname':'The Mad Stork', - }) - success_resp = DummySuccessResponse(message, {}) - sreg_resp = sreg.SRegResponse.fromSuccessResponse(success_resp) - self.failIf(sreg_resp) - - def test_fromSuccessResponse_unsigned(self): - message = Message.fromOpenIDArgs({ - 'sreg.nickname':'The Mad Stork', - }) - success_resp = DummySuccessResponse(message, {}) - sreg_resp = sreg.SRegResponse.fromSuccessResponse(success_resp, - signed_only=False) - self.failUnlessEqual([('nickname', 'The Mad Stork')], - sreg_resp.items()) - -class SendFieldsTest(unittest.TestCase): - def test(self): - # Create a request message with simple registration fields - sreg_req = sreg.SRegRequest(required=['nickname', 'email'], - optional=['fullname']) - req_msg = Message() - req_msg.updateArgs(sreg.ns_uri, sreg_req.getExtensionArgs()) - - req = OpenIDRequest() - req.message = req_msg - req.namespace = req_msg.getOpenIDNamespace() - - # -> send checkid_* request - - # Create an empty response message - resp_msg = Message() - resp = OpenIDResponse(req) - resp.fields = resp_msg - - # Put the requested data fields in the response message - sreg_resp = sreg.SRegResponse.extractResponse(sreg_req, data) - resp.addExtension(sreg_resp) - - # <- send id_res response - - # Extract the fields that were sent - sreg_data_resp = resp_msg.getArgs(sreg.ns_uri) - self.failUnlessEqual( - {'nickname':'linusaur', - 'email':'president@whitehouse.gov', - 'fullname':'Leonhard Euler', - }, sreg_data_resp) - -if __name__ == '__main__': - unittest.main() diff --git a/askbot/deps/openid/test/test_symbol.py b/askbot/deps/openid/test/test_symbol.py deleted file mode 100644 index 6f28048a..00000000 --- a/askbot/deps/openid/test/test_symbol.py +++ /dev/null @@ -1,35 +0,0 @@ -import unittest - -from askbot.deps.openid import oidutil - -class SymbolTest(unittest.TestCase): - def test_selfEquality(self): - s = oidutil.Symbol('xxx') - self.failUnlessEqual(s, s) - - def test_otherEquality(self): - x = oidutil.Symbol('xxx') - y = oidutil.Symbol('xxx') - self.failUnlessEqual(x, y) - - def test_inequality(self): - x = oidutil.Symbol('xxx') - y = oidutil.Symbol('yyy') - self.failIfEqual(x, y) - - def test_selfInequality(self): - x = oidutil.Symbol('xxx') - self.failIf(x != x) - - def test_otherInequality(self): - x = oidutil.Symbol('xxx') - y = oidutil.Symbol('xxx') - self.failIf(x != y) - - def test_ne_inequality(self): - x = oidutil.Symbol('xxx') - y = oidutil.Symbol('yyy') - self.failUnless(x != y) - -if __name__ == '__main__': - unittest.main() diff --git a/askbot/deps/openid/test/test_urinorm.py b/askbot/deps/openid/test/test_urinorm.py deleted file mode 100644 index ecc4344f..00000000 --- a/askbot/deps/openid/test/test_urinorm.py +++ /dev/null @@ -1,52 +0,0 @@ -import os -import unittest -from askbot.deps.openid import urinorm - -class UrinormTest(unittest.TestCase): - def __init__(self, desc, case, expected): - unittest.TestCase.__init__(self) - self.desc = desc - self.case = case - self.expected = expected - - def shortDescription(self): - return self.desc - - def runTest(self): - try: - actual = urinorm.urinorm(self.case) - except ValueError, why: - self.assertEqual(self.expected, 'fail', why) - else: - self.assertEqual(actual, self.expected) - - def parse(cls, full_case): - desc, case, expected = full_case.split('\n') - case = unicode(case, 'utf-8') - - return cls(desc, case, expected) - - parse = classmethod(parse) - - -def parseTests(test_data): - result = [] - - cases = test_data.split('\n\n') - for case in cases: - case = case.strip() - - if case: - result.append(UrinormTest.parse(case)) - - return result - -def pyUnitTests(): - here = os.path.dirname(os.path.abspath(__file__)) - test_data_file_name = os.path.join(here, 'urinorm.txt') - test_data_file = file(test_data_file_name) - test_data = test_data_file.read() - test_data_file.close() - - tests = parseTests(test_data) - return unittest.TestSuite(tests) diff --git a/askbot/deps/openid/test/test_verifydisco.py b/askbot/deps/openid/test/test_verifydisco.py deleted file mode 100644 index acf99f01..00000000 --- a/askbot/deps/openid/test/test_verifydisco.py +++ /dev/null @@ -1,270 +0,0 @@ -import unittest -from askbot.deps.openid import message -from askbot.deps.openid.test.support import OpenIDTestMixin -from askbot.deps.openid.consumer import consumer -from askbot.deps.openid.test.test_consumer import TestIdRes -from askbot.deps.openid.consumer import discover - -def const(result): - """Return a function that ignores any arguments and just returns - the specified result""" - def constResult(*args, **kwargs): - return result - - return constResult - -class DiscoveryVerificationTest(OpenIDTestMixin, TestIdRes): - def failUnlessProtocolError(self, prefix, callable, *args, **kwargs): - try: - result = callable(*args, **kwargs) - except consumer.ProtocolError, e: - self.failUnless( - e[0].startswith(prefix), - 'Expected message prefix %r, got message %r' % (prefix, e[0])) - else: - self.fail('Expected ProtocolError with prefix %r, ' - 'got successful return %r' % (prefix, result)) - - def test_openID1NoLocalID(self): - endpoint = discover.OpenIDServiceEndpoint() - endpoint.claimed_id = 'bogus' - - msg = message.Message.fromOpenIDArgs({}) - self.failUnlessProtocolError( - 'Missing required field openid.identity', - self.consumer._verifyDiscoveryResults, msg, endpoint) - self.failUnlessLogEmpty() - - def test_openID1NoEndpoint(self): - msg = message.Message.fromOpenIDArgs({'identity':'snakes on a plane'}) - self.failUnlessRaises(RuntimeError, - self.consumer._verifyDiscoveryResults, msg) - self.failUnlessLogEmpty() - - def test_openID2NoOPEndpointArg(self): - msg = message.Message.fromOpenIDArgs({'ns':message.OPENID2_NS}) - self.failUnlessRaises(KeyError, - self.consumer._verifyDiscoveryResults, msg) - self.failUnlessLogEmpty() - - def test_openID2LocalIDNoClaimed(self): - msg = message.Message.fromOpenIDArgs({'ns':message.OPENID2_NS, - 'op_endpoint':'Phone Home', - 'identity':'Jose Lius Borges'}) - self.failUnlessProtocolError( - 'openid.identity is present without', - self.consumer._verifyDiscoveryResults, msg) - self.failUnlessLogEmpty() - - def test_openID2NoLocalIDClaimed(self): - msg = message.Message.fromOpenIDArgs({'ns':message.OPENID2_NS, - 'op_endpoint':'Phone Home', - 'claimed_id':'Manuel Noriega'}) - self.failUnlessProtocolError( - 'openid.claimed_id is present without', - self.consumer._verifyDiscoveryResults, msg) - self.failUnlessLogEmpty() - - def test_openID2NoIdentifiers(self): - op_endpoint = 'Phone Home' - msg = message.Message.fromOpenIDArgs({'ns':message.OPENID2_NS, - 'op_endpoint':op_endpoint}) - result_endpoint = self.consumer._verifyDiscoveryResults(msg) - self.failUnless(result_endpoint.isOPIdentifier()) - self.failUnlessEqual(op_endpoint, result_endpoint.server_url) - self.failUnlessEqual(None, result_endpoint.claimed_id) - self.failUnlessLogEmpty() - - def test_openID2NoEndpointDoesDisco(self): - op_endpoint = 'Phone Home' - sentinel = discover.OpenIDServiceEndpoint() - sentinel.claimed_id = 'monkeysoft' - self.consumer._discoverAndVerify = const(sentinel) - msg = message.Message.fromOpenIDArgs( - {'ns':message.OPENID2_NS, - 'identity':'sour grapes', - 'claimed_id':'monkeysoft', - 'op_endpoint':op_endpoint}) - result = self.consumer._verifyDiscoveryResults(msg) - self.failUnlessEqual(sentinel, result) - self.failUnlessLogMatches('No pre-discovered') - - def test_openID2MismatchedDoesDisco(self): - mismatched = discover.OpenIDServiceEndpoint() - mismatched.identity = 'nothing special, but different' - mismatched.local_id = 'green cheese' - - op_endpoint = 'Phone Home' - sentinel = discover.OpenIDServiceEndpoint() - sentinel.claimed_id = 'monkeysoft' - self.consumer._discoverAndVerify = const(sentinel) - msg = message.Message.fromOpenIDArgs( - {'ns':message.OPENID2_NS, - 'identity':'sour grapes', - 'claimed_id':'monkeysoft', - 'op_endpoint':op_endpoint}) - result = self.consumer._verifyDiscoveryResults(msg, mismatched) - self.failUnlessEqual(sentinel, result) - self.failUnlessLogMatches('Error attempting to use stored', - 'Attempting discovery') - - def test_openid2UsePreDiscovered(self): - endpoint = discover.OpenIDServiceEndpoint() - endpoint.local_id = 'my identity' - endpoint.claimed_id = 'i am sam' - endpoint.server_url = 'Phone Home' - endpoint.type_uris = [discover.OPENID_2_0_TYPE] - - msg = message.Message.fromOpenIDArgs( - {'ns':message.OPENID2_NS, - 'identity':endpoint.local_id, - 'claimed_id':endpoint.claimed_id, - 'op_endpoint':endpoint.server_url}) - result = self.consumer._verifyDiscoveryResults(msg, endpoint) - self.failUnless(result is endpoint) - self.failUnlessLogEmpty() - - def test_openid2UsePreDiscoveredWrongType(self): - text = "verify failed" - - endpoint = discover.OpenIDServiceEndpoint() - endpoint.local_id = 'my identity' - endpoint.claimed_id = 'i am sam' - endpoint.server_url = 'Phone Home' - endpoint.type_uris = [discover.OPENID_1_1_TYPE] - - def discoverAndVerify(claimed_id, to_match_endpoints): - self.failUnlessEqual(claimed_id, endpoint.claimed_id) - for to_match in to_match_endpoints: - self.failUnlessEqual(claimed_id, to_match.claimed_id) - raise consumer.ProtocolError(text) - - self.consumer._discoverAndVerify = discoverAndVerify - - msg = message.Message.fromOpenIDArgs( - {'ns':message.OPENID2_NS, - 'identity':endpoint.local_id, - 'claimed_id':endpoint.claimed_id, - 'op_endpoint':endpoint.server_url}) - - try: - r = self.consumer._verifyDiscoveryResults(msg, endpoint) - except consumer.ProtocolError, e: - # Should we make more ProtocolError subclasses? - self.failUnless(str(e), text) - else: - self.fail("expected ProtocolError, %r returned." % (r,)) - - self.failUnlessLogMatches('Error attempting to use stored', - 'Attempting discovery') - - def test_openid1UsePreDiscovered(self): - endpoint = discover.OpenIDServiceEndpoint() - endpoint.local_id = 'my identity' - endpoint.claimed_id = 'i am sam' - endpoint.server_url = 'Phone Home' - endpoint.type_uris = [discover.OPENID_1_1_TYPE] - - msg = message.Message.fromOpenIDArgs( - {'ns':message.OPENID1_NS, - 'identity':endpoint.local_id}) - result = self.consumer._verifyDiscoveryResults(msg, endpoint) - self.failUnless(result is endpoint) - self.failUnlessLogEmpty() - - def test_openid1UsePreDiscoveredWrongType(self): - class VerifiedError(Exception): pass - - def discoverAndVerify(claimed_id, _to_match): - raise VerifiedError - - self.consumer._discoverAndVerify = discoverAndVerify - - endpoint = discover.OpenIDServiceEndpoint() - endpoint.local_id = 'my identity' - endpoint.claimed_id = 'i am sam' - endpoint.server_url = 'Phone Home' - endpoint.type_uris = [discover.OPENID_2_0_TYPE] - - msg = message.Message.fromOpenIDArgs( - {'ns':message.OPENID1_NS, - 'identity':endpoint.local_id}) - - self.failUnlessRaises( - VerifiedError, - self.consumer._verifyDiscoveryResults, msg, endpoint) - - self.failUnlessLogMatches('Error attempting to use stored', - 'Attempting discovery') - - def test_openid2Fragment(self): - claimed_id = "http://unittest.invalid/" - claimed_id_frag = claimed_id + "#fragment" - endpoint = discover.OpenIDServiceEndpoint() - endpoint.local_id = 'my identity' - endpoint.claimed_id = claimed_id - endpoint.server_url = 'Phone Home' - endpoint.type_uris = [discover.OPENID_2_0_TYPE] - - msg = message.Message.fromOpenIDArgs( - {'ns':message.OPENID2_NS, - 'identity':endpoint.local_id, - 'claimed_id': claimed_id_frag, - 'op_endpoint': endpoint.server_url}) - result = self.consumer._verifyDiscoveryResults(msg, endpoint) - - self.failUnlessEqual(result.local_id, endpoint.local_id) - self.failUnlessEqual(result.server_url, endpoint.server_url) - self.failUnlessEqual(result.type_uris, endpoint.type_uris) - - self.failUnlessEqual(result.claimed_id, claimed_id_frag) - - self.failUnlessLogEmpty() - - def test_openid1Fallback1_0(self): - claimed_id = 'http://claimed.id/' - endpoint = None - resp_mesg = message.Message.fromOpenIDArgs({ - 'ns': message.OPENID1_NS, - 'identity': claimed_id}) - # Pass the OpenID 1 claimed_id this way since we're passing - # None for the endpoint. - resp_mesg.setArg(message.BARE_NS, 'openid1_claimed_id', claimed_id) - - # We expect the OpenID 1 discovery verification to try - # matching the discovered endpoint against the 1.1 type and - # fall back to 1.0. - expected_endpoint = discover.OpenIDServiceEndpoint() - expected_endpoint.type_uris = [discover.OPENID_1_0_TYPE] - expected_endpoint.local_id = None - expected_endpoint.claimed_id = claimed_id - - discovered_services = [expected_endpoint] - self.consumer._discover = lambda *args: ('unused', discovered_services) - - actual_endpoint = self.consumer._verifyDiscoveryResults( - resp_mesg, endpoint) - self.failUnless(actual_endpoint is expected_endpoint) - -# XXX: test the implementation of _discoverAndVerify - - -class TestVerifyDiscoverySingle(TestIdRes): - # XXX: more test the implementation of _verifyDiscoverySingle - def test_endpointWithoutLocalID(self): - # An endpoint like this with no local_id is generated as a result of - # e.g. Yadis discovery with no LocalID tag. - endpoint = discover.OpenIDServiceEndpoint() - endpoint.server_url = "http://localhost:8000/openidserver" - endpoint.claimed_id = "http://localhost:8000/id/id-jo" - to_match = discover.OpenIDServiceEndpoint() - to_match.server_url = "http://localhost:8000/openidserver" - to_match.claimed_id = "http://localhost:8000/id/id-jo" - to_match.local_id = "http://localhost:8000/id/id-jo" - result = self.consumer._verifyDiscoverySingle(endpoint, to_match) - # result should always be None, raises exception on failure. - self.failUnlessEqual(result, None) - self.failUnlessLogEmpty() - -if __name__ == '__main__': - unittest.main() diff --git a/askbot/deps/openid/test/test_xri.py b/askbot/deps/openid/test/test_xri.py deleted file mode 100644 index ddab614f..00000000 --- a/askbot/deps/openid/test/test_xri.py +++ /dev/null @@ -1,102 +0,0 @@ -from unittest import TestCase -from askbot.deps.openid.yadis import xri - -class XriDiscoveryTestCase(TestCase): - def test_isXRI(self): - i = xri.identifierScheme - self.failUnlessEqual(i('=john.smith'), 'XRI') - self.failUnlessEqual(i('@smiths/john'), 'XRI') - self.failUnlessEqual(i('smoker.myopenid.com'), 'URI') - self.failUnlessEqual(i('xri://=john'), 'XRI') - self.failUnlessEqual(i(''), 'URI') - - -class XriEscapingTestCase(TestCase): - def test_escaping_percents(self): - self.failUnlessEqual(xri.escapeForIRI('@example/abc%2Fd/ef'), - '@example/abc%252Fd/ef') - - - def test_escaping_xref(self): - # no escapes - esc = xri.escapeForIRI - self.failUnlessEqual('@example/foo/(@bar)', esc('@example/foo/(@bar)')) - # escape slashes - self.failUnlessEqual('@example/foo/(@bar%2Fbaz)', - esc('@example/foo/(@bar/baz)')) - self.failUnlessEqual('@example/foo/(@bar%2Fbaz)/(+a%2Fb)', - esc('@example/foo/(@bar/baz)/(+a/b)')) - # escape query ? and fragment # - self.failUnlessEqual('@example/foo/(@baz%3Fp=q%23r)?i=j#k', - esc('@example/foo/(@baz?p=q#r)?i=j#k')) - - - -class XriTransformationTestCase(TestCase): - def test_to_iri_normal(self): - self.failUnlessEqual(xri.toIRINormal('@example'), 'xri://@example') - - try: - unichr(0x10000) - except ValueError: - # bleh narrow python build - def test_iri_to_url(self): - s = u'l\xa1m' - expected = 'l%C2%A1m' - self.failUnlessEqual(xri.iriToURI(s), expected) - else: - def test_iri_to_url(self): - s = u'l\xa1m\U00101010n' - expected = 'l%C2%A1m%F4%81%80%90n' - self.failUnlessEqual(xri.iriToURI(s), expected) - - - -class CanonicalIDTest(TestCase): - def mkTest(providerID, canonicalID, isAuthoritative): - def test(self): - result = xri.providerIsAuthoritative(providerID, canonicalID) - format = "%s providing %s, expected %s" - message = format % (providerID, canonicalID, isAuthoritative) - self.failUnlessEqual(isAuthoritative, result, message) - - return test - - test_equals = mkTest('=', '=!698.74D1.A1F2.86C7', True) - test_atOne = mkTest('@!1234', '@!1234!ABCD', True) - test_atTwo = mkTest('@!1234!5678', '@!1234!5678!ABCD', True) - - test_atEqualsFails = mkTest('@!1234', '=!1234!ABCD', False) - test_tooDeepFails = mkTest('@!1234', '@!1234!ABCD!9765', False) - test_atEqualsAndTooDeepFails = mkTest('@!1234!ABCD', '=!1234', False) - test_differentBeginningFails = mkTest('=!BABE', '=!D00D', False) - -class TestGetRootAuthority(TestCase): - def mkTest(the_xri, expected_root): - def test(self): - actual_root = xri.rootAuthority(the_xri) - self.failUnlessEqual(actual_root, xri.XRI(expected_root)) - return test - - test_at = mkTest("@foo", "@") - test_atStar = mkTest("@foo*bar", "@") - test_atStarStar = mkTest("@*foo*bar", "@") - test_atWithPath = mkTest("@foo/bar", "@") - test_bangBang = mkTest("!!990!991", "!") - test_bang = mkTest("!1001!02", "!") - test_equalsStar = mkTest("=foo*bar", "=") - test_xrefPath = mkTest("(example.com)/foo", "(example.com)") - test_xrefStar = mkTest("(example.com)*bar/foo", "(example.com)") - test_uriAuth = mkTest("baz.example.com/foo", "baz.example.com") - test_uriAuthPort = mkTest("baz.example.com:8080/foo", - "baz.example.com:8080") - - # Looking at the ABNF in XRI Syntax 2.0, I don't think you can - # have example.com*bar. You can do (example.com)*bar, but that - # would mean something else. - ##("example.com*bar/(=baz)", "example.com*bar"), - ##("baz.example.com!01/foo", "baz.example.com!01"), - -if __name__ == '__main__': - import unittest - unittest.main() diff --git a/askbot/deps/openid/test/test_xrires.py b/askbot/deps/openid/test/test_xrires.py deleted file mode 100644 index 02d010d6..00000000 --- a/askbot/deps/openid/test/test_xrires.py +++ /dev/null @@ -1,40 +0,0 @@ - -from unittest import TestCase -from askbot.deps.openid.yadis import xrires - -class ProxyQueryTestCase(TestCase): - def setUp(self): - self.proxy_url = 'http://xri.example.com/' - self.proxy = xrires.ProxyResolver(self.proxy_url) - self.servicetype = 'xri://+i-service*(+forwarding)*($v*1.0)' - self.servicetype_enc = 'xri%3A%2F%2F%2Bi-service%2A%28%2Bforwarding%29%2A%28%24v%2A1.0%29' - - - def test_proxy_url(self): - st = self.servicetype - ste = self.servicetype_enc - args_esc = "_xrd_r=application%2Fxrds%2Bxml&_xrd_t=" + ste - pqu = self.proxy.queryURL - h = self.proxy_url - self.failUnlessEqual(h + '=foo?' + args_esc, pqu('=foo', st)) - self.failUnlessEqual(h + '=foo/bar?baz&' + args_esc, - pqu('=foo/bar?baz', st)) - self.failUnlessEqual(h + '=foo/bar?baz=quux&' + args_esc, - pqu('=foo/bar?baz=quux', st)) - self.failUnlessEqual(h + '=foo/bar?mi=fa&so=la&' + args_esc, - pqu('=foo/bar?mi=fa&so=la', st)) - - # With no service endpoint selection. - args_esc = "_xrd_r=application%2Fxrds%2Bxml%3Bsep%3Dfalse" - self.failUnlessEqual(h + '=foo?' + args_esc, pqu('=foo', None)) - - - def test_proxy_url_qmarks(self): - st = self.servicetype - ste = self.servicetype_enc - args_esc = "_xrd_r=application%2Fxrds%2Bxml&_xrd_t=" + ste - pqu = self.proxy.queryURL - h = self.proxy_url - self.failUnlessEqual(h + '=foo/bar??' + args_esc, pqu('=foo/bar?', st)) - self.failUnlessEqual(h + '=foo/bar????' + args_esc, - pqu('=foo/bar???', st)) diff --git a/askbot/deps/openid/test/test_yadis_discover.py b/askbot/deps/openid/test/test_yadis_discover.py deleted file mode 100644 index aae83993..00000000 --- a/askbot/deps/openid/test/test_yadis_discover.py +++ /dev/null @@ -1,179 +0,0 @@ -#!/usr/bin/env python - -"""Tests for yadis.discover. - -@todo: Now that yadis.discover uses urljr.fetchers, we should be able to do - tests with a mock fetcher instead of spawning threads with BaseHTTPServer. -""" - -import unittest -import urlparse -import re -import types - -from askbot.deps.openid.yadis.discover import discover, DiscoveryFailure - -from askbot.deps.openid import fetchers - -import discoverdata - -status_header_re = re.compile(r'Status: (\d+) .*?$', re.MULTILINE) - -four04_pat = """\ -Content-Type: text/plain - -No such file %s -""" - -class QuitServer(Exception): pass - -def mkResponse(data): - status_mo = status_header_re.match(data) - headers_str, body = data.split('\n\n', 1) - headers = {} - for line in headers_str.split('\n'): - k, v = line.split(':', 1) - k = k.strip().lower() - v = v.strip() - headers[k] = v - status = int(status_mo.group(1)) - return fetchers.HTTPResponse(status=status, - headers=headers, - body=body) - -class TestFetcher(object): - def __init__(self, base_url): - self.base_url = base_url - - def fetch(self, url, headers, body): - current_url = url - while True: - parsed = urlparse.urlparse(current_url) - path = parsed[2][1:] - try: - data = discoverdata.generateSample(path, self.base_url) - except KeyError: - return fetchers.HTTPResponse(status=404, - final_url=current_url, - headers={}, - body='') - - response = mkResponse(data) - if response.status in [301, 302, 303, 307]: - current_url = response.headers['location'] - else: - response.final_url = current_url - return response - -class TestSecondGet(unittest.TestCase): - class MockFetcher(object): - def __init__(self): - self.count = 0 - def fetch(self, uri, headers=None, body=None): - self.count += 1 - if self.count == 1: - headers = { - 'X-XRDS-Location'.lower(): 'http://unittest/404', - } - return fetchers.HTTPResponse(uri, 200, headers, '') - else: - return fetchers.HTTPResponse(uri, 404) - - def setUp(self): - self.oldfetcher = fetchers.getDefaultFetcher() - fetchers.setDefaultFetcher(self.MockFetcher()) - - def tearDown(self): - fetchers.setDefaultFetcher(self.oldfetcher) - - def test_404(self): - uri = "http://something.unittest/" - self.failUnlessRaises(DiscoveryFailure, discover, uri) - - -class _TestCase(unittest.TestCase): - base_url = 'http://invalid.unittest/' - - def __init__(self, input_name, id_name, result_name, success): - self.input_name = input_name - self.id_name = id_name - self.result_name = result_name - self.success = success - # Still not quite sure how to best construct these custom tests. - # Between python2.3 and python2.4, a patch attached to pyunit.sf.net - # bug #469444 got applied which breaks loadTestsFromModule on this - # class if it has test_ or runTest methods. So, kludge to change - # the method name. - unittest.TestCase.__init__(self, methodName='runCustomTest') - - def setUp(self): - fetchers.setDefaultFetcher(TestFetcher(self.base_url), - wrap_exceptions=False) - - self.input_url, self.expected = discoverdata.generateResult( - self.base_url, - self.input_name, - self.id_name, - self.result_name, - self.success) - - def tearDown(self): - fetchers.setDefaultFetcher(None) - - def runCustomTest(self): - if self.expected is DiscoveryFailure: - self.failUnlessRaises(DiscoveryFailure, - discover, self.input_url) - else: - result = discover(self.input_url) - self.failUnlessEqual(self.input_url, result.request_uri) - - msg = 'Identity URL mismatch: actual = %r, expected = %r' % ( - result.normalized_uri, self.expected.normalized_uri) - self.failUnlessEqual( - self.expected.normalized_uri, result.normalized_uri, msg) - - msg = 'Content mismatch: actual = %r, expected = %r' % ( - result.response_text, self.expected.response_text) - self.failUnlessEqual( - self.expected.response_text, result.response_text, msg) - - expected_keys = dir(self.expected) - expected_keys.sort() - actual_keys = dir(result) - actual_keys.sort() - self.failUnlessEqual(actual_keys, expected_keys) - - for k in dir(self.expected): - if k.startswith('__') and k.endswith('__'): - continue - exp_v = getattr(self.expected, k) - if isinstance(exp_v, types.MethodType): - continue - act_v = getattr(result, k) - assert act_v == exp_v, (k, exp_v, act_v) - - def shortDescription(self): - try: - n = self.input_url - except AttributeError: - # run before setUp, or if setUp did not complete successfully. - n = self.input_name - return "%s (%s)" % ( - n, - self.__class__.__module__) - -def pyUnitTests(): - s = unittest.TestSuite() - for success, input_name, id_name, result_name in discoverdata.testlist: - test = _TestCase(input_name, id_name, result_name, success) - s.addTest(test) - - return s - -def test(): - runner = unittest.TextTestRunner() - return runner.run(loadTests()) - -if __name__ == '__main__': - test() diff --git a/askbot/deps/openid/test/trustroot.py b/askbot/deps/openid/test/trustroot.py deleted file mode 100644 index 2c378823..00000000 --- a/askbot/deps/openid/test/trustroot.py +++ /dev/null @@ -1,85 +0,0 @@ -import os -import unittest -from askbot.deps.openid.server.trustroot import TrustRoot - -class _ParseTest(unittest.TestCase): - def __init__(self, sanity, desc, case): - unittest.TestCase.__init__(self) - self.desc = desc + ': ' + repr(case) - self.case = case - self.sanity = sanity - - def shortDescription(self): - return self.desc - - def runTest(self): - tr = TrustRoot.parse(self.case) - if self.sanity == 'sane': - assert tr.isSane(), self.case - elif self.sanity == 'insane': - assert not tr.isSane(), self.case - else: - assert tr is None, tr - -class _MatchTest(unittest.TestCase): - def __init__(self, match, desc, line): - unittest.TestCase.__init__(self) - tr, rt = line.split() - self.desc = desc + ': ' + repr(tr) + ' ' + repr(rt) - self.tr = tr - self.rt = rt - self.match = match - - def shortDescription(self): - return self.desc - - def runTest(self): - tr = TrustRoot.parse(self.tr) - self.failIf(tr is None, self.tr) - - match = tr.validateURL(self.rt) - if self.match: - assert match - else: - assert not match - -def getTests(t, grps, head, dat): - tests = [] - top = head.strip() - gdat = map(str.strip, dat.split('-' * 40 + '\n')) - assert not gdat[0] - assert len(gdat) == (len(grps) * 2 + 1), (gdat, grps) - i = 1 - for x in grps: - n, desc = gdat[i].split(': ') - cases = gdat[i + 1].split('\n') - assert len(cases) == int(n) - for case in cases: - tests.append(t(x, top + ' - ' + desc, case)) - i += 2 - return tests - -def parseTests(data): - parts = map(str.strip, data.split('=' * 40 + '\n')) - assert not parts[0] - _, ph, pdat, mh, mdat = parts - - tests = [] - tests.extend(getTests(_ParseTest, ['bad', 'insane', 'sane'], ph, pdat)) - tests.extend(getTests(_MatchTest, [1, 0], mh, mdat)) - return tests - -def pyUnitTests(): - here = os.path.dirname(os.path.abspath(__file__)) - test_data_file_name = os.path.join(here, 'data', 'trustroot.txt') - test_data_file = file(test_data_file_name) - test_data = test_data_file.read() - test_data_file.close() - - tests = parseTests(test_data) - return unittest.TestSuite(tests) - -if __name__ == '__main__': - suite = pyUnitTests() - runner = unittest.TextTestRunner() - runner.run(suite) diff --git a/askbot/deps/openid/test/urinorm.txt b/askbot/deps/openid/test/urinorm.txt deleted file mode 100644 index a5db39e9..00000000 --- a/askbot/deps/openid/test/urinorm.txt +++ /dev/null @@ -1,87 +0,0 @@ -Already normal form -http://example.com/ -http://example.com/ - -Add a trailing slash -http://example.com -http://example.com/ - -Remove an empty port segment -http://example.com:/ -http://example.com/ - -Remove a default port segment -http://example.com:80/ -http://example.com/ - -Capitalization in host names -http://wWw.exaMPLE.COm/ -http://www.example.com/ - -Capitalization in scheme names -htTP://example.com/ -http://example.com/ - -Capitalization in percent-escaped reserved characters -http://example.com/foo%2cbar -http://example.com/foo%2Cbar - -Unescape percent-encoded unreserved characters -http://example.com/foo%2Dbar%2dbaz -http://example.com/foo-bar-baz - -remove_dot_segments example 1 -http://example.com/a/b/c/./../../g -http://example.com/a/g - -remove_dot_segments example 2 -http://example.com/mid/content=5/../6 -http://example.com/mid/6 - -remove_dot_segments: single-dot -http://example.com/a/./b -http://example.com/a/b - -remove_dot_segments: double-dot -http://example.com/a/../b -http://example.com/b - -remove_dot_segments: leading double-dot -http://example.com/../b -http://example.com/b - -remove_dot_segments: trailing single-dot -http://example.com/a/. -http://example.com/a/ - -remove_dot_segments: trailing double-dot -http://example.com/a/.. -http://example.com/ - -remove_dot_segments: trailing single-dot-slash -http://example.com/a/./ -http://example.com/a/ - -remove_dot_segments: trailing double-dot-slash -http://example.com/a/../ -http://example.com/ - -Test of all kinds of syntax-based normalization -hTTPS://a/./b/../b/%63/%7bfoo%7d -https://a/b/c/%7Bfoo%7D - -Unsupported scheme -ftp://example.com/ -fail - -Non-absolute URI -http:/foo -fail - -Illegal character in URI -http://<illegal>.com/ -fail - -Non-ascii character in URI -http://foo.com/ -fail diff --git a/askbot/deps/openid/urinorm.py b/askbot/deps/openid/urinorm.py deleted file mode 100644 index 5bdbaeff..00000000 --- a/askbot/deps/openid/urinorm.py +++ /dev/null @@ -1,202 +0,0 @@ -import re - -# from appendix B of rfc 3986 (http://www.ietf.org/rfc/rfc3986.txt) -uri_pattern = r'^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?' -uri_re = re.compile(uri_pattern) - -# gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" -# -# sub-delims = "!" / "$" / "&" / "'" / "(" / ")" -# / "*" / "+" / "," / ";" / "=" -# -# unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" - -uri_illegal_char_re = re.compile( - "[^-A-Za-z0-9:/?#[\]@!$&'()*+,;=._~%]", re.UNICODE) - -authority_pattern = r'^([^@]*@)?([^:]*)(:.*)?' -authority_re = re.compile(authority_pattern) - - -pct_encoded_pattern = r'%([0-9A-Fa-f]{2})' -pct_encoded_re = re.compile(pct_encoded_pattern) - -try: - unichr(0x10000) -except ValueError: - # narrow python build - UCSCHAR = [ - (0xA0, 0xD7FF), - (0xF900, 0xFDCF), - (0xFDF0, 0xFFEF), - ] - - IPRIVATE = [ - (0xE000, 0xF8FF), - ] -else: - UCSCHAR = [ - (0xA0, 0xD7FF), - (0xF900, 0xFDCF), - (0xFDF0, 0xFFEF), - (0x10000, 0x1FFFD), - (0x20000, 0x2FFFD), - (0x30000, 0x3FFFD), - (0x40000, 0x4FFFD), - (0x50000, 0x5FFFD), - (0x60000, 0x6FFFD), - (0x70000, 0x7FFFD), - (0x80000, 0x8FFFD), - (0x90000, 0x9FFFD), - (0xA0000, 0xAFFFD), - (0xB0000, 0xBFFFD), - (0xC0000, 0xCFFFD), - (0xD0000, 0xDFFFD), - (0xE1000, 0xEFFFD), - ] - - IPRIVATE = [ - (0xE000, 0xF8FF), - (0xF0000, 0xFFFFD), - (0x100000, 0x10FFFD), - ] - - -_unreserved = [False] * 256 -for _ in range(ord('A'), ord('Z') + 1): _unreserved[_] = True -for _ in range(ord('0'), ord('9') + 1): _unreserved[_] = True -for _ in range(ord('a'), ord('z') + 1): _unreserved[_] = True -_unreserved[ord('-')] = True -_unreserved[ord('.')] = True -_unreserved[ord('_')] = True -_unreserved[ord('~')] = True - - -_escapeme_re = re.compile('[%s]' % (''.join( - map(lambda (m, n): u'%s-%s' % (unichr(m), unichr(n)), - UCSCHAR + IPRIVATE)),)) - - -def _pct_escape_unicode(char_match): - c = char_match.group() - return ''.join(['%%%X' % (ord(octet),) for octet in c.encode('utf-8')]) - - -def _pct_encoded_replace_unreserved(mo): - try: - i = int(mo.group(1), 16) - if _unreserved[i]: - return chr(i) - else: - return mo.group().upper() - - except ValueError: - return mo.group() - - -def _pct_encoded_replace(mo): - try: - return chr(int(mo.group(1), 16)) - except ValueError: - return mo.group() - - -def remove_dot_segments(path): - result_segments = [] - - while path: - if path.startswith('../'): - path = path[3:] - elif path.startswith('./'): - path = path[2:] - elif path.startswith('/./'): - path = path[2:] - elif path == '/.': - path = '/' - elif path.startswith('/../'): - path = path[3:] - if result_segments: - result_segments.pop() - elif path == '/..': - path = '/' - if result_segments: - result_segments.pop() - elif path == '..' or path == '.': - path = '' - else: - i = 0 - if path[0] == '/': - i = 1 - i = path.find('/', i) - if i == -1: - i = len(path) - result_segments.append(path[:i]) - path = path[i:] - - return ''.join(result_segments) - - -def urinorm(uri): - if isinstance(uri, unicode): - uri = _escapeme_re.sub(_pct_escape_unicode, uri).encode('ascii') - - illegal_mo = uri_illegal_char_re.search(uri) - if illegal_mo: - raise ValueError('Illegal characters in URI: %r at position %s' % - (illegal_mo.group(), illegal_mo.start())) - - uri_mo = uri_re.match(uri) - - scheme = uri_mo.group(2) - if scheme is None: - raise ValueError('No scheme specified') - - scheme = scheme.lower() - if scheme not in ('http', 'https'): - raise ValueError('Not an absolute HTTP or HTTPS URI: %r' % (uri,)) - - authority = uri_mo.group(4) - if authority is None: - raise ValueError('Not an absolute URI: %r' % (uri,)) - - authority_mo = authority_re.match(authority) - if authority_mo is None: - raise ValueError('URI does not have a valid authority: %r' % (uri,)) - - userinfo, host, port = authority_mo.groups() - - if userinfo is None: - userinfo = '' - - if '%' in host: - host = host.lower() - host = pct_encoded_re.sub(_pct_encoded_replace, host) - host = unicode(host, 'utf-8').encode('idna') - else: - host = host.lower() - - if port: - if (port == ':' or - (scheme == 'http' and port == ':80') or - (scheme == 'https' and port == ':443')): - port = '' - else: - port = '' - - authority = userinfo + host + port - - path = uri_mo.group(5) - path = pct_encoded_re.sub(_pct_encoded_replace_unreserved, path) - path = remove_dot_segments(path) - if not path: - path = '/' - - query = uri_mo.group(6) - if query is None: - query = '' - - fragment = uri_mo.group(8) - if fragment is None: - fragment = '' - - return scheme + '://' + authority + path + query + fragment diff --git a/askbot/deps/openid/yadis/__init__.py b/askbot/deps/openid/yadis/__init__.py deleted file mode 100644 index cfa5f1e7..00000000 --- a/askbot/deps/openid/yadis/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ - - -__all__ = [ - 'constants', - 'discover', - 'etxrd', - 'filters', - 'manager', - 'parsehtml', - 'services', - 'xri', - 'xrires', - ] - -__version__ = '[library version:1.1.0-rc1]'[17:-1] - -# Parse the version info -try: - version_info = map(int, __version__.split('.')) -except ValueError: - version_info = (None, None, None) -else: - if len(version_info) != 3: - version_info = (None, None, None) - else: - version_info = tuple(version_info) diff --git a/askbot/deps/openid/yadis/accept.py b/askbot/deps/openid/yadis/accept.py deleted file mode 100644 index d7508131..00000000 --- a/askbot/deps/openid/yadis/accept.py +++ /dev/null @@ -1,133 +0,0 @@ -"""Functions for generating and parsing HTTP Accept: headers for -supporting server-directed content negotiation. -""" - -def generateAcceptHeader(*elements): - """Generate an accept header value - - [str or (str, float)] -> str - """ - parts = [] - for element in elements: - if type(element) is str: - qs = "1.0" - mtype = element - else: - mtype, q = element - q = float(q) - if q > 1 or q <= 0: - raise ValueError('Invalid preference factor: %r' % q) - - qs = '%0.1f' % (q,) - - parts.append((qs, mtype)) - - parts.sort() - chunks = [] - for q, mtype in parts: - if q == '1.0': - chunks.append(mtype) - else: - chunks.append('%s; q=%s' % (mtype, q)) - - return ', '.join(chunks) - -def parseAcceptHeader(value): - """Parse an accept header, ignoring any accept-extensions - - returns a list of tuples containing main MIME type, MIME subtype, - and quality markdown. - - str -> [(str, str, float)] - """ - chunks = [chunk.strip() for chunk in value.split(',')] - accept = [] - for chunk in chunks: - parts = [s.strip() for s in chunk.split(';')] - - mtype = parts.pop(0) - if '/' not in mtype: - # This is not a MIME type, so ignore the bad data - continue - - main, sub = mtype.split('/', 1) - - for ext in parts: - if '=' in ext: - k, v = ext.split('=', 1) - if k == 'q': - try: - q = float(v) - break - except ValueError: - # Ignore poorly formed q-values - pass - else: - q = 1.0 - - accept.append((q, main, sub)) - - accept.sort() - accept.reverse() - return [(main, sub, q) for (q, main, sub) in accept] - -def matchTypes(accept_types, have_types): - """Given the result of parsing an Accept: header, and the - available MIME types, return the acceptable types with their - quality markdowns. - - For example: - - >>> acceptable = parseAcceptHeader('text/html, text/plain; q=0.5') - >>> matchTypes(acceptable, ['text/plain', 'text/html', 'image/jpeg']) - [('text/html', 1.0), ('text/plain', 0.5)] - - - Type signature: ([(str, str, float)], [str]) -> [(str, float)] - """ - if not accept_types: - # Accept all of them - default = 1 - else: - default = 0 - - match_main = {} - match_sub = {} - for (main, sub, q) in accept_types: - if main == '*': - default = max(default, q) - continue - elif sub == '*': - match_main[main] = max(match_main.get(main, 0), q) - else: - match_sub[(main, sub)] = max(match_sub.get((main, sub), 0), q) - - accepted_list = [] - order_maintainer = 0 - for mtype in have_types: - main, sub = mtype.split('/') - if (main, sub) in match_sub: - q = match_sub[(main, sub)] - else: - q = match_main.get(main, default) - - if q: - accepted_list.append((1 - q, order_maintainer, q, mtype)) - order_maintainer += 1 - - accepted_list.sort() - return [(mtype, q) for (_, _, q, mtype) in accepted_list] - -def getAcceptable(accept_header, have_types): - """Parse the accept header and return a list of available types in - preferred order. If a type is unacceptable, it will not be in the - resulting list. - - This is a convenience wrapper around matchTypes and - parseAcceptHeader. - - (str, [str]) -> [str] - """ - accepted = parseAcceptHeader(accept_header) - preferred = matchTypes(accepted, have_types) - return [mtype for (mtype, _) in preferred] diff --git a/askbot/deps/openid/yadis/constants.py b/askbot/deps/openid/yadis/constants.py deleted file mode 100644 index 83765b02..00000000 --- a/askbot/deps/openid/yadis/constants.py +++ /dev/null @@ -1,13 +0,0 @@ -__all__ = ['YADIS_HEADER_NAME', 'YADIS_CONTENT_TYPE', 'YADIS_ACCEPT_HEADER'] -from askbot.deps.openid.yadis.accept import generateAcceptHeader - -YADIS_HEADER_NAME = 'X-XRDS-Location' -YADIS_CONTENT_TYPE = 'application/xrds+xml' - -# A value suitable for using as an accept header when performing YADIS -# discovery, unless the application has special requirements -YADIS_ACCEPT_HEADER = generateAcceptHeader( - ('text/html', 0.3), - ('application/xhtml+xml', 0.5), - (YADIS_CONTENT_TYPE, 1.0), - ) diff --git a/askbot/deps/openid/yadis/discover.py b/askbot/deps/openid/yadis/discover.py deleted file mode 100644 index 7e714fde..00000000 --- a/askbot/deps/openid/yadis/discover.py +++ /dev/null @@ -1,135 +0,0 @@ -# -*- test-case-name: openid.test.test_yadis_discover -*- -__all__ = ['discover', 'DiscoveryResult', 'DiscoveryFailure'] - -from cStringIO import StringIO - -from askbot.deps.openid import fetchers - -from askbot.deps.openid.yadis.constants import \ - YADIS_HEADER_NAME, YADIS_CONTENT_TYPE, YADIS_ACCEPT_HEADER -from askbot.deps.openid.yadis.parsehtml import MetaNotFound, findHTMLMeta - -class DiscoveryFailure(Exception): - """Raised when a YADIS protocol error occurs in the discovery process""" - identity_url = None - - def __init__(self, message, http_response): - Exception.__init__(self, message) - self.http_response = http_response - -class DiscoveryResult(object): - """Contains the result of performing Yadis discovery on a URI""" - - # The URI that was passed to the fetcher - request_uri = None - - # The result of following redirects from the request_uri - normalized_uri = None - - # The URI from which the response text was returned (set to - # None if there was no XRDS document found) - xrds_uri = None - - # The content-type returned with the response_text - content_type = None - - # The document returned from the xrds_uri - response_text = None - - def __init__(self, request_uri): - """Initialize the state of the object - - sets all attributes to None except the request_uri - """ - self.request_uri = request_uri - - def usedYadisLocation(self): - """Was the Yadis protocol's indirection used?""" - return self.normalized_uri != self.xrds_uri - - def isXRDS(self): - """Is the response text supposed to be an XRDS document?""" - return (self.usedYadisLocation() or - self.content_type == YADIS_CONTENT_TYPE) - -def discover(uri): - """Discover services for a given URI. - - @param uri: The identity URI as a well-formed http or https - URI. The well-formedness and the protocol are not checked, but - the results of this function are undefined if those properties - do not hold. - - @return: DiscoveryResult object - - @raises Exception: Any exception that can be raised by fetching a URL with - the given fetcher. - @raises DiscoveryFailure: When the HTTP response does not have a 200 code. - """ - result = DiscoveryResult(uri) - resp = fetchers.fetch(uri, headers={'Accept': YADIS_ACCEPT_HEADER}) - if resp.status not in (200, 206): - raise DiscoveryFailure( - 'HTTP Response status from identity URL host is not 200. ' - 'Got status %r' % (resp.status,), resp) - - # Note the URL after following redirects - result.normalized_uri = resp.final_url - - # Attempt to find out where to go to discover the document - # or if we already have it - result.content_type = resp.headers.get('content-type') - - result.xrds_uri = whereIsYadis(resp) - - if result.xrds_uri and result.usedYadisLocation(): - resp = fetchers.fetch(result.xrds_uri) - if resp.status not in (200, 206): - exc = DiscoveryFailure( - 'HTTP Response status from Yadis host is not 200. ' - 'Got status %r' % (resp.status,), resp) - exc.identity_url = result.normalized_uri - raise exc - result.content_type = resp.headers.get('content-type') - - result.response_text = resp.body - return result - - - -def whereIsYadis(resp): - """Given a HTTPResponse, return the location of the Yadis document. - - May be the URL just retrieved, another URL, or None, if I can't - find any. - - [non-blocking] - - @returns: str or None - """ - # Attempt to find out where to go to discover the document - # or if we already have it - content_type = resp.headers.get('content-type') - - # According to the spec, the content-type header must be an exact - # match, or else we have to look for an indirection. - if (content_type and - content_type.split(';', 1)[0].lower() == YADIS_CONTENT_TYPE): - return resp.final_url - else: - # Try the header - yadis_loc = resp.headers.get(YADIS_HEADER_NAME.lower()) - - if not yadis_loc: - # Parse as HTML if the header is missing. - # - # XXX: do we want to do something with content-type, like - # have a whitelist or a blacklist (for detecting that it's - # HTML)? - try: - yadis_loc = findHTMLMeta(StringIO(resp.body)) - except MetaNotFound: - pass - - return yadis_loc - diff --git a/askbot/deps/openid/yadis/etxrd.py b/askbot/deps/openid/yadis/etxrd.py deleted file mode 100644 index 7c921389..00000000 --- a/askbot/deps/openid/yadis/etxrd.py +++ /dev/null @@ -1,300 +0,0 @@ -# -*- test-case-name: yadis.test.test_etxrd -*- -""" -ElementTree interface to an XRD document. -""" - -__all__ = [ - 'nsTag', - 'mkXRDTag', - 'isXRDS', - 'parseXRDS', - 'getCanonicalID', - 'getYadisXRD', - 'getPriorityStrict', - 'getPriority', - 'prioSort', - 'iterServices', - 'expandService', - 'expandServices', - ] - -import sys -import random - -from datetime import datetime -from time import strptime - -from askbot.deps.openid.oidutil import importElementTree -ElementTree = importElementTree() - -# the different elementtree modules don't have a common exception -# model. We just want to be able to catch the exceptions that signify -# malformed XML data and wrap them, so that the other library code -# doesn't have to know which XML library we're using. -try: - # Make the parser raise an exception so we can sniff out the type - # of exceptions - ElementTree.XML('> purposely malformed XML <') -except (SystemExit, MemoryError, AssertionError, ImportError): - raise -except: - XMLError = sys.exc_info()[0] - -from askbot.deps.openid.yadis import xri - -class XRDSError(Exception): - """An error with the XRDS document.""" - - # The exception that triggered this exception - reason = None - - - -class XRDSFraud(XRDSError): - """Raised when there's an assertion in the XRDS that it does not have - the authority to make. - """ - - - -def parseXRDS(text): - """Parse the given text as an XRDS document. - - @return: ElementTree containing an XRDS document - - @raises XRDSError: When there is a parse error or the document does - not contain an XRDS. - """ - try: - element = ElementTree.XML(text) - except XMLError, why: - exc = XRDSError('Error parsing document as XML') - exc.reason = why - raise exc - else: - tree = ElementTree.ElementTree(element) - if not isXRDS(tree): - raise XRDSError('Not an XRDS document') - - return tree - -XRD_NS_2_0 = 'xri://$xrd*($v*2.0)' -XRDS_NS = 'xri://$xrds' - -def nsTag(ns, t): - return '{%s}%s' % (ns, t) - -def mkXRDTag(t): - """basestring -> basestring - - Create a tag name in the XRD 2.0 XML namespace suitable for using - with ElementTree - """ - return nsTag(XRD_NS_2_0, t) - -def mkXRDSTag(t): - """basestring -> basestring - - Create a tag name in the XRDS XML namespace suitable for using - with ElementTree - """ - return nsTag(XRDS_NS, t) - -# Tags that are used in Yadis documents -root_tag = mkXRDSTag('XRDS') -service_tag = mkXRDTag('Service') -xrd_tag = mkXRDTag('XRD') -type_tag = mkXRDTag('Type') -uri_tag = mkXRDTag('URI') -expires_tag = mkXRDTag('Expires') - -# Other XRD tags -canonicalID_tag = mkXRDTag('CanonicalID') - -def isXRDS(xrd_tree): - """Is this document an XRDS document?""" - root = xrd_tree.getroot() - return root.tag == root_tag - -def getYadisXRD(xrd_tree): - """Return the XRD element that should contain the Yadis services""" - xrd = None - - # for the side-effect of assigning the last one in the list to the - # xrd variable - for xrd in xrd_tree.findall(xrd_tag): - pass - - # There were no elements found, or else xrd would be set to the - # last one - if xrd is None: - raise XRDSError('No XRD present in tree') - - return xrd - -def getXRDExpiration(xrd_element, default=None): - """Return the expiration date of this XRD element, or None if no - expiration was specified. - - @type xrd_element: ElementTree node - - @param default: The value to use as the expiration if no - expiration was specified in the XRD. - - @rtype: datetime.datetime - - @raises ValueError: If the xrd:Expires element is present, but its - contents are not formatted according to the specification. - """ - expires_element = xrd_element.find(expires_tag) - if expires_element is None: - return default - else: - expires_string = expires_element.text - - # Will raise ValueError if the string is not the expected format - expires_time = strptime(expires_string, "%Y-%m-%dT%H:%M:%SZ") - return datetime(*expires_time[0:6]) - -def getCanonicalID(iname, xrd_tree): - """Return the CanonicalID from this XRDS document. - - @param iname: the XRI being resolved. - @type iname: unicode - - @param xrd_tree: The XRDS output from the resolver. - @type xrd_tree: ElementTree - - @returns: The XRI CanonicalID or None. - @returntype: unicode or None - """ - xrd_list = xrd_tree.findall(xrd_tag) - xrd_list.reverse() - - try: - canonicalID = xri.XRI(xrd_list[0].findall(canonicalID_tag)[0].text) - except IndexError: - return None - - childID = canonicalID.lower() - - for xrd in xrd_list[1:]: - # XXX: can't use rsplit until we require python >= 2.4. - parent_sought = childID[:childID.rindex('!')] - parent = xri.XRI(xrd.findtext(canonicalID_tag)) - if parent_sought != parent.lower(): - raise XRDSFraud("%r can not come from %s" % (childID, parent)) - - childID = parent_sought - - root = xri.rootAuthority(iname) - if not xri.providerIsAuthoritative(root, childID): - raise XRDSFraud("%r can not come from root %r" % (childID, root)) - - return canonicalID - - - -class _Max(object): - """Value that compares greater than any other value. - - Should only be used as a singleton. Implemented for use as a - priority value for when a priority is not specified.""" - def __cmp__(self, other): - if other is self: - return 0 - - return 1 - -Max = _Max() - -def getPriorityStrict(element): - """Get the priority of this element. - - Raises ValueError if the value of the priority is invalid. If no - priority is specified, it returns a value that compares greater - than any other value. - """ - prio_str = element.get('priority') - if prio_str is not None: - prio_val = int(prio_str) - if prio_val >= 0: - return prio_val - else: - raise ValueError('Priority values must be non-negative integers') - - # Any errors in parsing the priority fall through to here - return Max - -def getPriority(element): - """Get the priority of this element - - Returns Max if no priority is specified or the priority value is invalid. - """ - try: - return getPriorityStrict(element) - except ValueError: - return Max - -def prioSort(elements): - """Sort a list of elements that have priority attributes""" - # Randomize the services before sorting so that equal priority - # elements are load-balanced. - random.shuffle(elements) - - prio_elems = [(getPriority(e), e) for e in elements] - prio_elems.sort() - sorted_elems = [s for (_, s) in prio_elems] - return sorted_elems - -def iterServices(xrd_tree): - """Return an iterable over the Service elements in the Yadis XRD - - sorted by priority""" - xrd = getYadisXRD(xrd_tree) - return prioSort(xrd.findall(service_tag)) - -def sortedURIs(service_element): - """Given a Service element, return a list of the contents of all - URI tags in priority order.""" - return [uri_element.text for uri_element - in prioSort(service_element.findall(uri_tag))] - -def getTypeURIs(service_element): - """Given a Service element, return a list of the contents of all - Type tags""" - return [type_element.text for type_element - in service_element.findall(type_tag)] - -def expandService(service_element): - """Take a service element and expand it into an iterator of: - ([type_uri], uri, service_element) - """ - uris = sortedURIs(service_element) - if not uris: - uris = [None] - - expanded = [] - for uri in uris: - type_uris = getTypeURIs(service_element) - expanded.append((type_uris, uri, service_element)) - - return expanded - -def expandServices(service_elements): - """Take a sorted iterator of service elements and expand it into a - sorted iterator of: - ([type_uri], uri, service_element) - - There may be more than one item in the resulting list for each - service element if there is more than one URI or type for a - service, but each triple will be unique. - - If there is no URI or Type for a Service element, it will not - appear in the result. - """ - expanded = [] - for service_element in service_elements: - expanded.extend(expandService(service_element)) - - return expanded diff --git a/askbot/deps/openid/yadis/filters.py b/askbot/deps/openid/yadis/filters.py deleted file mode 100644 index 0008fd2f..00000000 --- a/askbot/deps/openid/yadis/filters.py +++ /dev/null @@ -1,200 +0,0 @@ -"""This module contains functions and classes used for extracting -endpoint information out of a Yadis XRD file using the ElementTree XML -parser. -""" - -__all__ = [ - 'BasicServiceEndpoint', - 'mkFilter', - 'IFilter', - 'TransformFilterMaker', - 'CompoundFilter', - ] - -from askbot.deps.openid.yadis.etxrd import expandService - -class BasicServiceEndpoint(object): - """Generic endpoint object that contains parsed service - information, as well as a reference to the service element from - which it was generated. If there is more than one xrd:Type or - xrd:URI in the xrd:Service, this object represents just one of - those pairs. - - This object can be used as a filter, because it implements - fromBasicServiceEndpoint. - - The simplest kind of filter you can write implements - fromBasicServiceEndpoint, which takes one of these objects. - """ - def __init__(self, yadis_url, type_uris, uri, service_element): - self.type_uris = type_uris - self.yadis_url = yadis_url - self.uri = uri - self.service_element = service_element - - def matchTypes(self, type_uris): - """Query this endpoint to see if it has any of the given type - URIs. This is useful for implementing other endpoint classes - that e.g. need to check for the presence of multiple versions - of a single protocol. - - @param type_uris: The URIs that you wish to check - @type type_uris: iterable of str - - @return: all types that are in both in type_uris and - self.type_uris - """ - return [uri for uri in type_uris if uri in self.type_uris] - - def fromBasicServiceEndpoint(endpoint): - """Trivial transform from a basic endpoint to itself. This - method exists to allow BasicServiceEndpoint to be used as a - filter. - - If you are subclassing this object, re-implement this function. - - @param endpoint: An instance of BasicServiceEndpoint - @return: The object that was passed in, with no processing. - """ - return endpoint - - fromBasicServiceEndpoint = staticmethod(fromBasicServiceEndpoint) - -class IFilter(object): - """Interface for Yadis filter objects. Other filter-like things - are convertable to this class.""" - - def getServiceEndpoints(self, yadis_url, service_element): - """Returns an iterator of endpoint objects""" - raise NotImplementedError - -class TransformFilterMaker(object): - """Take a list of basic filters and makes a filter that transforms - the basic filter into a top-level filter. This is mostly useful - for the implementation of mkFilter, which should only be needed - for special cases or internal use by this library. - - This object is useful for creating simple filters for services - that use one URI and are specified by one Type (we expect most - Types will fit this paradigm). - - Creates a BasicServiceEndpoint object and apply the filter - functions to it until one of them returns a value. - """ - - def __init__(self, filter_functions): - """Initialize the filter maker's state - - @param filter_functions: The endpoint transformer functions to - apply to the basic endpoint. These are called in turn - until one of them does not return None, and the result of - that transformer is returned. - """ - self.filter_functions = filter_functions - - def getServiceEndpoints(self, yadis_url, service_element): - """Returns an iterator of endpoint objects produced by the - filter functions.""" - endpoints = [] - - # Do an expansion of the service element by xrd:Type and xrd:URI - for type_uris, uri, _ in expandService(service_element): - - # Create a basic endpoint object to represent this - # yadis_url, Service, Type, URI combination - endpoint = BasicServiceEndpoint( - yadis_url, type_uris, uri, service_element) - - e = self.applyFilters(endpoint) - if e is not None: - endpoints.append(e) - - return endpoints - - def applyFilters(self, endpoint): - """Apply filter functions to an endpoint until one of them - returns non-None.""" - for filter_function in self.filter_functions: - e = filter_function(endpoint) - if e is not None: - # Once one of the filters has returned an - # endpoint, do not apply any more. - return e - - return None - -class CompoundFilter(object): - """Create a new filter that applies a set of filters to an endpoint - and collects their results. - """ - def __init__(self, subfilters): - self.subfilters = subfilters - - def getServiceEndpoints(self, yadis_url, service_element): - """Generate all endpoint objects for all of the subfilters of - this filter and return their concatenation.""" - endpoints = [] - for subfilter in self.subfilters: - endpoints.extend( - subfilter.getServiceEndpoints(yadis_url, service_element)) - return endpoints - -# Exception raised when something is not able to be turned into a filter -filter_type_error = TypeError( - 'Expected a filter, an endpoint, a callable or a list of any of these.') - -def mkFilter(parts): - """Convert a filter-convertable thing into a filter - - @param parts: a filter, an endpoint, a callable, or a list of any of these. - """ - # Convert the parts into a list, and pass to mkCompoundFilter - if parts is None: - parts = [BasicServiceEndpoint] - - try: - parts = list(parts) - except TypeError: - return mkCompoundFilter([parts]) - else: - return mkCompoundFilter(parts) - -def mkCompoundFilter(parts): - """Create a filter out of a list of filter-like things - - Used by mkFilter - - @param parts: list of filter, endpoint, callable or list of any of these - """ - # Separate into a list of callables and a list of filter objects - transformers = [] - filters = [] - for subfilter in parts: - try: - subfilter = list(subfilter) - except TypeError: - # If it's not an iterable - if hasattr(subfilter, 'getServiceEndpoints'): - # It's a full filter - filters.append(subfilter) - elif hasattr(subfilter, 'fromBasicServiceEndpoint'): - # It's an endpoint object, so put its endpoint - # conversion attribute into the list of endpoint - # transformers - transformers.append(subfilter.fromBasicServiceEndpoint) - elif callable(subfilter): - # It's a simple callable, so add it to the list of - # endpoint transformers - transformers.append(subfilter) - else: - raise filter_type_error - else: - filters.append(mkCompoundFilter(subfilter)) - - if transformers: - filters.append(TransformFilterMaker(transformers)) - - if len(filters) == 1: - return filters[0] - else: - return CompoundFilter(filters) diff --git a/askbot/deps/openid/yadis/manager.py b/askbot/deps/openid/yadis/manager.py deleted file mode 100644 index 709adb7d..00000000 --- a/askbot/deps/openid/yadis/manager.py +++ /dev/null @@ -1,194 +0,0 @@ -class YadisServiceManager(object): - """Holds the state of a list of selected Yadis services, managing - storing it in a session and iterating over the services in order.""" - - def __init__(self, starting_url, yadis_url, services, session_key): - # The URL that was used to initiate the Yadis protocol - self.starting_url = starting_url - - # The URL after following redirects (the identifier) - self.yadis_url = yadis_url - - # List of service elements - self.services = list(services) - - self.session_key = session_key - - # Reference to the current service object - self._current = None - - def __len__(self): - """How many untried services remain?""" - return len(self.services) - - def __iter__(self): - return self - - def next(self): - """Return the next service - - self.current() will continue to return that service until the - next call to this method.""" - try: - self._current = self.services.pop(0) - except IndexError: - raise StopIteration - else: - return self._current - - def current(self): - """Return the current service. - - Returns None if there are no services left. - """ - return self._current - - def forURL(self, url): - return url in [self.starting_url, self.yadis_url] - - def started(self): - """Has the first service been returned?""" - return self._current is not None - - def store(self, session): - """Store this object in the session, by its session key.""" - session[self.session_key] = self - -class Discovery(object): - """State management for discovery. - - High-level usage pattern is to call .getNextService(discover) in - order to find the next available service for this user for this - session. Once a request completes, call .finish() to clean up the - session state. - - @ivar session: a dict-like object that stores state unique to the - requesting user-agent. This object must be able to store - serializable objects. - - @ivar url: the URL that is used to make the discovery request - - @ivar session_key_suffix: The suffix that will be used to identify - this object in the session object. - """ - - DEFAULT_SUFFIX = 'auth' - PREFIX = '_yadis_services_' - - def __init__(self, session, url, session_key_suffix=None): - """Initialize a discovery object""" - self.session = session - self.url = url - if session_key_suffix is None: - session_key_suffix = self.DEFAULT_SUFFIX - - self.session_key_suffix = session_key_suffix - - def getNextService(self, discover): - """Return the next authentication service for the pair of - user_input and session. This function handles fallback. - - - @param discover: a callable that takes a URL and returns a - list of services - - @type discover: str -> [service] - - - @return: the next available service - """ - manager = self.getManager() - if manager is not None and not manager: - self.destroyManager() - - if not manager: - yadis_url, services = discover(self.url) - manager = self.createManager(services, yadis_url) - - if manager: - service = manager.next() - manager.store(self.session) - else: - service = None - - return service - - def cleanup(self, force=False): - """Clean up Yadis-related services in the session and return - the most-recently-attempted service from the manager, if one - exists. - - @param force: True if the manager should be deleted regardless - of whether it's a manager for self.url. - - @return: current service endpoint object or None if there is - no current service - """ - manager = self.getManager(force=force) - if manager is not None: - service = manager.current() - self.destroyManager(force=force) - else: - service = None - - return service - - ### Lower-level methods - - def getSessionKey(self): - """Get the session key for this starting URL and suffix - - @return: The session key - @rtype: str - """ - return self.PREFIX + self.session_key_suffix - - def getManager(self, force=False): - """Extract the YadisServiceManager for this object's URL and - suffix from the session. - - @param force: True if the manager should be returned - regardless of whether it's a manager for self.url. - - @return: The current YadisServiceManager, if it's for this - URL, or else None - """ - manager = self.session.get(self.getSessionKey()) - if (manager is not None and (manager.forURL(self.url) or force)): - return manager - else: - return None - - def createManager(self, services, yadis_url=None): - """Create a new YadisService Manager for this starting URL and - suffix, and store it in the session. - - @raises KeyError: When I already have a manager. - - @return: A new YadisServiceManager or None - """ - key = self.getSessionKey() - if self.getManager(): - raise KeyError('There is already a %r manager for %r' % - (key, self.url)) - - if not services: - return None - - manager = YadisServiceManager(self.url, yadis_url, services, key) - manager.store(self.session) - return manager - - def destroyManager(self, force=False): - """Delete any YadisServiceManager with this starting URL and - suffix from the session. - - If there is no service manager or the service manager is for a - different URL, it silently does nothing. - - @param force: True if the manager should be deleted regardless - of whether it's a manager for self.url. - """ - if self.getManager(force=force) is not None: - key = self.getSessionKey() - del self.session[key] diff --git a/askbot/deps/openid/yadis/parsehtml.py b/askbot/deps/openid/yadis/parsehtml.py deleted file mode 100644 index 948fef86..00000000 --- a/askbot/deps/openid/yadis/parsehtml.py +++ /dev/null @@ -1,197 +0,0 @@ -__all__ = ['findHTMLMeta', 'MetaNotFound'] - -from HTMLParser import HTMLParser, HTMLParseError -import htmlentitydefs -import re - -from askbot.deps.openid.yadis.constants import YADIS_HEADER_NAME - -# Size of the chunks to search at a time (also the amount that gets -# read at a time) -CHUNK_SIZE = 1024 * 16 # 16 KB - -class ParseDone(Exception): - """Exception to hold the URI that was located when the parse is - finished. If the parse finishes without finding the URI, set it to - None.""" - -class MetaNotFound(Exception): - """Exception to hold the content of the page if we did not find - the appropriate <meta> tag""" - -re_flags = re.IGNORECASE | re.UNICODE | re.VERBOSE -ent_pat = r''' -& - -(?: \#x (?P<hex> [a-f0-9]+ ) -| \# (?P<dec> \d+ ) -| (?P<word> \w+ ) -) - -;''' - -ent_re = re.compile(ent_pat, re_flags) - -def substituteMO(mo): - if mo.lastgroup == 'hex': - codepoint = int(mo.group('hex'), 16) - elif mo.lastgroup == 'dec': - codepoint = int(mo.group('dec')) - else: - assert mo.lastgroup == 'word' - codepoint = htmlentitydefs.name2codepoint.get(mo.group('word')) - - if codepoint is None: - return mo.group() - else: - return unichr(codepoint) - -def substituteEntities(s): - return ent_re.sub(substituteMO, s) - -class YadisHTMLParser(HTMLParser): - """Parser that finds a meta http-equiv tag in the head of a html - document. - - When feeding in data, if the tag is matched or it will never be - found, the parser will raise ParseDone with the uri as the first - attribute. - - Parsing state diagram - ===================== - - Any unlisted input does not affect the state:: - - 1, 2, 5 8 - +--------------------------+ +-+ - | | | | - 4 | 3 1, 2, 5, 7 v | v - TOP -> HTML -> HEAD ----------> TERMINATED - | | ^ | ^ ^ - | | 3 | | | | - | +------------+ +-> FOUND ------+ | - | 6 8 | - | 1, 2 | - +------------------------------------+ - - 1. any of </body>, </html>, </head> -> TERMINATE - 2. <body> -> TERMINATE - 3. <head> -> HEAD - 4. <html> -> HTML - 5. <html> -> TERMINATE - 6. <meta http-equiv='X-XRDS-Location'> -> FOUND - 7. <head> -> TERMINATE - 8. Any input -> TERMINATE - """ - TOP = 0 - HTML = 1 - HEAD = 2 - FOUND = 3 - TERMINATED = 4 - - def __init__(self): - HTMLParser.__init__(self) - self.phase = self.TOP - - def _terminate(self): - self.phase = self.TERMINATED - raise ParseDone(None) - - def handle_endtag(self, tag): - # If we ever see an end of head, body, or html, bail out right away. - # [1] - if tag in ['head', 'body', 'html']: - self._terminate() - - def handle_starttag(self, tag, attrs): - # if we ever see a start body tag, bail out right away, since - # we want to prevent the meta tag from appearing in the body - # [2] - if tag=='body': - self._terminate() - - if self.phase == self.TOP: - # At the top level, allow a html tag or a head tag to move - # to the head or html phase - if tag == 'head': - # [3] - self.phase = self.HEAD - elif tag == 'html': - # [4] - self.phase = self.HTML - - elif self.phase == self.HTML: - # if we are in the html tag, allow a head tag to move to - # the HEAD phase. If we get another html tag, then bail - # out - if tag == 'head': - # [3] - self.phase = self.HEAD - elif tag == 'html': - # [5] - self._terminate() - - elif self.phase == self.HEAD: - # If we are in the head phase, look for the appropriate - # meta tag. If we get a head or body tag, bail out. - if tag == 'meta': - attrs_d = dict(attrs) - http_equiv = attrs_d.get('http-equiv', '').lower() - if http_equiv == YADIS_HEADER_NAME.lower(): - raw_attr = attrs_d.get('content') - yadis_loc = substituteEntities(raw_attr) - # [6] - self.phase = self.FOUND - raise ParseDone(yadis_loc) - - elif tag in ['head', 'html']: - # [5], [7] - self._terminate() - - def feed(self, chars): - # [8] - if self.phase in [self.TERMINATED, self.FOUND]: - self._terminate() - - return HTMLParser.feed(self, chars) - -def findHTMLMeta(stream): - """Look for a meta http-equiv tag with the YADIS header name. - - @param stream: Source of the html text - @type stream: Object that implements a read() method that works - like file.read - - @return: The URI from which to fetch the XRDS document - @rtype: str - - @raises MetaNotFound: raised with the content that was - searched as the first parameter. - """ - parser = YadisHTMLParser() - chunks = [] - - while 1: - chunk = stream.read(CHUNK_SIZE) - if not chunk: - # End of file - break - - chunks.append(chunk) - try: - parser.feed(chunk) - except HTMLParseError, why: - # HTML parse error, so bail - chunks.append(stream.read()) - break - except ParseDone, why: - uri = why[0] - if uri is None: - # Parse finished, but we may need the rest of the file - chunks.append(stream.read()) - break - else: - return uri - - content = ''.join(chunks) - raise MetaNotFound(content) diff --git a/askbot/deps/openid/yadis/services.py b/askbot/deps/openid/yadis/services.py deleted file mode 100644 index dad230c1..00000000 --- a/askbot/deps/openid/yadis/services.py +++ /dev/null @@ -1,54 +0,0 @@ -# -*- test-case-name: openid.test.test_services -*- - -from askbot.deps.openid.yadis.filters import mkFilter -from askbot.deps.openid.yadis.discover import discover, DiscoveryFailure -from askbot.deps.openid.yadis.etxrd import parseXRDS, iterServices, XRDSError - -def getServiceEndpoints(input_url, flt=None): - """Perform the Yadis protocol on the input URL and return an - iterable of resulting endpoint objects. - - @param flt: A filter object or something that is convertable to - a filter object (using mkFilter) that will be used to generate - endpoint objects. This defaults to generating BasicEndpoint - objects. - - @param input_url: The URL on which to perform the Yadis protocol - - @return: The normalized identity URL and an iterable of endpoint - objects generated by the filter function. - - @rtype: (str, [endpoint]) - - @raises DiscoveryFailure: when Yadis fails to obtain an XRDS document. - """ - result = discover(input_url) - try: - endpoints = applyFilter(result.normalized_uri, - result.response_text, flt) - except XRDSError, err: - raise DiscoveryFailure(str(err), None) - return (result.normalized_uri, endpoints) - -def applyFilter(normalized_uri, xrd_data, flt=None): - """Generate an iterable of endpoint objects given this input data, - presumably from the result of performing the Yadis protocol. - - @param normalized_uri: The input URL, after following redirects, - as in the Yadis protocol. - - - @param xrd_data: The XML text the XRDS file fetched from the - normalized URI. - @type xrd_data: str - - """ - flt = mkFilter(flt) - et = parseXRDS(xrd_data) - - endpoints = [] - for service_element in iterServices(et): - endpoints.extend( - flt.getServiceEndpoints(normalized_uri, service_element)) - - return endpoints diff --git a/askbot/deps/openid/yadis/xri.py b/askbot/deps/openid/yadis/xri.py deleted file mode 100644 index 3a39a6b8..00000000 --- a/askbot/deps/openid/yadis/xri.py +++ /dev/null @@ -1,168 +0,0 @@ -# -*- test-case-name: openid.test.test_xri -*- -"""Utility functions for handling XRIs. - -@see: XRI Syntax v2.0 at the U{OASIS XRI Technical Committee<http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=xri>} -""" - -import re - -XRI_AUTHORITIES = ['!', '=', '@', '+', '$', '('] - -try: - unichr(0x10000) -except ValueError: - # narrow python build - UCSCHAR = [ - (0xA0, 0xD7FF), - (0xF900, 0xFDCF), - (0xFDF0, 0xFFEF), - ] - - IPRIVATE = [ - (0xE000, 0xF8FF), - ] -else: - UCSCHAR = [ - (0xA0, 0xD7FF), - (0xF900, 0xFDCF), - (0xFDF0, 0xFFEF), - (0x10000, 0x1FFFD), - (0x20000, 0x2FFFD), - (0x30000, 0x3FFFD), - (0x40000, 0x4FFFD), - (0x50000, 0x5FFFD), - (0x60000, 0x6FFFD), - (0x70000, 0x7FFFD), - (0x80000, 0x8FFFD), - (0x90000, 0x9FFFD), - (0xA0000, 0xAFFFD), - (0xB0000, 0xBFFFD), - (0xC0000, 0xCFFFD), - (0xD0000, 0xDFFFD), - (0xE1000, 0xEFFFD), - ] - - IPRIVATE = [ - (0xE000, 0xF8FF), - (0xF0000, 0xFFFFD), - (0x100000, 0x10FFFD), - ] - - -_escapeme_re = re.compile('[%s]' % (''.join( - map(lambda (m, n): u'%s-%s' % (unichr(m), unichr(n)), - UCSCHAR + IPRIVATE)),)) - - -def identifierScheme(identifier): - """Determine if this identifier is an XRI or URI. - - @returns: C{"XRI"} or C{"URI"} - """ - if identifier.startswith('xri://') or ( - identifier and identifier[0] in XRI_AUTHORITIES): - return "XRI" - else: - return "URI" - - -def toIRINormal(xri): - """Transform an XRI to IRI-normal form.""" - if not xri.startswith('xri://'): - xri = 'xri://' + xri - return escapeForIRI(xri) - - -_xref_re = re.compile('\((.*?)\)') - - -def _escape_xref(xref_match): - """Escape things that need to be escaped if they're in a cross-reference. - """ - xref = xref_match.group() - xref = xref.replace('/', '%2F') - xref = xref.replace('?', '%3F') - xref = xref.replace('#', '%23') - return xref - - -def escapeForIRI(xri): - """Escape things that need to be escaped when transforming to an IRI.""" - xri = xri.replace('%', '%25') - xri = _xref_re.sub(_escape_xref, xri) - return xri - - -def toURINormal(xri): - """Transform an XRI to URI normal form.""" - return iriToURI(toIRINormal(xri)) - - -def _percentEscapeUnicode(char_match): - c = char_match.group() - return ''.join(['%%%X' % (ord(octet),) for octet in c.encode('utf-8')]) - - -def iriToURI(iri): - """Transform an IRI to a URI by escaping unicode.""" - # According to RFC 3987, section 3.1, "Mapping of IRIs to URIs" - return _escapeme_re.sub(_percentEscapeUnicode, iri) - - -def providerIsAuthoritative(providerID, canonicalID): - """Is this provider ID authoritative for this XRI? - - @returntype: bool - """ - # XXX: can't use rsplit until we require python >= 2.4. - lastbang = canonicalID.rindex('!') - parent = canonicalID[:lastbang] - return parent == providerID - - -def rootAuthority(xri): - """Return the root authority for an XRI. - - Example:: - - rootAuthority("xri://@example") == "xri://@" - - @type xri: unicode - @returntype: unicode - """ - if xri.startswith('xri://'): - xri = xri[6:] - authority = xri.split('/', 1)[0] - if authority[0] == '(': - # Cross-reference. - # XXX: This is incorrect if someone nests cross-references so there - # is another close-paren in there. Hopefully nobody does that - # before we have a real xriparse function. Hopefully nobody does - # that *ever*. - root = authority[:authority.index(')') + 1] - elif authority[0] in XRI_AUTHORITIES: - # Other XRI reference. - root = authority[0] - else: - # IRI reference. XXX: Can IRI authorities have segments? - segments = authority.split('!') - segments = reduce(list.__add__, - map(lambda s: s.split('*'), segments)) - root = segments[0] - - return XRI(root) - - -def XRI(xri): - """An XRI object allowing comparison of XRI. - - Ideally, this would do full normalization and provide comparsion - operators as per XRI Syntax. Right now, it just does a bit of - canonicalization by ensuring the xri scheme is present. - - @param xri: an xri string - @type xri: unicode - """ - if not xri.startswith('xri://'): - xri = 'xri://' + xri - return xri diff --git a/askbot/deps/openid/yadis/xrires.py b/askbot/deps/openid/yadis/xrires.py deleted file mode 100644 index 33cc6d65..00000000 --- a/askbot/deps/openid/yadis/xrires.py +++ /dev/null @@ -1,123 +0,0 @@ -# -*- test-case-name: openid.test.test_xrires -*- -"""XRI resolution. -""" - -from urllib import urlencode -from askbot.deps.openid import fetchers -from askbot.deps.openid.yadis import etxrd -from askbot.deps.openid.yadis.xri import toURINormal -from askbot.deps.openid.yadis.services import iterServices - -DEFAULT_PROXY = 'http://proxy.xri.net/' - -class ProxyResolver(object): - """Python interface to a remote XRI proxy resolver. - """ - def __init__(self, proxy_url=DEFAULT_PROXY): - self.proxy_url = proxy_url - - - def queryURL(self, xri, service_type=None): - """Build a URL to query the proxy resolver. - - @param xri: An XRI to resolve. - @type xri: unicode - - @param service_type: The service type to resolve, if you desire - service endpoint selection. A service type is a URI. - @type service_type: str - - @returns: a URL - @returntype: str - """ - # Trim off the xri:// prefix. The proxy resolver didn't accept it - # when this code was written, but that may (or may not) change for - # XRI Resolution 2.0 Working Draft 11. - qxri = toURINormal(xri)[6:] - hxri = self.proxy_url + qxri - args = { - # XXX: If the proxy resolver will ensure that it doesn't return - # bogus CanonicalIDs (as per Steve's message of 15 Aug 2006 - # 11:13:42), then we could ask for application/xrd+xml instead, - # which would give us a bit less to process. - '_xrd_r': 'application/xrds+xml', - } - if service_type: - args['_xrd_t'] = service_type - else: - # Don't perform service endpoint selection. - args['_xrd_r'] += ';sep=false' - query = _appendArgs(hxri, args) - return query - - - def query(self, xri, service_types): - """Resolve some services for an XRI. - - Note: I don't implement any service endpoint selection beyond what - the resolver I'm querying does, so the Services I return may well - include Services that were not of the types you asked for. - - May raise fetchers.HTTPFetchingError or L{etxrd.XRDSError} if - the fetching or parsing don't go so well. - - @param xri: An XRI to resolve. - @type xri: unicode - - @param service_types: A list of services types to query for. Service - types are URIs. - @type service_types: list of str - - @returns: tuple of (CanonicalID, Service elements) - @returntype: (unicode, list of C{ElementTree.Element}s) - """ - # FIXME: No test coverage! - services = [] - # Make a seperate request to the proxy resolver for each service - # type, as, if it is following Refs, it could return a different - # XRDS for each. - - canonicalID = None - - for service_type in service_types: - url = self.queryURL(xri, service_type) - response = fetchers.fetch(url) - if response.status not in (200, 206): - # XXX: sucks to fail silently. - # print "response not OK:", response - continue - et = etxrd.parseXRDS(response.body) - canonicalID = etxrd.getCanonicalID(xri, et) - some_services = list(iterServices(et)) - services.extend(some_services) - # TODO: - # * If we do get hits for multiple service_types, we're almost - # certainly going to have duplicated service entries and - # broken priority ordering. - return canonicalID, services - - -def _appendArgs(url, args): - """Append some arguments to an HTTP query. - """ - # to be merged with oidutil.appendArgs when we combine the projects. - if hasattr(args, 'items'): - args = args.items() - args.sort() - - if len(args) == 0: - return url - - # According to XRI Resolution section "QXRI query parameters": - # - # """If the original QXRI had a null query component (only a leading - # question mark), or a query component consisting of only question - # marks, one additional leading question mark MUST be added when - # adding any XRI resolution parameters.""" - - if '?' in url.rstrip('?'): - sep = '&' - else: - sep = '?' - - return '%s%s%s' % (url, sep, urlencode(args)) diff --git a/askbot/deps/recaptcha_django/__init__.py b/askbot/deps/recaptcha_django/__init__.py index 518c214b..bf4c90dc 100644 --- a/askbot/deps/recaptcha_django/__init__.py +++ b/askbot/deps/recaptcha_django/__init__.py @@ -33,11 +33,15 @@ class ReCaptchaWidget(Widget): """ options = ['theme', 'lang', 'custom_theme_widget', 'tabindex'] + def __init__(self, public_key = None, *args, **kwargs): + self.public_key = public_key + super(ReCaptchaWidget, self).__init__(*args, **kwargs) + def render(self, name, value, attrs=None): final_attrs = self.build_attrs(attrs) error = final_attrs.get('error', None) html = captcha.displayhtml( - askbot_settings.RECAPTCHA_KEY, + self.public_key, error=error ) options = u',\n'.join([u'%s: "%s"' % (k, conditional_escape(v)) \ @@ -66,7 +70,11 @@ class ReCaptchaField(Field): """ Field definition for a ReCAPTCHA """ - widget = ReCaptchaWidget + + def __init__(self, private_key = None, public_key = None, *args, **kwargs): + self.widget = ReCaptchaWidget(public_key = public_key) + self.private_key = private_key + super(ReCaptchaField, self).__init__(*args, **kwargs) def clean(self, value): if value is None: diff --git a/askbot/forms.py b/askbot/forms.py index 57775ed0..446cbf83 100644 --- a/askbot/forms.py +++ b/askbot/forms.py @@ -526,7 +526,10 @@ class AdvancedSearchForm(forms.Form): return data class NotARobotForm(forms.Form): - recaptcha = ReCaptchaField() + recaptcha = ReCaptchaField( + private_key = askbot_settings.RECAPTCHA_SECRET, + public_key = askbot_settings.RECAPTCHA_KEY + ) class FeedbackForm(forms.Form): name = forms.CharField(label=_('Your name:'), required=False) diff --git a/askbot/skins/default/media/js/yuicompressor-2.4.2.jar b/askbot/skins/default/media/js/yuicompressor-2.4.2.jar Binary files differdeleted file mode 100755 index c29470bd..00000000 --- a/askbot/skins/default/media/js/yuicompressor-2.4.2.jar +++ /dev/null @@ -23,6 +23,7 @@ install_requires = [ 'django-celery==2.2.3', 'django-kombu==0.9.2', 'django-followit', + 'python-openid', ] #todo: have a dirty version retriever that |