def set(self, key, value): with self.pool.reserve() as mc: try: if not mc.set(key, value): raise BackendError() except MemcachedError, err: raise BackendError(str(err))
def update_password(self, user_id, new_password, old_password): """Change the user password. Uses the admin bind or the user bind if the old password is provided. Args: user_id: user id new_password: new password old_password: old password of the user (optional) Returns: True if the change was successful, False otherwise """ user_dn = self._userid2dn(user_id) if user_dn is None: raise BackendError('Unknown user "%s"' % user_id) password_hash = ssha(new_password) user = [(ldap.MOD_REPLACE, 'userPassword', [password_hash])] try: with self._conn(user_dn, old_password) as conn: try: res, __ = conn.modify_s(user_dn, user) except (ldap.TIMEOUT, ldap.SERVER_DOWN, ldap.OTHER), e: self.logger.debug('Could not update the password in ldap.') raise BackendError(str(e)) except ldap.INVALID_CREDENTIALS: return False self._purge_conn(user_dn, new_password) return res == ldap.RES_MODIFY
def create_user(self, user_name, password, email): """Creates a user. Returns a user object on success.""" if not self.allow_new_users: raise BackendError("Creation of new users is disabled") # no unicode user_name = user_name.encode('utf8') password = password.encode('utf8') email = email.encode('utf8') #First make sure the username isn't taken. There'll still be a race #condition, but it's pretty small test_user = User() test_user['username'] = user_name dn = self._get_dn(test_user) if dn is not None: return False user_id = self._get_next_user_id() password_hash = ssha(password) key = '%s%s' % (random.randint(0, 9999999), user_name) key = sha1(key).hexdigest() user = { 'cn': user_name, 'sn': user_name, 'uid': user_name, 'uidNumber': str(user_id), 'userPassword': password_hash, 'primaryNode': 'weave:', 'accountStatus': '1', 'account-enabled': 'Yes', 'mail': email, 'mail-verified': key, 'objectClass': ['dataStore', 'inetOrgPerson'] } dn = "uidNumber=%i,%s" % (user_id, self.users_root) #need a copy with some of the info for the return value userobj = User() userobj['username'] = user['uid'] userobj['userid'] = user['uidNumber'] userobj['mail'] = email userobj['dn'] = dn #need to turn the user hash into tuples user = user.items() with self._conn() as conn: try: res, __ = conn.add_s(dn, user) except (ldap.TIMEOUT, ldap.SERVER_DOWN, ldap.OTHER), e: self.logger.debug('Could not create the user.') raise BackendError(str(e))
def delete_user(self, user, credentials=None): """ Deletes a user. Args: user: the user object Returns: True if the deletion was successful, False otherwise """ if credentials is not None: if not self.authenticate_user(user, credentials): return False dn = self._get_dn(user) if dn is None: return True try: with self._conn() as conn: try: res, __ = conn.delete_s(dn) except ldap.NO_SUCH_OBJECT: return False except (ldap.TIMEOUT, ldap.SERVER_DOWN, ldap.OTHER), e: self.logger.debug('Could not delete the user in ldap') raise BackendError(str(e)) except ldap.INVALID_CREDENTIALS: return False self._purge_conn(dn) return res == ldap.RES_DELETE
def get_user_info(self, user, attrs): """Returns user info Args: user_id: user id Returns: user object populated with attrs """ need = [attr for attr in attrs if not user.get(attr)] if need == []: return user dn = self._get_dn(user) if not dn: return user scope = ldap.SCOPE_BASE with self._conn() as conn: try: res = conn.search_st(dn, scope, attrlist=need, timeout=self.ldap_timeout) except (ldap.TIMEOUT, ldap.SERVER_DOWN, ldap.OTHER), e: self.logger.debug('Could not get the user info in ldap.') raise BackendError(str(e)) except ldap.NO_SUCH_OBJECT: return user
def get(self, key): with self.pool.reserve() as mc: try: return mc.get(key) except MemcachedError, err: # memcache seems down raise BackendError(str(err))
def _set_reset_code(self, user_id): code = self._generate_reset_code() key = self._generate_key(user_id) if not self._engine.set(key, code, self.expiration): raise BackendError() return code
def create_user(self, user_name, password, email): """Creates a user. Returns True on success.""" user_name = str(user_name) # XXX only ASCII user_id = self._get_next_user_id() password_hash = ssha(password) key = '%s%s' % (random.randint(0, 9999999), user_name) key = sha1(key).hexdigest() user = { 'cn': user_name, 'sn': user_name, 'uid': user_name, 'uidNumber': str(user_id), 'primaryNode': 'weave:', 'userPassword': password_hash, 'account-enabled': 'Yes', 'mail': email, 'mail-verified': key, 'objectClass': ['dataStore', 'inetOrgPerson'] } user = user.items() dn = "uidNumber=%i,%s" % (user_id, self.users_root) with self._conn(self.admin_user, self.admin_password) as conn: try: res, __ = conn.add_s(dn, user) except (ldap.TIMEOUT, ldap.SERVER_DOWN, ldap.OTHER), e: logger.debug('Could not create the user.') raise BackendError(str(e))
def update_email(self, user_id, email, password=None): """Change the user e-mail Args: user_id: user id email: new email Returns: True if the change was successful, False otherwise """ if password is None: return False # we need a password user = [(ldap.MOD_REPLACE, 'mail', [email])] #not going to change this behavior yet #user = [(ldap.MOD_REPLACE, 'mail', [email]), # (ldap.MOD_REPLACE, 'uid', [extract_username(email)]) # ] user_name = self._get_username(user_id) dn = self._get_dn(user_name) with self._conn(dn, password) as conn: try: res, __ = conn.modify_s(dn, user) except (ldap.TIMEOUT, ldap.SERVER_DOWN, ldap.OTHER), e: logger.debug('Could not update the email field in ldap.') raise BackendError(str(e))
def delete_user(self, user_id, password=None): """Deletes a user Args: user_id: user id password: user password Returns: True if the deletion was successful, False otherwise """ user_name = self._get_username(user_id) dn = self._get_dn(user_name) if password is None: return False # we need a password try: with self._conn(dn, password) as conn: try: res, __ = conn.delete_s(dn) except ldap.NO_SUCH_OBJECT: return False except (ldap.TIMEOUT, ldap.SERVER_DOWN, ldap.OTHER), e: logger.debug('Could not delete the user in ldap') raise BackendError(str(e)) except ldap.INVALID_CREDENTIALS: return False self._purge_conn(dn) return res == ldap.RES_DELETE
def authenticate_user(self, user_name, password, host=None): """Authenticates a user given a user_name and password. Returns the user id in case of success. Returns None otherwise.""" if password is None or password == '': return None dn = self._username2dn(user_name) if dn is None: # unknown user, we can return immediatly return None attrs = ['uidNumber'] if self.check_account_state: attrs.append('account-enabled') if self.check_node: attrs.append('primaryNode') try: with self._conn(dn, password) as conn: user = conn.search_st(dn, ldap.SCOPE_BASE, attrlist=attrs, timeout=self.ldap_timeout) except (ldap.NO_SUCH_OBJECT, ldap.INVALID_CREDENTIALS): return None except (ldap.TIMEOUT, ldap.SERVER_DOWN, ldap.OTHER), e: self.logger.debug('Could not authenticate the user.') raise BackendError(str(e))
def incr(self, key, size=1): size = int(size) with self.pool.reserve() as mc: try: return mc.incr(key, size) except NotFound: return mc.set(key, size) except MemcachedError, err: raise BackendError(str(err))
def delete(self, key): with self.pool.reserve() as mc: try: return mc.delete(key) except NotFound: return False except MemcachedError, err: # memcache seems down raise BackendError(str(err))
def create_user(self, username, password, email): """Creates a user. Returns True on success.""" payload = {'password': password, 'email': email} url = self.generate_url(username) status, body = self._proxy('PUT', url, payload) if status != 200: raise BackendError() # the result is the username on success return body == username
def __init__(self, config): if _NO_CAPTCHA_LIB: raise ImportError('Recaptcha lib is not installed') self.use = config.get('use', False) self.private_key = config.get('private_key') self.public_key = config.get('public_key') self.use_ssl = config.get('use_ssl', True) if self.use and (self.private_key is None or self.public_key is None): logger.error("No key defined for captcha!") raise BackendError()
def update_password(self, user_id, new_password, old_password=None, key=None): """Change the user password. Uses the admin bind or the user bind if the old password is provided. Args: user_id: user id new_password: new password old_password: old password of the user (optional) Returns: True if the change was successful, False otherwise """ user_name = self._get_username(user_id) user_dn = self._get_dn(user_name) if old_password is None: if key: #using a key, therefore we should check it if self.verify_reset_code(user_id, key): self.clear_reset_code(user_id) else: logger.error("bad key used for update password") return False # we will use admin auth dn = self.admin_user ldap_password = self.admin_password else: # user auth dn = user_dn ldap_password = old_password # we need a password password_hash = ssha(new_password) user = [(ldap.MOD_REPLACE, 'userPassword', [password_hash])] try: with self._conn(dn, ldap_password) as conn: try: res, __ = conn.modify_s(user_dn, user) except (ldap.TIMEOUT, ldap.SERVER_DOWN, ldap.OTHER), e: logger.debug('Could not update the password in ldap.') raise BackendError(str(e)) except ldap.INVALID_CREDENTIALS: return False self._purge_conn(user_dn, new_password) return res == ldap.RES_MODIFY
def admin_update_password(self, user_id, new_password, key): """Change the user password. Uses the admin bind or the user bind if the old password is provided. Args: user_id: user id new_password: new password key: password reset key Returns: True if the change was successful, False otherwise """ user_dn = self._userid2dn(user_id) if user_dn is None: raise BackendError('Unknown user "%s"' % user_id) # using a key, therefore we should check it if self.verify_reset_code(user_id, key): self.clear_reset_code(user_id) else: self.logger.error("bad key used for update password") return False password_hash = ssha(new_password) user = [(ldap.MOD_REPLACE, 'userPassword', [password_hash])] try: with self._conn(self.admin_user, self.admin_password) as conn: try: res, __ = conn.modify_s(user_dn, user) except (ldap.TIMEOUT, ldap.SERVER_DOWN, ldap.OTHER), e: self.logger.debug('Could not update the password in ldap.') raise BackendError(str(e)) except ldap.INVALID_CREDENTIALS: return False self._purge_conn(user_dn, new_password) return res == ldap.RES_MODIFY
def get_user_node(self, user_id, assign=True): if self.single_box: return None node = super(MozillaAuth, self).get_user_node(user_id, assign=False) if node is not None or assign is False: return node username = self._get_username(user_id) url = self.generate_url(username, 'node/weave') status, body = self._proxy('GET', url) if status != 200: raise BackendError() return body
def _get_dn_by_filter(self, filter): dn = self.users_root scope = ldap.SCOPE_SUBTREE with self._conn() as conn: try: user = conn.search_st(dn, scope, filterstr=filter, attrlist=[], timeout=self.ldap_timeout) except (ldap.TIMEOUT, ldap.SERVER_DOWN, ldap.OTHER), e: self.logger.debug('Could not get the user info from ldap') raise BackendError(str(e)) except ldap.NO_SUCH_OBJECT: return None
def get_user_node(self, user_id, assign=True): if self.single_box: return None user_name = self._get_username(user_id) dn = self._get_dn(user_name) # getting the list of primary nodes with self._conn() as conn: try: res = conn.search_st(dn, ldap.SCOPE_BASE, attrlist=['primaryNode'], timeout=self.ldap_timeout) except (ldap.TIMEOUT, ldap.SERVER_DOWN, ldap.OTHER), e: logger.debug('Could not get the user node in ldap') raise BackendError(str(e))
def clear_reset_code(self, user_id): """Clears the reset code Args: user_id: user id Returns: True if the change was successful, False otherwise """ # handled by sreg username = self._get_username(user_id) status, body = self._proxy( 'DELETE', self.generate_url(username, 'password_reset_code')) if status != 200: raise BackendError() return body == 0
def get_user_id(self, user_name): """Returns the id for a user name""" dn = self.users_root scope = ldap.SCOPE_SUBTREE filter = '(uid=%s)' % user_name with self._conn() as conn: try: user = conn.search_st(dn, scope, filterstr=filter, attrlist=['uidNumber'], timeout=self.ldap_timeout) except (ldap.TIMEOUT, ldap.OTHER), e: self.logger.debug('Could not get the user id from ldap.') raise BackendError(str(e)) except ldap.NO_SUCH_OBJECT: return None
def authenticate_user(self, user_name, passwd): """Authenticates a user given a user_name and password. Returns the user id in case of success. Returns None otherwise.""" dn = self._get_dn(user_name) attrs = ['uidNumber'] if self.check_account_state: attrs.append('account-enabled') try: with self._conn(dn, passwd) as conn: user = conn.search_st(dn, ldap.SCOPE_BASE, attrlist=attrs, timeout=self.ldap_timeout) except (ldap.NO_SUCH_OBJECT, ldap.INVALID_CREDENTIALS): return None except (ldap.TIMEOUT, ldap.SERVER_DOWN, ldap.OTHER), e: logger.debug('Could not authenticate the user.') raise BackendError(str(e))
def _get_username(self, user_id): """Returns the name for a user id""" dn = self.users_root if dn == 'md5': dn = self.users_base_dn scope = ldap.SCOPE_SUBTREE filter = '(uidNumber=%s)' % user_id with self._conn() as conn: try: user = conn.search_st(dn, scope, filterstr=filter, attrlist=['uid'], timeout=self.ldap_timeout) except (ldap.TIMEOUT, ldap.SERVER_DOWN, ldap.OTHER), e: logger.debug('Could not get the user info from ldap') raise BackendError(str(e)) except ldap.NO_SUCH_OBJECT: return None
def _get_dn(self, user_name=None, user_id=None): dn = self.users_root #if we already have the uid, just build it if user_id: return "uidNumber=%i,%s" % (user_id, dn) scope = ldap.SCOPE_SUBTREE filter = '(uid=%s)' % user_name with self._conn() as conn: try: user = conn.search_st(dn, scope, filterstr=filter, attrlist=[], timeout=self.ldap_timeout) except (ldap.TIMEOUT, ldap.SERVER_DOWN, ldap.OTHER), e: logger.debug('Could not get the user info from ldap') raise BackendError(str(e)) except ldap.NO_SUCH_OBJECT: return None
def generate_reset_code(self, user_id, overwrite=True): """Sends a reset code by e-mail Args: user_id: user id overwrite: if True, overwrites an existing code Returns: True if reset code was generated and sent to user, False otherwise """ username = self._get_username(user_id) status, body = self._proxy( 'GET', self.generate_url(username, 'password_reset_code')) if status == 200: return body == 0 if status == 400: if body == WEAVE_NO_EMAIL_ADRESS: raise NoEmailError() raise BackendError()
def _get_dn(self, user): """ Gets the user's dn from either their id or username Args: user: user object (may be updated as a side effect) Returns: user's dn or None if the user cannot be found """ if user.get('dn'): return user['dn'] user_id = user.get('userid') if user_id: #build it from the user id user['dn'] = "uidNumber=%s,%s" % (user_id, self.users_root) return user['dn'] user_name = user.get('username') if not user_name: #we have nothing to do a search on return None dn = self.search_root scope = ldap.SCOPE_SUBTREE filter = '(uid=%s)' % user_name attrs = ['uidNumber'] with self._conn() as conn: try: res = conn.search_st(dn, scope, filterstr=filter, attrlist=attrs, timeout=self.ldap_timeout) except (ldap.TIMEOUT, ldap.SERVER_DOWN, ldap.OTHER), e: self.logger.debug('Could not get the user info from ldap') raise BackendError(str(e)) except ldap.NO_SUCH_OBJECT: return None
def update_password(self, user_id, new_password, old_password=None, key=None): """Change the user password. Uses the admin bind or the user bind if the old password is provided. Args: user_id: user id password: new password old_password: old password of the user (optional) key: the reset code Returns: True if the change was successful, False otherwise """ if old_password is not None: return super(MozillaAuth, self).update_password(user_id, new_password, old_password=old_password) if not key: logger.error("Calling update password without password or key") return False payload = {'reset_code': key, 'password': new_password} username = self._get_username(user_id) url = self.generate_url(username, 'password') status, body = self._proxy('POST', url, payload) if status == 200: return body == 0 elif status == 400: if body == WEAVE_INVALID_RESET_CODE: raise InvalidCodeError() raise BackendError()
def authenticate_user(self, user, credentials, attrs=None): """Authenticates a user given a user_name and credentials. Returns the user id in case of success. Returns None otherwise. """ username = credentials.get("username") if username is None: return None if user.get("username") is None: user["username"] = username elif user.get("username") != username: return None password = credentials.get("password") if not password: return None dn = self._get_dn(user) if not dn: return None if attrs is None: attrs = [] if self.check_account_state and 'account-enabled' not in attrs: attrs.append('account-enabled') try: with self._conn(dn, password) as conn: result = conn.search_st(dn, ldap.SCOPE_BASE, attrlist=attrs, timeout=self.ldap_timeout) except (ldap.NO_SUCH_OBJECT, ldap.INVALID_CREDENTIALS): return None except (ldap.TIMEOUT, ldap.SERVER_DOWN, ldap.OTHER), e: self.logger.debug('Could not authenticate the user.') raise BackendError(str(e))
def _modify_record(self, user, key, value, ldap_user=None, ldap_pass=None): """ Change a value in the user's account. Uses the account passed in with ldap_user and ldap_pass. Args: user: user object key: field in ldap to be changed value: value to change the field to ldap_user, ldap_pass: bind information (admin or user) Returns: True if the change was successful, False otherwise """ if ldap_user is not None and ldap_pass is None: return False dn = self._get_dn(user) if dn is None: return False action = [(ldap.MOD_REPLACE, key, value)] try: with self._conn(ldap_user, ldap_pass) as conn: try: res, __ = conn.modify_s(dn, action) except (ldap.TIMEOUT, ldap.SERVER_DOWN, ldap.OTHER), e: self.logger.debug('Could not update the password in ldap.') raise BackendError(str(e)) except ldap.INVALID_CREDENTIALS: return False if res != ldap.RES_MODIFY: return False user[key] = value return True