Skip to content

Commit

Permalink
Merge "Changes to stored hashed password in backends. Using passlib a…
Browse files Browse the repository at this point in the history
… password hashing library. Using sha512. Setting hashing to be the default behavior."
  • Loading branch information
Jenkins authored and openstack-gerrit committed Sep 29, 2011
2 parents 50542df + 011005c commit 8948d10
Show file tree
Hide file tree
Showing 10 changed files with 72 additions and 33 deletions.
3 changes: 3 additions & 0 deletions etc/keystone.conf
Expand Up @@ -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.
Expand Down
6 changes: 6 additions & 0 deletions keystone/backends/__init__.py
Expand Up @@ -28,6 +28,7 @@
ADMIN_ROLE_NAME = None
SERVICE_ADMIN_ROLE_ID = None
SERVICE_ADMIN_ROLE_NAME = None
SHOULD_HASH_PASSWORD = None


def configure_backends(options):
Expand All @@ -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"]
49 changes: 49 additions & 0 deletions keystone/backends/backendutils.py
@@ -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)
13 changes: 4 additions & 9 deletions keystone/backends/ldap/api/user.py
@@ -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

Expand Down Expand Up @@ -37,7 +37,7 @@ def create(self, values):
# 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'])
Expand All @@ -55,6 +55,7 @@ def update(self, id, values):
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):
Expand Down Expand Up @@ -113,13 +114,7 @@ def users_get_by_tenant_get_page_markers(self, tenant_id, marker, limit):
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',
Expand Down
14 changes: 4 additions & 10 deletions keystone/backends/sqlalchemy/api/user.py
Expand Up @@ -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
Expand All @@ -29,17 +29,11 @@ def get_all(self, session=None):

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()
Expand Down Expand Up @@ -119,7 +113,7 @@ def update(self, id, values, session=None):
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)

Expand Down Expand Up @@ -315,7 +309,7 @@ def users_get_by_tenant_get_page_markers(self, tenant_id, marker, limit, \
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():
Expand Down
1 change: 1 addition & 0 deletions keystone/test/etc/ldap.conf.template
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion keystone/test/etc/sql.conf.template
Expand Up @@ -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
Expand Down Expand Up @@ -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
1 change: 1 addition & 0 deletions keystone/test/unit/base.py
Expand Up @@ -101,6 +101,7 @@ class ServiceAPITest(unittest.TestCase):
},
'keystone-admin-role': 'Admin',
'keystone-service-admin-role': 'KeystoneServiceAdmin',
'hash-password': 'True',
}

def setUp(self):
Expand Down
13 changes: 0 additions & 13 deletions keystone/utils.py
Expand Up @@ -19,9 +19,7 @@
import sys
import logging
import functools

from webob import Response

import keystone.logic.types.fault as fault


Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions tools/pip-requires
Expand Up @@ -13,6 +13,8 @@ sqlalchemy
webob
Routes
httplib2
#For Hashing
passlib

# Optional backend: LDAP
python-ldap==2.3.13
Expand Down

0 comments on commit 8948d10

Please sign in to comment.