Changes to stored hashed password in backends.

Using passlib a password hashing library.
Using sha512.
Setting hashing to be the default behavior.

Change-Id: I872f53516180714754ccc157b7e675fdcfd12d02
This commit is contained in:
Yogeshwar Srikrishnan 2011-09-28 15:41:40 -05:00
parent 67264e70ed
commit 011005cdc6
10 changed files with 72 additions and 33 deletions

View File

@ -44,6 +44,9 @@ keystone-admin-role = Admin
#Role that allows to perform service admin operations.
keystone-service-admin-role = KeystoneServiceAdmin
#Tells whether password user need to be hashed in the backend
hash-password = True
[keystone.backends.sqlalchemy]
# SQLAlchemy connection string for the reference implementation registry
# server. Any valid SQLAlchemy connection string is fine.

View File

@ -28,6 +28,7 @@ ADMIN_ROLE_ID = None
ADMIN_ROLE_NAME = None
SERVICE_ADMIN_ROLE_ID = None
SERVICE_ADMIN_ROLE_NAME = None
SHOULD_HASH_PASSWORD = None
def configure_backends(options):
@ -43,3 +44,8 @@ def configure_backends(options):
global SERVICE_ADMIN_ROLE_NAME
SERVICE_ADMIN_ROLE_NAME = options["keystone-service-admin-role"]
global SHOULD_HASH_PASSWORD
if "hash-password" in options\
and ast.literal_eval(options["hash-password"]) == True:
SHOULD_HASH_PASSWORD = options["hash-password"]

View File

@ -0,0 +1,49 @@
from keystone.backends import models
import keystone.backends as backends
from passlib.hash import sha512_crypt as sc
def __get_hashed_password(password):
if password != None and len(password) > 0:
return __make_password(password)
else:
return None
def set_hashed_password(values):
"""
Sets hashed password for password.
"""
if backends.SHOULD_HASH_PASSWORD:
if type(values) is dict and 'password' in values.keys():
values['password'] = __get_hashed_password(values['password'])
elif type(values) is models.User:
values.password = __get_hashed_password(values.password)
def check_password(raw_password, enc_password):
"""
Compares raw password and encoded password.
"""
if not raw_password:
return False
if backends.SHOULD_HASH_PASSWORD:
return sc.verify(raw_password, enc_password)
else:
return enc_password == raw_password
def __make_password(raw_password):
"""
Produce a new encoded password.
"""
if raw_password is None:
return None
hsh = __get_hexdigest(raw_password)
return '%s' % (hsh)
#Refer http://packages.python.org/passlib/lib/passlib.hash.sha512_crypt.html
#Using the default properties as of now.Salt gets generated automatically.
def __get_hexdigest(raw_password):
return sc.encrypt(raw_password)

View File

