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:
parent
67264e70ed
commit
011005cdc6
|
@ -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.
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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)
|
|
@ -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',
|
||||
|
|
|
@ -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():
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -101,6 +101,7 @@ class ServiceAPITest(unittest.TestCase):
|
|||
},
|
||||
'keystone-admin-role': 'Admin',
|
||||
'keystone-service-admin-role': 'KeystoneServiceAdmin',
|
||||
'hash-password': 'True',
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -13,6 +13,8 @@ sqlalchemy
|
|||
webob
|
||||
Routes
|
||||
httplib2
|
||||
#For Hashing
|
||||
passlib
|
||||
|
||||
# Optional backend: LDAP
|
||||
python-ldap==2.3.13
|
||||
|
|
Loading…
Reference in New Issue