PYTHON-768 - Support authMechanismProperties.
This change also deprecates the gssapiServiceName option, which is replaced by authMechanismProperties=SERVICE_NAME:<service name>.
This commit is contained in:
parent
c101ed036c
commit
d3e88ee8ed
@ -144,15 +144,15 @@ or using :meth:`~pymongo.database.Database.authenticate`::
|
||||
True
|
||||
|
||||
The default service name used by MongoDB and PyMongo is `mongodb`. You can
|
||||
specify a custom service name with the ``gssapiServiceName`` option::
|
||||
specify a custom service name with the ``authMechanismProperties`` option::
|
||||
|
||||
>>> from pymongo import MongoClient
|
||||
>>> uri = "mongodb://mongodbuser%40EXAMPLE.COM@example.com/?authMechanism=GSSAPI&gssapiServiceName=myservicename"
|
||||
>>> uri = "mongodb://mongodbuser%40EXAMPLE.COM@example.com/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:myservicename"
|
||||
>>> client = MongoClient(uri)
|
||||
>>>
|
||||
>>> client = MongoClient('example.com')
|
||||
>>> db = client.test
|
||||
>>> db.authenticate('mongodbuser@EXAMPLE.COM', mechanism='GSSAPI', gssapiServiceName='myservicename')
|
||||
>>> db.authenticate('mongodbuser@EXAMPLE.COM', mechanism='GSSAPI', authMechanismProperties='SERVICE_NAME:myservicename')
|
||||
True
|
||||
|
||||
.. note::
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
"""Authentication helpers."""
|
||||
|
||||
import hmac
|
||||
import warnings
|
||||
try:
|
||||
import hashlib
|
||||
_MD5 = hashlib.md5
|
||||
@ -52,7 +53,17 @@ def _build_credentials_tuple(mech, source, user, passwd, extra):
|
||||
"""Build and return a mechanism specific credentials tuple.
|
||||
"""
|
||||
if mech == 'GSSAPI':
|
||||
gsn = extra.get('gssapiservicename', 'mongodb')
|
||||
gsn = 'mongodb'
|
||||
if "gssapiservicename" in extra:
|
||||
gsn = extra.get('gssapiservicename')
|
||||
msg = ('The gssapiServiceName option is deprecated. Use '
|
||||
'"authMechanismProperties=SERVICE_NAME:%s" instead.' % gsn)
|
||||
warnings.warn(msg, DeprecationWarning, stacklevel=3)
|
||||
# SERVICE_NAME overrides gssapiServiceName.
|
||||
if 'authmechanismproperties' in extra:
|
||||
props = extra['authmechanismproperties']
|
||||
if 'SERVICE_NAME' in props:
|
||||
gsn = props.get('SERVICE_NAME')
|
||||
# No password, source is always $external.
|
||||
return (mech, '$external', user, gsn)
|
||||
elif mech == 'MONGODB-X509':
|
||||
|
||||
@ -265,6 +265,28 @@ def validate_uuid_subtype(dummy, value):
|
||||
return value
|
||||
|
||||
|
||||
_MECHANISM_PROPS = frozenset(['SERVICE_NAME'])
|
||||
|
||||
|
||||
def validate_auth_mechanism_properties(option, value):
|
||||
"""Validate authMechanismProperties."""
|
||||
value = validate_basestring(option, value)
|
||||
props = {}
|
||||
for opt in value.split(','):
|
||||
try:
|
||||
key, val = opt.split(':')
|
||||
if key not in _MECHANISM_PROPS:
|
||||
raise ConfigurationError("%s is not a supported auth "
|
||||
"mechanism property. Must be one of "
|
||||
"%s." % (key, tuple(_MECHANISM_PROPS)))
|
||||
props[key] = val
|
||||
except ValueError:
|
||||
raise ConfigurationError("auth mechanism properties must be "
|
||||
"key:value pairs like SERVICE_NAME:"
|
||||
"mongodb, not %s." % (opt,))
|
||||
return props
|
||||
|
||||
|
||||
# jounal is an alias for j,
|
||||
# wtimeoutms is an alias for wtimeout,
|
||||
# readpreferencetags is an alias for tag_sets.
|
||||
@ -299,12 +321,13 @@ VALIDATORS = {
|
||||
'authmechanism': validate_auth_mechanism,
|
||||
'authsource': validate_basestring,
|
||||
'gssapiservicename': validate_basestring,
|
||||
'authmechanismproperties': validate_auth_mechanism_properties,
|
||||
'uuidrepresentation': validate_uuid_representation,
|
||||
'socketkeepalive': validate_boolean
|
||||
}
|
||||
|
||||
|
||||
_AUTH_OPTIONS = frozenset(['gssapiservicename'])
|
||||
_AUTH_OPTIONS = frozenset(['gssapiservicename', 'authmechanismproperties'])
|
||||
|
||||
|
||||
def validate_auth_option(option, value):
|
||||
|
||||
@ -107,22 +107,27 @@ class TestGSSAPI(unittest.TestCase):
|
||||
# Without gssapiServiceName
|
||||
self.assertTrue(client.test.authenticate(PRINCIPAL,
|
||||
mechanism='GSSAPI'))
|
||||
self.assertTrue(client.database_names())
|
||||
client.database_names()
|
||||
uri = ('mongodb://%s@%s:%d/?authMechanism='
|
||||
'GSSAPI' % (quote_plus(PRINCIPAL), GSSAPI_HOST, GSSAPI_PORT))
|
||||
client = MongoClient(uri)
|
||||
self.assertTrue(client.database_names())
|
||||
client.database_names()
|
||||
|
||||
# With gssapiServiceName
|
||||
self.assertTrue(client.test.authenticate(PRINCIPAL,
|
||||
mechanism='GSSAPI',
|
||||
gssapiServiceName='mongodb'))
|
||||
self.assertTrue(client.database_names())
|
||||
client.database_names()
|
||||
uri = ('mongodb://%s@%s:%d/?authMechanism='
|
||||
'GSSAPI;gssapiServiceName=mongodb' % (quote_plus(PRINCIPAL),
|
||||
GSSAPI_HOST, GSSAPI_PORT))
|
||||
client = MongoClient(uri)
|
||||
self.assertTrue(client.database_names())
|
||||
client.database_names()
|
||||
uri = ('mongodb://%s@%s:%d/?authMechanism='
|
||||
'GSSAPI;authMechanismProperties=SERVICE_NAME:mongodb' % (
|
||||
quote_plus(PRINCIPAL), GSSAPI_HOST, GSSAPI_PORT))
|
||||
client = MongoClient(uri)
|
||||
client.database_names()
|
||||
|
||||
set_name = client.admin.command('ismaster').get('setName')
|
||||
if set_name:
|
||||
@ -132,25 +137,31 @@ class TestGSSAPI(unittest.TestCase):
|
||||
# Without gssapiServiceName
|
||||
self.assertTrue(client.test.authenticate(PRINCIPAL,
|
||||
mechanism='GSSAPI'))
|
||||
self.assertTrue(client.database_names())
|
||||
client.database_names()
|
||||
uri = ('mongodb://%s@%s:%d/?authMechanism=GSSAPI;replicaSet'
|
||||
'=%s' % (quote_plus(PRINCIPAL),
|
||||
GSSAPI_HOST, GSSAPI_PORT, str(set_name)))
|
||||
client = MongoReplicaSetClient(uri)
|
||||
self.assertTrue(client.database_names())
|
||||
client.database_names()
|
||||
|
||||
# With gssapiServiceName
|
||||
self.assertTrue(client.test.authenticate(PRINCIPAL,
|
||||
mechanism='GSSAPI',
|
||||
gssapiServiceName='mongodb'))
|
||||
self.assertTrue(client.database_names())
|
||||
client.database_names()
|
||||
uri = ('mongodb://%s@%s:%d/?authMechanism=GSSAPI;replicaSet'
|
||||
'=%s;gssapiServiceName=mongodb' % (quote_plus(PRINCIPAL),
|
||||
GSSAPI_HOST,
|
||||
GSSAPI_PORT,
|
||||
str(set_name)))
|
||||
client = MongoReplicaSetClient(uri)
|
||||
self.assertTrue(client.database_names())
|
||||
client.database_names()
|
||||
uri = ('mongodb://%s@%s:%d/?authMechanism=GSSAPI;replicaSet=%s;'
|
||||
'authMechanismProperties=SERVICE_NAME:mongodb' % (
|
||||
quote_plus(PRINCIPAL),
|
||||
GSSAPI_HOST, GSSAPI_PORT, str(set_name)))
|
||||
client = MongoReplicaSetClient(uri)
|
||||
client.database_names()
|
||||
|
||||
def test_gssapi_threaded(self):
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user