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 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: logger.debug('Could not authenticate the user.') raise BackendError(str(e))
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: 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_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: logger.debug('Could not get the user info in ldap.') raise BackendError(str(e)) except ldap.NO_SUCH_OBJECT: return user
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: 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 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 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 _set_reset_code(self, user_id): code, expiration = generate_reset_code() query = update(users).values(reset=code, reset_expiration=expiration) res = safe_execute(self._engine, query.where(users.c.id == user_id)) if res.rowcount != 1: logger.debug('Unable to add a new reset code') return None # XXX see if appropriate return code
def _set_reset_code(self, user_id): rc = ResetCode() code = rc._generate_reset_code() expiration = datetime.datetime.now() + datetime.timedelta(hours=6) query = update(users).values(reset=code, reset_expiration=expiration) res = safe_execute(self._engine, query.where(users.c.id == user_id)) if res.rowcount != 1: logger.debug("Unable to add a new reset code") return None # XXX see if appropriate return code
def get_association_by_site(self, site_id, request): # look up the user by site_id where = and_(site_id_to_uid.c.site_id == site_id, site_id_to_uid.c.site == site_loc) query = select([site_id_to_uid]).where(where) res = self._db.execute(query) res = res.fetchone() if res is None: logger.debug("No data for %s at %s" % (site_id, site_loc)) return None # return a json object containing the response. return self.row_to_dict(res)
def set_user_info(self, uid=None, pemail="", sname="", fname="", avatar="", nickname="", poco_server="", **kw): if uid is None: uid = self.uid query = insert(user_info).values( uid=uid, pemail=pemail, sname=sname, fname=fname, avatar=avatar, nickname=nickname, poco_server=poco_server ) res = self._db.execute(query) if res.rowcount != 1: logger.debug("Unable to add user info ") return False return True
def __gather_recipes(location): recipes = [] if os.path.isfile(location): return YmlReader(location).read() if os.path.isdir(location): for dir in os.listdir(location): logger.debug('recipe is %s' % os.path.join(location, dir)) recipes.append(YmlReader(os.path.join(location, dir)).read()) return recipes
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 delete_password_reset(self, request, **data): """Forces a password reset clear""" if self.reset is None: logger.debug('reset attempted, but no resetcode library installed') raise HTTPServiceUnavailable() self._check_captcha(request, data) self.auth.get_user_id(request.user) self.reset.clear_reset_code(request.user) log_cef("User requested password reset clear", 9, request.environ, self.app.config, request.user.get('username'), PASSWD_RESET_CLR) return text_response('success')
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: logger.debug('Could not get the user info from ldap') raise BackendError(str(e)) except ldap.NO_SUCH_OBJECT: return None
def __init__(self, app): self.app = app self.strict_usernames = app.config.get('auth.strict_usernames', True) self.shared_secret = app.config.get('global.shared_secret') self.auth = self.app.auth.backend self.fallback_node = \ self.clean_location(app.config.get('nodes.fallback_node')) try: self.reset = load_and_configure(app.config, 'reset_codes') except Exception: logger.debug(traceback.format_exc()) logger.debug("No reset code library in place") self.reset = None
def _set_reset_code(self, user_id): code = self.rc._generate_reset_code() expiration = datetime.datetime.now() + datetime.timedelta(hours=6) query = delete(reset_codes).where(reset_codes.c.username == user_id) self._engine.execute(query) query = insert(reset_codes).values(reset=code, expiration=expiration, username=user_id) res = safe_execute(self._engine, query) if res.rowcount != 1: logger.debug("Unable to add a new reset code in the" " reset_code table") return None # XXX see if appropriate return code
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 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: logger.debug('Could not get the user id from ldap.') raise BackendError(str(e)) except ldap.NO_SUCH_OBJECT: return None
def create_user(self, user_name, password, email): """Creates a user. Returns a user object on success.""" user_name = str(user_name) # XXX only ASCII #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: logger.debug('Could not create the user.') raise BackendError(str(e))
def _set_reset_code(self, user_id): code, expiration = generate_reset_code() query = delete(reset_codes).where(reset_codes.c.username == user_id) self._engine.execute(query) query = insert(reset_codes).values(reset=code, expiration=expiration, username=user_id) res = safe_execute(self._engine, query) if res.rowcount != 1: logger.debug('Unable to add a new reset code in the' ' reset_code table') return None # XXX see if appropriate return code
def password_reset(self, request, **data): """Sends an e-mail for a password reset request.""" if self.reset is None: logger.debug('reset attempted, but no resetcode library installed') raise HTTPServiceUnavailable() user_id = self.auth.get_user_id(request.user) if user_id is None: # user not found raise HTTPJsonBadRequest(ERROR_INVALID_USER) self.auth.get_user_info(request.user, ['mail']) if request.user.get('mail') is None: raise HTTPJsonBadRequest(ERROR_NO_EMAIL_ADDRESS) self._check_captcha(request, data) try: # the request looks fine, let's generate the reset code code = self.reset.generate_reset_code(request.user) data = { 'host': request.host_url, 'user_name': request.user['username'], 'code': code } body = render_mako('password_reset_mail.mako', **data) sender = request.config['smtp.sender'] host = request.config['smtp.host'] port = int(request.config['smtp.port']) user = request.config.get('smtp.user') password = request.config.get('smtp.password') subject = 'Resetting your Services password' res, msg = send_email(sender, request.user['mail'], subject, body, host, port, user, password) if not res: raise HTTPServiceUnavailable(msg) except AlreadySentError: #backend handled the reset code email. Keep going pass return text_response('success')
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 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 password_reset(self, request, **data): """Sends an e-mail for a password reset request.""" if self.reset is None: logger.debug('reset attempted, but no resetcode library installed') raise HTTPServiceUnavailable() user_id = self.auth.get_user_id(request.user) if user_id is None: # user not found raise HTTPJsonBadRequest(ERROR_INVALID_USER) self.auth.get_user_info(request.user, ['mail']) if request.user.get('mail') is None: raise HTTPJsonBadRequest(ERROR_NO_EMAIL_ADDRESS) self._check_captcha(request, data) try: # the request looks fine, let's generate the reset code code = self.reset.generate_reset_code(request.user) data = {'host': request.host_url, 'user_name': request.user['username'], 'code': code} body = render_mako('password_reset_mail.mako', **data) sender = request.config['smtp.sender'] host = request.config['smtp.host'] port = int(request.config['smtp.port']) user = request.config.get('smtp.user') password = request.config.get('smtp.password') subject = 'Resetting your Services password' res, msg = send_email(sender, request.user['mail'], subject, body, host, port, user, password) if not res: raise HTTPServiceUnavailable(msg) except AlreadySentError: #backend handled the reset code email. Keep going pass return text_response('success')
def get_user_id(self, user_name): """Returns the id for a user name""" dn = self.users_root if dn == 'md5': dn = self.users_base_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=['uidNumber'], timeout=self.ldap_timeout) except (ldap.TIMEOUT, ldap.OTHER), e: logger.debug('Could not get the user id from ldap.') raise BackendError(str(e)) except ldap.NO_SUCH_OBJECT: return None
def set_site_association(self, site_id, request, uid=None, secret="", permissions={}, **kw): # record the site info. if uid is None: uid = self.uid site_loc = self._site_loc(request) parms = { "site": unicode(site_loc), "uid": uid, "site_id": unicode(site_id), "secret": unicode(secret), "created": int(time.time()), "accessed": int(time.time()), "state": 1, "permissions": self.as_permission(permissions), } query = insert(site_id_to_uid).values(**parms) res = self._db.execute(query) if res.rowcount != 1: logger.debug("Unable to add site association for %s at %s" % uid, site_loc) return None return parms
def test_graberrors(self): # simpler case: services logger, error level with capture_logs() as errors: logger.error("Yeah") self.assertEqual(errors.read(), "Yeah\n") # services logger, warning level with capture_logs(level=logging.WARNING) as wrn: logger.debug("Yeah") logger.warning("Yeah2") self.assertEqual(wrn.read(), "Yeah2\n") # root logger, warning root = logging.getLogger() with capture_logs(logger="root", level=logging.WARNING) as wrn: root.debug("Yeah") root.warning("Yeah2") self.assertEqual(wrn.read(), "Yeah2\n")
def get_user_info(self, user_id): """Returns user info Args: user_id: user id Returns: tuple: username, email """ user_name = self._get_username(user_id) dn = self._get_dn(user_name) scope = ldap.SCOPE_BASE with self._conn() as conn: try: res = conn.search_st(dn, scope, attrlist=['mail'], timeout=self.ldap_timeout) except (ldap.TIMEOUT, ldap.SERVER_DOWN, ldap.OTHER), e: logger.debug('Could not get the user info in ldap.') raise BackendError(str(e)) except ldap.NO_SUCH_OBJECT: return None, None
def do_password_reset(self, request): """Do a password reset.""" if self.reset is None: logger.debug('reset attempted, but no resetcode library installed') raise HTTPServiceUnavailable() user_name = request.POST.get('username') if user_name is not None: user_name = extract_username(user_name) if request.POST.keys() == ['username']: # setting up a password reset # XXX add support for captcha here via **data request.user = User(user_name) try: self.password_reset(request) except (HTTPServiceUnavailable, HTTPJsonBadRequest), e: return render_mako('password_failure.mako', error=e.detail) else: return render_mako('password_key_sent.mako') raise HTTPJsonBadRequest()
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: 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: 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 _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: 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
def write(self, path, data): logger.debug("git url for push! %s" % path) DeployLogGitRepository.__write_service_file(path, data) self.git_client.check_in()
return '%s://%s/' % (self.nodes_scheme, node) if not assign: return None # the user don't have a node yet, let's pick the most bored node where = and_(available_nodes.c.available_assignments > 0, available_nodes.c.downed == 0) query = select([available_nodes]).where(where) query = query.order_by(available_nodes.c.actives).limit(1) res = self._engine.execute(query) res = res.fetchone() if res is None: # unable to get a node logger.debug('Unable to get a node for user id: %s' % str(user_id)) raise NodeAttributionError(user_id) node = str(res.node) available = res.available_assignments actives = res.actives # updating LDAP now user = [(ldap.MOD_REPLACE, 'primaryNode', ['weave:%s' % node])] with self._conn(self.admin_user, self.admin_password) as conn: try: ldap_res, __ = conn.modify_s(dn, user) except (ldap.TIMEOUT, ldap.SERVER_DOWN, ldap.OTHER), e: logger.debug('Could not update the server node in LDAP') raise BackendError(str(e))