def begin(self, url): endpoint = OpenIDServiceEndpoint() endpoint.claimed_id = 'oid' endpoint.server_url = 'http://localhost/' endpoint.type_uris = [OPENID_2_0_TYPE] auth_request = consumer.AuthRequest(endpoint, None) return auth_request
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 create_sreg_response(self, fullname='', email='', identifier=''): message = Message(OPENID2_NS) message.setArg(SREG_NS, "fullname", fullname) message.setArg(SREG_NS, "email", email) endpoint = OpenIDServiceEndpoint() endpoint.display_identifier = identifier return SuccessResponse(endpoint, message, signed_fields=message.toPostArgs().keys())
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
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
def create_sreg_response(self, fullname='', email='', identifier=''): message = Message(OPENID2_NS) message.setArg(SREG_NS, "fullname", fullname) message.setArg(SREG_NS, "email", email) endpoint = OpenIDServiceEndpoint() endpoint.display_identifier = identifier return SuccessResponse(endpoint, message, signed_fields=message.toPostArgs().keys())
def make_openid_response(self, sreg_args=None, teams_args=None): endpoint = OpenIDServiceEndpoint() endpoint.claimed_id = 'some-id' message = Message(OPENID2_NS) if sreg_args is not None: for key, value in sreg_args.items(): message.setArg(SREG_NS, key, value) if teams_args is not None: for key, value in teams_args.items(): message.setArg(TEAMS_NS, key, value) response = SuccessResponse( endpoint, message, signed_fields=message.toPostArgs().keys()) return response
def make_openid_response(self, sreg_args=None, teams_args=None): endpoint = OpenIDServiceEndpoint() endpoint.claimed_id = 'some-id' message = Message(OPENID2_NS) if sreg_args is not None: for key, value in sreg_args.items(): message.setArg(SREG_NS, key, value) if teams_args is not None: for key, value in teams_args.items(): message.setArg(TEAMS_NS, key, value) response = SuccessResponse(endpoint, message, signed_fields=message.toPostArgs().keys()) return response
def test_sreg(self): authreq = self.consumer.beginWithoutDiscovery( OpenIDServiceEndpoint.fromOPEndpointURL('http://example.com/endpoint/')) sreg_request = sreg.SRegRequest(required=['nickname']) authreq.addExtension(sreg_request) url = authreq.redirectURL( 'http://localhost/', return_to='http://localhost/') self.client.post('/login/') response = self.client.open(make_builder(url)) self.assertEqual(response.status_code, 302) parsed = urlparse(response.headers['LOCATION']) info = self.consumer.complete( dict(parse_qsl(parsed.query)), response.headers['LOCATION']) self.assertIsInstance(info, SuccessResponse) sreg_resp = sreg.SRegResponse.fromSuccessResponse(info) self.assertEqual(sreg_resp['nickname'], 'user')
def associate(services, url): '''Create an association (OpenID section 8) between RP and OP. Return response as a dictionary.''' req_data = { 'openid.ns':"http://specs.openid.net/auth/2.0", 'openid.mode':"associate", 'openid.assoc_type':"HMAC-SHA1", 'openid.session_type':"no-encryption", } if url.startswith('http:'): # Use DH exchange req_data['openid.session_type'] = "DH-SHA1" # Private key: random number between 1 and dh_prime-1 priv = random.SystemRandom().randrange(1, dh_prime - 1) # Public key: 2^priv mod prime pubkey = pow(2L, priv, dh_prime) dh_public_base64 = base64.b64encode(btwoc(pubkey)) # No need to send key and generator req_data['openid.dh_consumer_public'] = dh_public_base64 if is_compat_1x(services): # 14.2.1: clear session_type in 1.1 compatibility mode if req_data['openid.session_type'] == "no-encryption": req_data['openid.session_type'] = '' del req_data['openid.ns'] res = urllib.urlopen(url, b(urllib.urlencode(req_data))) try: if res.getcode() != 200: raise ValueError("OpenID provider refuses connection with status %s" % res.getcode()) data = parse_response(res.read()) except ValueError: endpoint = OpenIDServiceEndpoint.fromOPEndpointURL(url) store = MemoryStore() consumer = GenericConsumer(store) try: assoc = consumer._requestAssociation(endpoint, req_data['openid.assoc_type'], req_data['openid.session_type']) data = { 'assoc_handle': assoc.handle, 'expires_in': assoc.lifetime, 'mac_key': oidutil.toBase64(assoc.secret), } except ServerError: data = { 'error': 'Server %s refused its suggested association type: session_type=%s, assoc_type=%s' % (url, req_data['openid.assoc_type'], req_data['openid.session_type']), } if 'error' in data: raise ValueError, "associate failed: "+data['error'] if url.startswith('http:'): enc_mac_key = b(data.get('enc_mac_key')) if not enc_mac_key: raise ValueError, "Provider protocol error: not using DH-SHA1" enc_mac_key = base64.b64decode(enc_mac_key) dh_server_public = unbtwoc(base64.b64decode(b(data['dh_server_public']))) # shared secret: sha1(2^(server_priv*priv) mod prime) xor enc_mac_key shared_secret = btwoc(pow(dh_server_public, priv, dh_prime)) shared_secret = hashlib.sha1(shared_secret).digest() if len(shared_secret) != len(enc_mac_key): raise ValueError, "incorrect DH key size" # Fake mac_key result data['mac_key'] = b(base64.b64encode(string_xor(enc_mac_key, shared_secret))) return data
def openid2_get_redirect(request, provider, callback, scope, mode): xrds = urllib.urlopen(providers[provider]["xrds"]) if xrds.getcode() != 200: raise Exception("OpenID Failed: Invalid response from " + providers[provider]["displayname"] + " on obtaining a XRDS information: " + xrds.read()) xrds = xrds.read() from openid.consumer.consumer import Consumer from openid.consumer.discover import OpenIDServiceEndpoint from openid.store.memstore import MemoryStore service = OpenIDServiceEndpoint.fromXRDS(providers[provider]["xrds"], xrds)[0] consumer = Consumer(request.session, MemoryStore()) auth = consumer.beginWithoutDiscovery(service) if "extensions" in providers[provider]: for ext, d in providers[provider]["extensions"].iteritems(): for k, v in d.iteritems(): auth.addExtensionArg(ext, k, v) if mode == "compact": # works with Google auth.addExtensionArg("http://specs.openid.net/extensions/ui/1.0", "mode", "popup") return auth.redirectURL(realm=SITE_ROOT_URL, return_to=callback)
def __init__(self, status, identity_url): self.status = status self.identity_url = identity_url self.message = message.Message() sig_ext = SignatureVerification(consumer_key='CKEY', secret_key='SKEY', request_token='token', hmac=None, timestamp=None) sig_ext.toMessage(self.message) sreg_ext = SRegRequest( required=['nickname', 'email'], optional=['fullname'], policy_url=None, sreg_ns_uri='http://openid.net/extensions/sreg/1.1') sreg_ext.toMessage(self.message) self.signed_fields = [ 'openid.sreg.nickname', 'openid.sreg.email', 'openid.sreg.required', 'openid.sreg.optional', 'openid.sreg.fullname' ] self.endpoint = OpenIDServiceEndpoint() self.endpoint.claimed_id = identity_url
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 openid2_get_redirect(request, provider, callback, scope, mode): xrds = urllib.request.urlopen(providers[provider]["xrds"]) if xrds.getcode() != 200: raise Exception("OpenID Failed: Invalid response from " + providers[provider]["displayname"] + " on obtaining a XRDS information: " + xrds.read()) xrds = xrds.read() from openid.consumer.consumer import Consumer from openid.consumer.discover import OpenIDServiceEndpoint service = OpenIDServiceEndpoint.fromXRDS(providers[provider]["xrds"], xrds)[0] consumer = Consumer(request.session, openid_store) auth = consumer.beginWithoutDiscovery(service) if "extensions" in providers[provider]: for ext, d in providers[provider]["extensions"].items(): for k, v in d.items(): auth.addExtensionArg(ext, k, v) if mode == "compact": # works with Google auth.addExtensionArg("http://specs.openid.net/extensions/ui/1.0", "mode", "popup") return auth.redirectURL(realm=settings.SITE_ROOT_URL, return_to=callback)
def make_response_ax(self, schema="http://axschema.org/", fullname="Some User", nickname="someuser", email="*****@*****.**", first=None, last=None, verified=False): endpoint = OpenIDServiceEndpoint() message = Message(OPENID2_NS) attributes = [("nickname", schema + "namePerson/friendly", nickname), ("fullname", schema + "namePerson", fullname), ("email", schema + "contact/email", email), ("account_verified", "http://ns.login.ubuntu.com/2013/validation/account", "token_via_email" if verified else "no")] if first: attributes.append( ("first", "http://axschema.org/namePerson/first", first)) if last: attributes.append( ("last", "http://axschema.org/namePerson/last", last)) message.setArg(AX_NS, "mode", "fetch_response") for (alias, uri, value) in attributes: message.setArg(AX_NS, "type.%s" % alias, uri) message.setArg(AX_NS, "value.%s" % alias, value) return SuccessResponse(endpoint, message, signed_fields=message.toPostArgs().keys())
def make_endpoint(self, protocol_uri, claimed_id, local_id=None): """Create an endpoint for use with `Consumer.beginWithoutDiscovery`. :arg protocol_uri: The URI for the OpenID protocol version. This should be one of the OPENID_X_Y_TYPE constants. :arg claimed_id: The claimed identity URL for the endpoint. :arg local_id: The OP local identifier for the endpoint. If this argument is not provided, it defaults to claimed_id. """ msg = "Unexpected protocol URI: %s" % protocol_uri assert protocol_uri in OPENID_TYPES, msg endpoint = OpenIDServiceEndpoint() endpoint.type_uris = [protocol_uri] endpoint.server_url = self.base_openid_url endpoint.claimed_id = claimed_id endpoint.local_id = local_id or claimed_id return endpoint
def test_openidserviceendpoint(self): self.assertEqual( json.loads(JSONEncoder().encode({'k': OpenIDServiceEndpoint()})), {"k": { "type_uris": [], "claimed_id": None, "canonicalID": None, "__class__": "OpenIDServiceEndpoint", "used_yadis": False, "server_url": None, "local_id": None, "display_identifier": None, }} )
def _object_hook(self, d): cls_name = d.pop('__class__', None) if cls_name in ('OpenID', 'YadisServiceManager'): return globals()[cls_name](**d) elif cls_name == 'OpenIDServiceEndpoint': endpoint = OpenIDServiceEndpoint() for k, v in d.items(): setattr(endpoint, k, v) return endpoint return d
def make_endpoint(protocol_uri, claimed_id, local_id=None): """Create an endpoint for use with `Consumer.beginWithoutDiscovery`. :arg protocol_uri: The URI for the OpenID protocol version. This should be one of the OPENID_X_Y_TYPE constants. :arg claimed_id: The claimed identity URL for the endpoint. :arg local_id: The OP local identifier for the endpoint. If this argument is not provided, it defaults to claimed_id. """ assert protocol_uri in [ OPENID_1_0_TYPE, OPENID_1_1_TYPE, OPENID_2_0_TYPE], ( "Unexpected protocol URI: %s" % protocol_uri) endpoint = OpenIDServiceEndpoint() endpoint.type_uris = [protocol_uri] endpoint.server_url = get_requested_server_url(claimed_id) endpoint.claimed_id = claimed_id endpoint.local_id = local_id or claimed_id return endpoint
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_setup_redirect(self): authreq = self.consumer.beginWithoutDiscovery( OpenIDServiceEndpoint.fromOPEndpointURL('http://example.com/endpoint/')) url = authreq.redirectURL( 'http://localhost/', return_to='http://localhost/') response = self.client.open(make_builder(url)) self.assertEqual(response.status_code, 302) self.assertEqual( parse_qs(urlparse(response.headers['LOCATION']).query)['next'][0], url)
def test_extract_user_details_sreg(self): endpoint = OpenIDServiceEndpoint() message = Message(OPENID2_NS) message.setArg(SREG_NS, "nickname", "someuser") message.setArg(SREG_NS, "fullname", "Some User") message.setArg(SREG_NS, "email", "*****@*****.**") response = SuccessResponse( endpoint, message, signed_fields=message.toPostArgs().keys()) data = self.backend._extract_user_details(response) self.assertEqual(data, {"nickname": "someuser", "first_name": "Some", "last_name": "User", "email": "*****@*****.**"})
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 consumer.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 consumer.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 to_match.server_url.startswith(u'https://www.google.com/a/'): import urllib claimed_id = u'https://www.google.com/accounts/o8/user-xrds?uri=%s' % urllib.quote_plus(to_match.claimed_id) else: claimed_id = to_match.claimed_id if not endpoint: oidutil.log('No pre-discovered information supplied.') endpoint = self._discoverAndVerify(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 consumer.ProtocolError, e: oidutil.log( "Error attempting to use stored discovery information: " + str(e)) oidutil.log("Attempting discovery to verify endpoint") endpoint = self._discoverAndVerify( claimed_id, [to_match])
def test_immediate_failure(self): authreq = self.consumer.beginWithoutDiscovery( OpenIDServiceEndpoint.fromOPEndpointURL('http://example.com/endpoint/')) url = authreq.redirectURL( 'http://localhost/', return_to='http://localhost/', immediate=True) response = self.client.open(make_builder(url)) self.assertEqual(response.status_code, 302) parsed = urlparse(response.headers['LOCATION']) info = self.consumer.complete( dict(parse_qsl(parsed.query)), response.headers['LOCATION']) self.assertIsInstance(info, SetupNeededResponse)
def test_extract_user_details_ax(self): endpoint = OpenIDServiceEndpoint() message = Message(OPENID2_NS) attributes = [ ("nickname", "http://axschema.org/namePerson/friendly", "someuser"), ("fullname", "http://axschema.org/namePerson", "Some User"), ("email", "http://axschema.org/contact/email", "*****@*****.**"), ] message.setArg(AX_NS, "mode", "fetch_response") for (alias, uri, value) in attributes: message.setArg(AX_NS, "type.%s" % alias, uri) message.setArg(AX_NS, "value.%s" % alias, value) response = SuccessResponse( endpoint, message, signed_fields=message.toPostArgs().keys()) data = self.backend._extract_user_details(response) self.assertEqual(data, {"nickname": "someuser", "first_name": "Some", "last_name": "User", "email": "*****@*****.**"})
def make_identifier_select_endpoint(self, protocol_uri): """Create an endpoint for use in OpenID identifier select mode. :arg protocol_uri: The URI for the OpenID protocol version. This should be one of the OPENID_X_Y_TYPE constants. If the OpenID 1.x protocol is selected, the endpoint will be suitable for use with Launchpad's non-standard identifier select workflow. """ msg = "Unexpected protocol URI: %s" % protocol_uri assert protocol_uri in OPENID_TYPES, msg endpoint = OpenIDServiceEndpoint() endpoint.server_url = self.base_openid_url if protocol_uri == OPENID_2_0_TYPE: endpoint.type_uris = [OPENID_IDP_2_0_TYPE] else: endpoint.type_uris = [protocol_uri] endpoint.claimed_id = IDENTIFIER_SELECT endpoint.local_id = IDENTIFIER_SELECT return endpoint
def make_identifier_select_endpoint(): """Create an endpoint for use in OpenID identifier select mode.""" endpoint = OpenIDServiceEndpoint() endpoint.server_url = get_server_url() endpoint.type_uris = [OPENID_IDP_2_0_TYPE] return endpoint
def make_fake_openid_endpoint(self, claimed_id=None): endpoint = OpenIDServiceEndpoint() endpoint.claimed_id = claimed_id return endpoint
def runOneTest(self): actual = OpenIDServiceEndpoint.fromHTML('http://unused.url/', self.data) expected = [] self.assertEqual(expected, actual)
def create_service(): endpoint_type = 'production' if mojeid_settings.MOJEID_INSTANCE_PRODUCTION \ else 'testing' defs = mojeid_services[endpoint_type] return OpenIDServiceEndpoint.fromXRDS(defs['url'], defs['xrds'])[0]
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.identity_url = user_url endpoint.server_url = server_url endpoint.delegate = delegate_url fetcher = TestFetcher(None, None, assocs[0]) fetchers.setDefaultFetcher(fetcher, wrap_exceptions=False) def run(): trust_root = consumer_url consumer = GenericConsumer(store) request = consumer.begin(endpoint) return_to = consumer_url 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) query = { 'nonce':request.return_to_args['nonce'], '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) assoc.addSignature(['mode', 'return_to', 'identity'], query) info = consumer.complete(query, request.endpoint) 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
def setUp(self): self.consumer = ErrorRaisingConsumer(store=None) self.endpoint = OpenIDServiceEndpoint() self.endpoint.type_uris = [OPENID1_NS] self.endpoint.server_url = 'bogus'
def test_from_html(self): for html in self.cases: actual = OpenIDServiceEndpoint.fromHTML('http://unused.url/', html) self.assertEqual(actual, [])
def test_invalid_html(self): self.assertEqual( OpenIDServiceEndpoint.fromHTML('http://example.url/', "http://not.in.a.link.tag/"), [])
def test_empty(self): self.assertEqual(OpenIDServiceEndpoint.fromHTML('http://example.url/', ''), [])
def create_service(): endpoint_type = 'production' if mojeid_settings.MOJEID_INSTANCE_PRODUCTION \ else 'testing' defs = mojeid_services[endpoint_type] return OpenIDServiceEndpoint.fromXRDS(defs['url'], defs['xrds'])[0]
def makeSuccessResponse(claimed_id, query): """Returns an OpenID success response with given arguments, all signed.""" endpoint = OpenIDServiceEndpoint() endpoint.claimed_id = claimed_id signed_list = ['openid.' + k for k in query] return SuccessResponse(endpoint, Message.fromOpenIDArgs(query), signed_list)
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. 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()
def test_auth_failure_response(self): response = FailureResponse(OpenIDServiceEndpoint()) self.assertIsNone(self.backend.authenticate(openid_response=response)) self.assert_no_users_created()
def test_openidserviceendpoint(self): endpoint = OpenIDServiceEndpoint() endpoint2 = JSONSerializer().loads(JSONSerializer().dumps({'k': endpoint})) self.assertIsInstance(endpoint2['k'], OpenIDServiceEndpoint)
def setUp(self): self.store = memstore.MemoryStore() self.consumer = GenericConsumer(self.store) self.endpoint = OpenIDServiceEndpoint()
def test_no_op_url(self): html = '<html><head><link rel="openid.server"></head></html>' self.assertEqual(OpenIDServiceEndpoint.fromHTML('http://example.url/', html), [])
class TestOpenID1SessionNegotiation(unittest.TestCase): """ 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, see the LogCapture. 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): 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())] with LogCapture() as logbook: self.assertIsNone(self.consumer._negotiateAssociation(self.endpoint)) logbook.check( ('openid.consumer.consumer', 'ERROR', StringComparison('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] with LogCapture() as logbook: self.assertIsNone(self.consumer._negotiateAssociation(self.endpoint)) logbook.check( ('openid.consumer.consumer', 'ERROR', StringComparison('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] with LogCapture() as logbook: self.assertIsNone(self.consumer._negotiateAssociation(self.endpoint)) logbook.check( ('openid.consumer.consumer', 'ERROR', StringComparison('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] with LogCapture() as logbook: self.assertIsNone(self.consumer._negotiateAssociation(self.endpoint)) logbook.check( ('openid.consumer.consumer', 'ERROR', StringComparison('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', b'secret', 'issued', 10000, 'HMAC-SHA1') self.consumer.return_messages = [msg, assoc] with LogCapture() as logbook: self.assertIsNone(self.consumer._negotiateAssociation(self.endpoint)) logbook.check( ('openid.consumer.consumer', 'ERROR', StringComparison('Server error when requesting an association .*'))) def testValid(self): assoc = association.Association('handle', b'secret', 'issued', 10000, 'HMAC-SHA1') self.consumer.return_messages = [assoc] with LogCapture() as logbook: self.assertEqual(self.consumer._negotiateAssociation(self.endpoint), assoc) self.assertEqual(logbook.records, [])
def test_empty(self): self.assertEqual( OpenIDServiceEndpoint.fromHTML('http://example.url/', ''), [])
class TestOpenID2SessionNegotiation(unittest.TestCase): """ Test the session type negotiation behavior of an OpenID 2 consumer. """ def 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())] with LogCapture() as logbook: self.assertEqual(self.consumer._negotiateAssociation(self.endpoint), None) logbook.check( ('openid.consumer.consumer', 'ERROR', StringComparison('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] with LogCapture() as logbook: self.assertIsNone(self.consumer._negotiateAssociation(self.endpoint)) no_fallback_msg = 'Server responded with unsupported association session but did not supply a fallback.' logbook.check(('openid.consumer.consumer', 'WARNING', StringComparison('Unsupported association type .*')), ('openid.consumer.consumer', 'WARNING', no_fallback_msg)) 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] with LogCapture() as logbook: self.assertIsNone(self.consumer._negotiateAssociation(self.endpoint)) no_fallback_msg = 'Server responded with unsupported association session but did not supply a fallback.' logbook.check(('openid.consumer.consumer', 'WARNING', StringComparison('Unsupported association type .*')), ('openid.consumer.consumer', 'WARNING', no_fallback_msg)) 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] with LogCapture() as logbook: self.assertIsNone(self.consumer._negotiateAssociation(self.endpoint)) unsupported_msg = StringComparison('Server sent unsupported session/association type: .*') logbook.check(('openid.consumer.consumer', 'WARNING', StringComparison('Unsupported association type .*')), ('openid.consumer.consumer', 'WARNING', unsupported_msg)) 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', b'secret', 'issued', 10000, 'HMAC-SHA1') self.consumer.return_messages = [msg, assoc] with LogCapture() as logbook: self.assertEqual(self.consumer._negotiateAssociation(self.endpoint), assoc) logbook.check(('openid.consumer.consumer', 'WARNING', StringComparison('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())] with LogCapture() as logbook: self.assertIsNone(self.consumer._negotiateAssociation(self.endpoint)) refused_msg = StringComparison('Server %s refused its .*' % self.endpoint.server_url) logbook.check(('openid.consumer.consumer', 'WARNING', StringComparison('Unsupported association type .*')), ('openid.consumer.consumer', 'ERROR', refused_msg)) def testValid(self): """ Test the valid case, wherein an association is returned on the first attempt to get one. """ assoc = association.Association('handle', b'secret', 'issued', 10000, 'HMAC-SHA1') self.consumer.return_messages = [assoc] with LogCapture() as logbook: self.assertEqual(self.consumer._negotiateAssociation(self.endpoint), assoc) self.assertEqual(logbook.records, [])
def test_auth_setup_needed_response(self): response = SetupNeededResponse(OpenIDServiceEndpoint()) self.assertIsNone(self.backend.authenticate(openid_response=response)) self.assert_no_users_created()
def test_invalid_html(self): self.assertEqual(OpenIDServiceEndpoint.fromHTML('http://example.url/', "http://not.in.a.link.tag/"), [])
def __init__(self, openid_namespace=OPENID2_NS): super(TestMessage, self).__init__(openid_namespace=openid_namespace) endpoint = OpenIDServiceEndpoint() endpoint.claimed_id = make_claimed_id('some-id') endpoint.server_url = SERVER_URL self.endpoint = endpoint
def associate(services, url): '''Create an association (OpenID section 8) between RP and OP. Return response as a dictionary.''' req_data = { 'openid.ns': "http://specs.openid.net/auth/2.0", 'openid.mode': "associate", 'openid.assoc_type': "HMAC-SHA1", 'openid.session_type': "no-encryption", } if url.startswith('http:'): # Use DH exchange req_data['openid.session_type'] = "DH-SHA1" # Private key: random number between 1 and dh_prime-1 priv = random.SystemRandom().randrange(1, dh_prime - 1) # Public key: 2^priv mod prime pubkey = pow(2L, priv, dh_prime) dh_public_base64 = base64.b64encode(btwoc(pubkey)) # No need to send key and generator req_data['openid.dh_consumer_public'] = dh_public_base64 if is_compat_1x(services): # 14.2.1: clear session_type in 1.1 compatibility mode if req_data['openid.session_type'] == "no-encryption": req_data['openid.session_type'] = '' del req_data['openid.ns'] res = urllib.urlopen(url, b(urllib.urlencode(req_data))) try: if res.getcode() != 200: raise ValueError( "OpenID provider refuses connection with status %s" % res.getcode()) data = parse_response(res.read()) except ValueError: endpoint = OpenIDServiceEndpoint.fromOPEndpointURL(url) store = MemoryStore() consumer = GenericConsumer(store) try: assoc = consumer._requestAssociation( endpoint, req_data['openid.assoc_type'], req_data['openid.session_type']) data = { 'assoc_handle': assoc.handle, 'expires_in': assoc.lifetime, 'mac_key': oidutil.toBase64(assoc.secret), } except ServerError: data = { 'error': 'Server %s refused its suggested association type: session_type=%s, assoc_type=%s' % (url, req_data['openid.assoc_type'], req_data['openid.session_type']), } if 'error' in data: raise ValueError, "associate failed: " + data['error'] if url.startswith('http:'): enc_mac_key = b(data.get('enc_mac_key')) if not enc_mac_key: raise ValueError, "Provider protocol error: not using DH-SHA1" enc_mac_key = base64.b64decode(enc_mac_key) dh_server_public = unbtwoc( base64.b64decode(b(data['dh_server_public']))) # shared secret: sha1(2^(server_priv*priv) mod prime) xor enc_mac_key shared_secret = btwoc(pow(dh_server_public, priv, dh_prime)) shared_secret = hashlib.sha1(shared_secret).digest() if len(shared_secret) != len(enc_mac_key): raise ValueError, "incorrect DH key size" # Fake mac_key result data['mac_key'] = b( base64.b64encode(string_xor(enc_mac_key, shared_secret))) return data
def callFunc(self): return OpenIDServiceEndpoint.fromHTML('http://unused.url/', self.data)
def runOneTest(self): actual = OpenIDServiceEndpoint.fromHTML('http://unused.url/', self.data) expected = [] self.failUnlessEqual(expected, actual)
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
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()
def make_identifier_select_endpoint(): """Create an endpoint for use in OpenID identifier select mode.""" endpoint = OpenIDServiceEndpoint() endpoint.server_url = get_server_url() endpoint.type_uris = [OPENID_IDP_2_0_TYPE] return endpoint
def test_no_op_url(self): html = '<html><head><link rel="openid.server"></head></html>' self.assertEqual( OpenIDServiceEndpoint.fromHTML('http://example.url/', html), [])
def test_openid_request_failure_response(self): openid_response = MockOpenIdFailureResponse( endpoint=OpenIDServiceEndpoint()) user = self.backend.authenticate(openid_response=openid_response) self.failUnlessEqual(user, None)