@ -1,7 +1,7 @@
import ldap
import ldap.filter
from keystone import utils
import keystone.backends.backendutils as utils
from keystone.backends.api import BaseUserAPI
from keystone.backends.sqlalchemy.api.user import UserAPI as SQLUserAPI
@ -37,7 +37,7 @@ class UserAPI(BaseLdapAPI, BaseUserAPI):
# Persist the 'name' as the UID
values['id'] = values['name']
delattr(values, 'name')
utils.set_hashed_password(values)
values = super(UserAPI, self).create(values)
if values['tenant_id'] is not None:
self.api.tenant.add_user(values['tenant_id'], values['id'])
@ -55,6 +55,7 @@ class UserAPI(BaseLdapAPI, BaseUserAPI):
self.api.tenant.remove_user(old_obj.tenant_id, id)
if new_tenant:
self.api.tenant.add_user(new_tenant, id)
utils.set_hashed_password(values)
super(UserAPI, self).update(id, values, old_obj)
def delete(self, id):
@ -113,13 +114,7 @@ class UserAPI(BaseLdapAPI, BaseUserAPI):
self.api.tenant.get_users(tenant_id))
def check_password(self, user, password):
try:
self.api.get_connection(self._id_to_dn(user.id), password)
except (ldap.NO_SUCH_OBJECT, ldap.INAPPROPRIATE_AUTH,
ldap.INVALID_CREDENTIALS):
return False
else:
return True
return utils.check_password(password, user.password)
add_redirects(locals(), SQLUserAPI, ['get_by_group', 'tenant_group',
'tenant_group_delete', 'user_groups_get_all',

View File

@ -15,7 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import keystone.utils as utils
import keystone.backends.backendutils as utils
from keystone.backends.sqlalchemy import get_session, models, aliased, \
joinedload
from keystone.backends.api import BaseUserAPI
@ -29,17 +29,11 @@ class UserAPI(BaseUserAPI):
def create(self, values):
user_ref = models.User()
self.__check_and_use_hashed_password(values)
utils.set_hashed_password(values)
user_ref.update(values)
user_ref.save()
return user_ref
def __check_and_use_hashed_password(self, values):
if type(values) is dict and 'password' in values.keys():
values['password'] = utils.get_hashed_password(values['password'])
elif type(values) is models.User:
values.password = utils.get_hashed_password(values.password)
def get(self, id, session=None):
if not session:
session = get_session()
@ -119,7 +113,7 @@ class UserAPI(BaseUserAPI):
session = get_session()
with session.begin():
user_ref = self.get(id, session)
self.__check_and_use_hashed_password(values)
utils.set_hashed_password(values)
user_ref.update(values)
user_ref.save(session=session)
@ -315,7 +309,7 @@ class UserAPI(BaseUserAPI):
return (prev_page, next_page)
def check_password(self, user, password):
return user.password == utils.get_hashed_password(password)
return utils.check_password(password, user.password)
def get():

View File

@ -14,6 +14,7 @@ admin_host = 0.0.0.0
admin_port = 5001
keystone-admin-role = Admin
keystone-service-admin-role = KeystoneServiceAdmin
hash-password = True
[keystone.backends.sqlalchemy]
sql_connection = for_testing_only

View File

@ -14,6 +14,7 @@ admin_host = 0.0.0.0
admin_port = 5001
keystone-admin-role = Admin
keystone-service-admin-role = KeystoneServiceAdmin
hash-password = True
[keystone.backends.sqlalchemy]
sql_connection = for_testing_only
@ -45,4 +46,4 @@ paste.filter_factory = keystone.middleware.url:filter_factory
paste.filter_factory = keystone.frontends.legacy_token_auth:filter_factory
[filter:RAX-KEY-extension]
paste.filter_factory = keystone.contrib.extensions.service.raxkey.frontend:filter_factory
paste.filter_factory = keystone.contrib.extensions.service.raxkey.frontend:filter_factory

View File

@ -101,6 +101,7 @@ class ServiceAPITest(unittest.TestCase):
},
'keystone-admin-role': 'Admin',
'keystone-service-admin-role': 'KeystoneServiceAdmin',
'hash-password': 'True',
}
def setUp(self):

View File

@ -19,9 +19,7 @@ import os
import sys
import logging
import functools
from webob import Response
import keystone.logic.types.fault as fault
@ -144,17 +142,6 @@ def send_legacy_result(code, headers):
return resp
# Currently using sha1 to hash, without a salt value.
# Need to research relevant openstack standards.
def get_hashed_password(password):
if password != None and len(password) > 0:
return password
# why is this disabled?
#return hashlib.sha1(password).hexdigest()
else:
return None
def import_module(module_name, class_name=None):
'''Import a class given a full module.class name or seperate
module and options. If no class_name is given, it is assumed to

View File

@ -13,6 +13,8 @@ sqlalchemy
webob
Routes
httplib2
#For Hashing
passlib
# Optional backend: LDAP
python-ldap==2.3.13