def check_crendential(l, r): # Check results if len(r) != 1: logger.debug("user [%s] not found in LDAP", username) return None # Bind using the user credentials. Throws an exception in case of # error. l.simple_bind_s(r[0][0], password) try: logger.info("user [%s] found in LDAP", username) # Verify the shadow expire if self.check_shadow_expire: shadow_expire = self._attr_shadow_expire(r) # Convert nb. days into seconds. if shadow_expire and shadow_expire * 24 * 60 * 60 < time.time(): logger.warn("user account %s expired: %s", username, shadow_expire) raise RdiffError(_('User account %s expired.' % username)) # Get username dn = r[0][0] new_username = self._decode(r[0][1][self.attribute][0]) # Verify if the user is member of the required group if self.require_group: value = dn if self.group_attribute_is_dn else new_username logger.info("check if user [%s] is member of [%s]", value, self.require_group) if not l.compare_s(self.require_group, self.group_attribute, value): raise RdiffError(_('Permissions denied for user account %s.' % username)) finally: l.unbind_s() # Return the username return new_username
def _handle_add(self, filename, **kwargs): """ Called to add a new key to an authorized_keys file. """ assert 'key' in kwargs, "key is missing" # Validate the content of the key. try: key = authorizedkeys.check_publickey(kwargs['key']) except: raise RdiffWarning(_("Invalid SSH key.")) # Check if already exists if authorizedkeys.exists(filename, key): raise RdiffWarning(_("SSH key already exists.")) # Check size. if key.size and key.size < 2048: raise RdiffWarning(_("SSH key is too short. RSA key of at least 2048 bits is required.")) # Add comment to the key. comment = key.comment if 'title' in kwargs: comment = kwargs['title'].strip() key = authorizedkeys.KeySplit( lineno=key.lineno, options=key.options, keytype=key.keytype, key=key.key, comment=comment) # Add key to file _logger.info("add key [%s] to [%s]", key, filename) authorizedkeys.add(filename, key)
def render_prefs_panel(self, panelid, **kwargs): # @UnusedVariable # Get user root directory user_root = self.app.userdb.get_user_root(self.app.currentuser.username) user_root_b = encode_s(user_root) filename = os.path.join(user_root_b, b'.ssh', b'authorized_keys') # Handle action params = {} if 'action' in kwargs: try: action = kwargs['action'] if action == 'add': self._handle_add(filename, **kwargs) elif action == 'delete': self._handle_delete(filename, **kwargs) except ValueError as e: params['error'] = unicode(e) except Exception as e: _logger.warn("unknown error processing action", exc_info=True) params['error'] = _("Unknown error") # Get SSH keys if file exists. params["sshkeys"] = [] if os.access(filename, os.R_OK): try: params["sshkeys"] = [ {'title': key.comment or (key.keytype + ' ' + key.key[:18]), 'fingerprint': key.fingerprint, 'lineno': key.lineno} for key in authorizedkeys.read(filename)] except IOError: params['error'] = _("error reading SSH keys file") _logger.warn("error reading SSH keys file [%s]", filename) return "prefs_sshkeys.html", params
def _handle_set_profile_info(self, **kwargs): """ Called when changing user profile. """ # Check data. if 'email' not in kwargs: raise RdiffWarning(_("Email is undefined.")) # Check if email update is supported if not self.app.userdb.supports('set_email'): return {'error': _("Email update is not supported.")} # Parse the email value to extract a valid email. The following method # return an empty string if the email is not valid. This RFC also accept # local email address without '@'. So we add verification for '@' if not PATTERN_EMAIL.match(kwargs['email'].lower()): raise RdiffWarning(_("Invalid email.")) # Update the user's email assert self.app.currentuser username = self.app.currentuser.username email = kwargs['email'] _logger.info("updating user [%s] email [%s]", username, email) self.app.currentuser.email = kwargs['email'] return {'success': _("Profile updated successfully.")}
def _handle_set_password(self, **kwargs): """ Called when changing user password. """ if 'current' not in kwargs or not kwargs['current']: raise RdiffWarning(_("Current password is missing.")) if 'new' not in kwargs or not kwargs['new']: raise RdiffWarning(_("New password is missing.")) if 'confirm' not in kwargs or not kwargs['confirm']: raise RdiffWarning(_("Confirmation password is missing.")) # Check if confirmation is valid. if kwargs['new'] != kwargs['confirm']: return { 'error': _("The new password and its confirmation do not match.") } # Update user password user = self.app.currentuser.username _logger.info("updating user [%s] password", user) self.app.userdb.set_password(user, kwargs['new'], old_password=kwargs['current']) return {'success': _("Password updated successfully.")}
def _handle_set_profile_info(self, **kwargs): """ Called when changing user profile. """ # Check data. if 'email' not in kwargs: raise ValueError(_("email is undefined")) # Check if email update is supported if not self.app.userdb.supports('set_email'): return {'error': _("Email update is not supported.")} # Parse the email value to extract a valid email. The following method # return an empty string if the email is not valid. This RFC also accept # local email address without '@'. So we add verification for '@' if not PATTERN_EMAIL.match(kwargs['email'].lower()): raise ValueError(_("invalid email")) # Update the user's email if not self.app.currentuser: raise RdiffError(_("invalid state")) username = self.app.currentuser.username email = kwargs['email'] _logger.info("updating user [%s] email [%s]", username, email) self.app.userdb.set_email(username, kwargs['email']) return {'success': _("Profile updated successfully.")}
def index(self, path=b'', new_encoding=None): """ Update repository encoding via Ajax. """ self.assertIsInstance(path, bytes) self.assertTrue(new_encoding) _logger.debug("update repo [%r] settings [%r]", path, new_encoding) # Check user permissions repo_obj = self.validate_user_path(path)[0] # Validate the encoding value new_codec = encodings.search_function(new_encoding.lower()) if not new_codec: raise cherrypy.HTTPError(400, _("invalid encoding value")) new_encoding = new_codec.name if not isinstance(new_encoding, str): # Python 2 new_encoding = new_encoding.decode('ascii') # Update the repository encoding _logger.info("updating repository [%s] encoding [%s]", repo_obj, new_encoding) repo_obj.set_encoding(new_encoding) return _("Updated")
def _users_handle_action(self, action, username, email, password, user_root, is_admin): success = "" # We need to change values. Change them, then give back that main # page again, with a message if username == self.app.currentuser.username: # Don't allow the user to changes it's "admin" state. is_admin = self.app.currentuser.is_admin is_admin = str(is_admin).lower() in ['true', '1'] # Fork the behaviour according to the action. if action == "edit": user = self.app.userdb.get_user(username) logger.info("updating user [%s] info", user) if password: self.app.userdb.set_password(username, password, old_password=None) user.user_root = user_root user.is_admin = is_admin # Avoid updating the email fields is it didn'T changed. see pdsl/minarca#187 if email != user.email: user.email = email success = _("User information modified successfully.") # Check and update user directory self._check_user_root_dir(user_root) rdw_spider_repos.find_repos_for_user(user) elif action == "add": if username == "": raise RdiffWarning(_("The username is invalid.")) logger.info("adding user [%s]", username) user = self.app.userdb.add_user(username, password) user.user_root = user_root user.is_admin = is_admin user.email = email # Check and update user directory self._check_user_root_dir(user_root) rdw_spider_repos.find_repos_for_user(user) success = _("User added successfully.") if action == "delete": user = self.app.userdb.get_user(username) if username == self.app.currentuser.username: raise RdiffWarning(_("You cannot remove your own account!.")) logger.info("deleting user [%s]", username) self.app.userdb.delete_user(user) success = _("User account removed.") # Return messages return {'success': success}
def check_username_and_password(self, username, password): """Validate user credentials.""" logger.debug("check credentials for [%s]", username) try: userobj = cherrypy.request.app.userdb.login(username, password) # @UndefinedVariable except: logger.exception("fail to validate user credential.",) raise RdiffWarning(_("Fail to validate user credential.")) if not userobj: logger.warning("invalid username or password") raise RdiffWarning(_("Invalid username or password.")) return userobj
def set_password(self, username, password, old_password=None): assert isinstance(username, str) assert old_password is None or isinstance(old_password, str) assert isinstance(password, str) if not password: raise RdiffError(_("Password can't be empty.")) # Check old password value. if old_password and not self.are_valid_credentials(username, old_password): raise RdiffError(_("Wrong password.")) # Update password. self._set_user_field(username, 'Password', self._hash_password(password))
def check_username_and_password(self, username, password): """Validate user credentials.""" logger.info("check credentials for [%s]", username) try: userobj = cherrypy.request.app.userdb.login( username, password) # @UndefinedVariable except: logger.exception("fail to validate user credential") raise RdiffWarning(_("Fail to validate user credential.")) if not userobj: logger.warning("invalid username [%s] or password", username) raise RdiffWarning(_("Invalid username or password.")) return userobj
def _handle_set_encoding(self, repo_obj, **kwargs): """ Change the encoding of the repository. """ # Validate the encoding value new_encoding = kwargs.get('encoding') new_encoding = unicode(encodings.normalize_encoding(new_encoding)).lower() if new_encoding not in self._get_encodings(): raise ValueError(_("invalid encoding value")) # Update the repository encoding _logger.info("updating repository [%s] encoding [%s]", repo_obj, new_encoding) repo_obj.set_encoding(new_encoding) return {'success': _("Repository updated successfully with new encoding.")}
def set_password(self, username, password, old_password=None): assert isinstance(username, str) assert old_password is None or isinstance(old_password, str) assert isinstance(password, str) if not password: raise RdiffError(_("Password can't be empty.")) # Check old password value. if old_password and not self.are_valid_credentials( username, old_password): raise RdiffError(_("Wrong password.")) # Update password. self._set_user_field(username, 'Password', self._hash_password(password))
def do_check(self): """Assert username. Raise redirect, or return True if request handled.""" request = cherrypy.serving.request response = cherrypy.serving.response if not self.is_login(): url = cherrypy.url(qs=request.query_string) # If browser requesting text/plain. It's probably an Ajax call, don't # redirect and raise an exception. mtype = cherrypy.tools.accept.callable(['text/html', 'text/plain']) # @UndefinedVariable if mtype == 'text/plain': logger.debug('No username, requesting plain text, routing to 403 error from_page %(url)r', locals()) raise cherrypy.HTTPError(403, _("Not logged in")) logger.debug('No username, routing to login_screen with from_page %(url)r', locals()) response.body = self.login_screen(url) if "Content-Length" in response.headers: # Delete Content-Length header so finalize() recalcs it. del response.headers["Content-Length"] return True # Define the value of request.login to later in code we can reuse it. username = cherrypy.session[self.session_key] # @UndefinedVariable userobj = cherrypy.request.app.userdb.get_user(username) # @UndefinedVariable if not userobj: raise cherrypy.HTTPError(403) logger.debug('Setting request.login to %r', userobj) cherrypy.serving.request.login = userobj
def set_password(self, user, password, old_password=None): # Check if user exists in database db = self.find_user_database(user) if not db: raise InvalidUserError(user) # Try to update the user password. store = self.find_user_store(user) if store and not store.supports('set_password'): raise RdiffError(_("""The authentication backend for user %s does not support setting the password""" % user)) elif not store: store = self._get_supporting_store('set_password') if not store: raise RdiffError(_("none of the IPasswordStore supports setting the password")) store.set_password(user, password, old_password) self._notify('password_changed', user, password)
def index(self, path=b"", limit='10', **kwargs): self.assertIsInstance(path, bytes) self.assertIsInt(limit) limit = int(limit) logger.debug("history [%r]", path) repo_obj = self.validate_user_path(path)[0] assert isinstance(repo_obj, librdiff.RdiffRepo) # Set up warning about in-progress backups, if necessary warning = False status = repo_obj.status if status[0] != 'ok': warning = status[1] + ' ' + _("The displayed data may be inconsistent.") parms = { "limit": limit, "repo_name": repo_obj.display_name, "repo_path": repo_obj.path, "history_entries": repo_obj.get_history_entries(numLatestEntries=limit, reverse=True), "warning": warning, } return self._compile_template("history.html", **parms)
def _handle_update_repos(self): """ Called to refresh the user repos. """ rdw_spider_repos.find_repos_for_user(self.app.currentuser.username, self.app.userdb) return {'success': _("Repositories successfully updated.")}
def render_prefs_panel(self, panelid, **kwargs): # @UnusedVariable # Process the parameters. params = dict() action = kwargs.get('action') if action: try: if action == "set_profile_info": params = self._handle_set_profile_info(**kwargs) elif action == "set_password": params = self._handle_set_password(**kwargs) elif action == "update_repos": params = self._handle_update_repos() else: _logger.info("unknown action: %s", action) raise cherrypy.NotFound("Unknown action") except RdiffWarning as e: params['warning'] = str(e) except RdiffError as e: params['error'] = str(e) except Exception as e: _logger.warning("unknown error processing action", exc_info=True) params['error'] = _("Unknown error") user = self.app.currentuser.username params.update({ 'email': self.app.currentuser.email, 'supports_set_email': self.app.userdb.supports('set_email', user), 'supports_set_password': self.app.userdb.supports('set_password', user), }) return "prefs_general.html", params
def do_check(self): """Assert username. Raise redirect, or return True if request handled.""" request = cherrypy.serving.request response = cherrypy.serving.response if not self.is_login(): url = cherrypy.url(qs=request.query_string) # If browser requesting text/plain. It's probably an Ajax call, don't # redirect and raise an exception. mtype = cherrypy.tools.accept.callable(['text/html', 'text/plain' ]) # @UndefinedVariable if mtype == 'text/plain': logger.debug( 'No username, requesting plain text, routing to 403 error from_page %(url)r', locals()) raise cherrypy.HTTPError(403, _("Not logged in")) logger.debug( 'No username, routing to login_screen with from_page %(url)r', locals()) response.body = self.login_screen(url) if "Content-Length" in response.headers: # Delete Content-Length header so finalize() recalcs it. del response.headers["Content-Length"] return True # Define the value of request.login to later in code we can reuse it. username = cherrypy.session[self.session_key] # @UndefinedVariable userobj = cherrypy.request.app.userdb.get_user( username) # @UndefinedVariable if not userobj: raise cherrypy.HTTPError(403) logger.debug('Setting request.login to %r', userobj) cherrypy.serving.request.login = userobj
def render_prefs_panel(self, panelid, **kwargs): # @UnusedVariable # Process the parameters. params = dict() action = kwargs.get('action') if action: try: if action == "set_profile_info": params = self._handle_set_profile_info(**kwargs) elif action == "set_password": params = self._handle_set_password(**kwargs) elif action == "update_repos": params = self._handle_update_repos() else: _logger.info("unknown action: %s", action) raise cherrypy.NotFound("Unknown action") except RdiffError as e: params['error'] = unicode(e) except ValueError as e: params['error'] = unicode(e) except Exception as e: _logger.warn("unknown error processing action", exc_info=True) params['error'] = _("Unknown error") user = self.app.currentuser.username params.update({ 'email': self.app.currentuser.email, 'supports_set_email': self.app.userdb.supports('set_email', user), 'supports_set_password': self.app.userdb.supports('set_password', user), }) return "prefs_general.html", params
def send_notifications(self): """ Loop trough all the user repository and send notifications. """ now = librdiff.RdiffTime() def _user_repos(): """Return a generator trought user repos to be notified.""" for user in self.app.userdb.list(): # Check if user has email. if not user.email: continue # Identify old repo for current user. old_repos = [] for repo in user.repo_list: # Check if repo has age configured (in days) maxage = repo.maxage if not maxage or maxage <= 0: continue # Check repo age. r = librdiff.RdiffRepo(user.user_root, repo.name) if r.last_backup_date < (now - datetime.timedelta(days=maxage)): old_repos.append(r) # Return an item only if user had old repo if old_repos: yield user, old_repos # For each candidate, send mail. for user, repos in _user_repos(): parms = {'user': user, 'repos': repos} self.send_mail(user, _('Notification'), 'email_notification.html', **parms)
def render_prefs_panel(self, panelid, **kwargs): # @UnusedVariable # Process the parameters. params = dict() action = kwargs.get('action') if action: try: if action == "set_notification_info": self._handle_set_notification_info(**kwargs) else: _logger.info("unknown action: %s", action) raise cherrypy.NotFound("Unknown action") except RdiffWarning as e: params['warning'] = str(e) except RdiffError as e: params['error'] = str(e) except Exception as e: _logger.warning("unknown error processing action", exc_info=True) params['error'] = _("Unknown error") params.update({ 'email': self.app.currentuser.email, 'repos': [ {'name': r.name, 'maxage': r.maxage} for r in self.app.currentuser.repo_list], }) return "prefs_notification.html", params
def _handle_delete(self, filename, **kwargs): """ Called for delete a key from an authorized_keys file. """ # Check if key is valid. if 'key' not in kwargs: raise ValueError(_("key is missing")) try: lineno = int(kwargs['key']) except ValueError: raise ValueError(_("key is invalid")) # Remove the key _logger.info("removing key [%s] from [%s]", lineno, filename) authorizedkeys.remove(filename, lineno)
def index(self, path=b"", limit='10', **kwargs): self.assertIsInstance(path, bytes) self.assertIsInt(limit) limit = int(limit) logger.debug("history [%r]", path) repo_obj = self.validate_user_path(path)[0] assert isinstance(repo_obj, librdiff.RdiffRepo) # Set up warning about in-progress backups, if necessary warning = False status = repo_obj.status if status[0] != 'ok': warning = status[1] + ' ' + _( "The displayed data may be inconsistent.") parms = { "limit": limit, "repo_name": repo_obj.display_name, "repo_path": repo_obj.path, "history_entries": repo_obj.get_history_entries(numLatestEntries=limit, reverse=True), "warning": warning, } return self._compile_template("history.html", **parms)
def users(self, userfilter=u"", usersearch=u"", action=u"", username=u"", email=u"", password=u"", user_root=u"", is_admin=u""): # Check if user is an administrator if not self.app.currentuser or not self.app.currentuser.is_admin: raise cherrypy.HTTPError(403) self.assertIsInstance(userfilter, str) self.assertIsInstance(usersearch, str) # If we're just showing the initial page, just do that params = {} if self._is_submit(): try: params = self._users_handle_action(action, username, email, password, user_root, is_admin) except RdiffWarning as e: params['warning'] = str(e) except RdiffError as e: params['error'] = str(e) except Exception as e: logger.warning("unknown error", exc_info=True) params['error'] = _("Unknown error") # Get page parameters params.update( self._users_get_params_for_page(userfilter, usersearch)) # Build users page return self._compile_template("admin_users.html", **params)
def default(self, path=b"", date=None, kind=None, usetar=None): self.assertIsInstance(path, bytes) self.assertIsInstance(date, str) self.assertTrue(kind is None or kind in ARCHIVERS) self.assertTrue(usetar is None or isinstance(usetar, str)) logger.debug("restoring [%r][%s]", path, date) # The path wont have leading and trailing "/". (path, file_b) = os.path.split(path) if not path: path = file_b file_b = b"" # Check user access to repo / path. (repo_obj, path_obj) = self.validate_user_path(path) # Get the restore date try: rdw_helpers.rdwTime(int(date)) except: logger.warning("invalid date %s", date) raise cherrypy.HTTPError(400, _("Invalid date.")) # Get if backup in progress if repo_obj.in_progress: raise cherrypy.HTTPError( 500, _("""A backup is currently in progress to this repository. Restores are disabled until this backup is complete.""" )) # Determine the kind. kind = kind or 'zip' if usetar is not None: kind = 'tar.gz' # Restore file(s) filename, fileobj = path_obj.restore(file_b, int(date), kind=kind) # Define content-disposition. cherrypy.response.headers[ "Content-Disposition"] = self._content_disposition(filename) # Stream the data. return _serve_fileobj(fileobj, content_type=None, content_length=None)
def render_prefs_panel(self, panelid, **kwargs): # @UnusedVariable # Get user root directory filename = None user_root = self.app.currentuser.user_root if user_root: filename = os.path.join(user_root, '.ssh', 'authorized_keys') # Handle action params = {} if 'action' in kwargs: try: action = kwargs['action'] if action == 'add': self._handle_add(filename, **kwargs) elif action == 'delete': self._handle_delete(filename, **kwargs) except RdiffWarning as e: params['warning'] = str(e) except RdiffError as e: params['error'] = str(e) except Exception as e: _logger.warning("unknown error processing action", exc_info=True) params['error'] = _("Unknown error") # Get SSH keys if file exists. params["sshkeys"] = [] if filename: try: params["sshkeys"] = [{ 'title': key.comment or (key.keytype + ' ' + key.key[:18]), 'fingerprint': key.fingerprint, 'lineno': key.lineno } for key in authorizedkeys.read(filename)] except IOError: params['error'] = _("error reading SSH keys file") _logger.warning("error reading SSH keys file [%s]", filename) else: params['error'] = _("error reading SSH keys file") _logger.warning("SSH keys file [%s] is not accessible", filename) return "prefs_sshkeys.html", params
def filter_data(self, template_name, data): if data.get('repo_path'): # Add our graph item in repo_nav_bar # id, label, url, icon data.setdefault('repo_nav_bar_extras', []).append( ('graphs', _('Graphs'), url_for_graphs(data.get('repo_path'), 'activities'), 'icon-chart-bar'))
def set_password(self, username, password, old_password=None): """Update the password of the given user.""" assert isinstance(username, str) assert old_password is None or isinstance(old_password, str) assert isinstance(password, str) # Do nothing if password is empty if not password: raise RdiffError(_("Password can't be empty.")) # Check if users are allowed to change their password in LDAP. if not self.allow_password_change: raise RdiffError(_("LDAP users are not allowed to change their password.")) # Check if old_password id valid if old_password and not self.are_valid_credentials(username, old_password): raise RdiffError(_("Wrong password.")) # Update the username password of the given user. If possible. return self._set_password_in_ldap(username, old_password, password)
def user_password_changed(self, username, password): """ Implementation of IUserChangeListener interface. """ # get User object (to get email) userobj = self.app.userdb.get_user(username) assert userobj # If the email attributes was changed, send a mail notification. self.send_mail(userobj, _("Password changed"), "password_changed.html")
def set_password(self, username, password, old_password=None): """Update the password of the given user.""" assert isinstance(username, unicode) assert old_password is None or isinstance(old_password, unicode) assert isinstance(password, unicode) # Do nothing if password is empty if not password: raise ValueError(_("password can't be empty")) # Check if users are allowed to change their password in LDAP. if not self.allow_password_change: raise RdiffError(_("LDAP users are not allowed to change their password.")) # Check if old_password id valid if old_password and not self.are_valid_credentials(username, old_password): raise ValueError(_("wrong password")) # Update the username password of the given user. If possible. return self._set_password_in_ldap(username, old_password, password)
def _execute(self, username, function): assert isinstance(username, str) """Reusable method to run LDAP operation.""" assert self.uri, "LdapUri must be define in configuration" assert self.base_dn, "LdapBaseDn must be define in configuration" if self.scope == "base": scope = ldap.SCOPE_BASE elif self.scope == "onelevel": scope = ldap.SCOPE_ONELEVEL else: scope = ldap.SCOPE_SUBTREE # try STARTLS if configured if self.tls: ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER) # Check LDAP credential only. l = ldap.initialize(self.uri) # Set v2 or v3 if self.version == 2: l.protocol_version = ldap.VERSION2 else: l.protocol_version = ldap.VERSION3 try: # Bind to the LDAP server logger.debug("binding to ldap server {}".format(self.uri)) l.simple_bind_s(self.bind_dn, self.bind_password) # Search the LDAP server search_filter = "(&{}({}={}))".format( self.filter, self.attribute, username) logger.debug("search ldap server: {}/{}?{}?{}?{}".format( self.uri, self.base_dn, self.attribute, scope, search_filter)) r = l.search_s(self.base_dn, scope, search_filter) # Execute operation return function(l, r) except ldap.LDAPError as e: l.unbind_s() # Handle the LDAP exception and build a nice user message. logger.warning('ldap error', exc_info=1) msg = _("An LDAP error occurred: %s") ldap_msg = str(e) if hasattr(e, 'message') and isinstance(e.message, dict): if 'desc' in e.message: ldap_msg = e.message['desc'] if 'info' in e.message: ldap_msg = e.message['info'] raise RdiffError(msg % ldap_msg)
def set_password(self, user, password, old_password=None): # Check if user exists in database db = self.find_user_database(user) if not db: raise InvalidUserError(user) # Try to update the user password. store = self.find_user_store(user) if store and not store.supports('set_password'): logger.warn( "authentication backend for user [%s] does not support changing the password", user) raise RdiffError(_("You cannot change the user's password.")) elif not store: store = self._get_supporting_store('set_password') if not store: logger.warn( "none of the IPasswordStore supports setting the password") raise RdiffError(_("You cannot change the user's password.")) store.set_password(user, password, old_password) self._notify('password_changed', user, password)
def _handle_set_password(self, **kwargs): """ Called when changing user password. """ if 'current' not in kwargs or not kwargs['current']: raise ValueError(_("current password is missing")) if 'new' not in kwargs or not kwargs['new']: raise ValueError(_("new password is missing")) if 'confirm' not in kwargs or not kwargs['confirm']: raise ValueError(_("confirmation password is missing")) # Check if confirmation is valid. if kwargs['new'] != kwargs['confirm']: return {'error': _("The new password and its confirmation does not matches.")} # Update user password user = self.app.currentuser.username _logger.info("updating user [%s] password", user) self.app.userdb.set_password(user, kwargs['new'], old_password=kwargs['current']) return {'success': _("Password updated successfully.")}
def default(self, path=b"", date=None, kind=None, usetar=None): self.assertIsInstance(path, bytes) self.assertIsInstance(date, str) self.assertTrue(kind is None or kind in ARCHIVERS) self.assertTrue(usetar is None or isinstance(usetar, str)) logger.debug("restoring [%r][%s]", path, date) # The path wont have leading and trailing "/". (path, file_b) = os.path.split(path) if not path: path = file_b file_b = b"" # Check user access to repo / path. (repo_obj, path_obj) = self.validate_user_path(path) # Get the restore date try: rdw_helpers.rdwTime(int(date)) except: logger.warning("invalid date %s", date) raise cherrypy.HTTPError(400, _("Invalid date.")) # Get if backup in progress if repo_obj.in_progress: raise cherrypy.HTTPError(500, _("""A backup is currently in progress to this repository. Restores are disabled until this backup is complete.""")) # Determine the kind. kind = kind or 'zip' if usetar is not None: kind = 'tar.gz' # Restore file(s) filename, fileobj = path_obj.restore(file_b, int(date), kind=kind) # Define content-disposition. cherrypy.response.headers["Content-Disposition"] = self._content_disposition(filename) # Stream the data. return _serve_fileobj(fileobj, content_type=None, content_length=None)
def render_prefs_panel(self, panelid, **kwargs): # @UnusedVariable # Get user root directory filename = None user_root = self.app.currentuser.user_root if user_root: filename = os.path.join(user_root, '.ssh', 'authorized_keys') # Handle action params = {} if 'action' in kwargs: try: action = kwargs['action'] if action == 'add': self._handle_add(filename, **kwargs) elif action == 'delete': self._handle_delete(filename, **kwargs) except RdiffWarning as e: params['warning'] = str(e) except RdiffError as e: params['error'] = str(e) except Exception as e: _logger.warning("unknown error processing action", exc_info=True) params['error'] = _("Unknown error") # Get SSH keys if file exists. params["sshkeys"] = [] if filename: try: params["sshkeys"] = [ {'title': key.comment or (key.keytype + ' ' + key.key[:18]), 'fingerprint': key.fingerprint, 'lineno': key.lineno} for key in authorizedkeys.read(filename)] except IOError: params['error'] = _("error reading SSH keys file") _logger.warning("error reading SSH keys file [%s]", filename) else: params['error'] = _("error reading SSH keys file") _logger.warning("SSH keys file [%s] is not accessible", filename) return "prefs_sshkeys.html", params
def check_crendential(l, r): # Check results if len(r) != 1: logger.debug("user [%s] not found in LDAP", username) return None # Bind using the user credentials. Throws an exception in case of # error. l.simple_bind_s(r[0][0], password) try: logger.info("user [%s] found in LDAP", username) # Verify the shadow expire if self.check_shadow_expire: shadow_expire = self._attr_shadow_expire(r) # Convert nb. days into seconds. if shadow_expire and shadow_expire * 24 * 60 * 60 < time.time( ): logger.warn("user account %s expired: %s", username, shadow_expire) raise RdiffError( _('User account %s expired.' % username)) # Get username dn = r[0][0] new_username = self._decode(r[0][1][self.attribute][0]) # Verify if the user is member of the required group if self.require_group: value = dn if self.group_attribute_is_dn else new_username logger.info("check if user [%s] is member of [%s]", value, self.require_group) if not l.compare_s(self.require_group, self.group_attribute, value): raise RdiffError( _('Permissions denied for user account %s.' % username)) finally: l.unbind_s() # Return the username return new_username
def change_passwd(l, r): if len(r) != 1: raise ValueError(_("user %s not found)" % (username,))) # Bind using the user credentials. Throws an exception in case of # error. if old_password is not None: l.simple_bind_s(r[0][0], encode_s(old_password)) l.passwd_s(r[0][0], encode_s(old_password), encode_s(password)) l.unbind_s() logger.info("password for user [%s] is updated in LDAP" % username) # User updated, return False return False
def change_passwd(l, r): if len(r) != 1: raise RdiffError(_("User %s not found." % (username, ))) # Bind using the user credentials. Throws an exception in case of # error. if old_password is not None: l.simple_bind_s(r[0][0], old_password) l.passwd_s(r[0][0], old_password, password) l.unbind_s() logger.info("password for user [%s] is updated in LDAP", username) # User updated, return False return False
def index(self, path=b"", keepdays=None): self.assertIsInstance(path, bytes) self.assertTrue(keepdays) _logger.debug("repo settings [%r]", path) # Get new value try: keepdays = int(keepdays) except: _logger.warning("invalid keepdays value %r", keepdays) raise cherrypy.HTTPError(400, _("Invalid value")) # Check user permissions repo_obj = self.validate_user_path(path)[0] # Get repository object from user database. r = self.app.currentuser.get_repo(repo_obj.path) # Update the database. r.set_attr(KEEPDAYS, keepdays) return _("Updated")
def user_attr_changed(self, username, attrs={}): """ Implementation of IUserChangeListener interface. """ # Leave if the mail was not changed. if 'email' not in attrs: return # get User object (to get email) userobj = self.app.userdb.get_user(username) assert userobj # If the email attributes was changed, send a mail notification. self.send_mail(userobj, _("Email address changed"), "email_changed.html")
def status(self): """Check if a backup is in progress for the current repo.""" # Filter the files to keep current_mirror.* files current_mirrors = [ x for x in self._data_entries if x.startswith(b"current_mirror.") ] pid_re = re.compile(b"^PID\s*([0-9]+)", re.I | re.M) def extract_pid(current_mirror): """Return process ID from a current mirror marker, if any""" entry = IncrementEntry(self, current_mirror) match = pid_re.search(entry.read()) if not match: return None else: return int(match.group(1)) # Read content of the file and check if pid still exists for current_mirror in current_mirrors: pid = extract_pid(current_mirror) try: p = psutil.Process(pid) if any('rdiff-backup' in c for c in p.cmdline()): return ( 'in_progress', _('A backup is currently in progress to this repository.' )) except psutil.NoSuchProcess: logger.debug('pid [%s] does not exists', pid) pass # If multiple current_mirror file exists and none of them are associated to a PID, this mean the last backup was interrupted. # Also, if the last backup date is undefined, this mean the first initial backup was interrupted. if len(current_mirrors) > 1 or not self.last_backup_date: return ('interrupted', _('The previous backup seams to have failed.')) return ('ok', '')
def default(self, path=b"", date=None, kind=None, usetar=None): self.assertIsInstance(path, bytes) self.assertIsInstance(date, str) self.assertTrue(kind is None or kind in ARCHIVERS) self.assertTrue(usetar is None or isinstance(usetar, str)) logger.debug("restoring [%r][%s]", path, date) # Check user access to repo / path. (repo_obj, path_obj) = self.validate_user_path(path) # Get the restore date try: RdiffTime(int(date)) except: logger.warning("invalid date %s", date) raise cherrypy.HTTPError(400, _("Invalid date.")) # Get if backup in progress # status = repo_obj.status # if status[0] != 'ok': # raise cherrypy.HTTPError(500, _(status[1] + ' ' + _("""Restores are disabled."""))) # Determine the kind. kind = kind or 'zip' if usetar is not None: kind = 'tar.gz' # Restore file(s) filename, fileobj = path_obj.restore(int(date), kind=kind) # Define content-disposition. cherrypy.response.headers[ "Content-Disposition"] = _content_disposition(filename) # Set content-type based on filename extension content_type = _content_type(filename) cherrypy.response.headers['Content-Type'] = content_type # Stream the data. # Make use of _serve_fileobj() because the fsstat() function on a pipe # return a size of 0 for Content-Length. This behavior brake all the flow. return _serve_fileobj(fileobj, content_type=content_type, content_length=None)
def index(self, path_b=b"", **kwargs): assert isinstance(path_b, str) _logger.debug("repo settings [%s]", decode_s(path_b, 'replace')) # Check user permissions try: repo_obj = self.validate_user_path(path_b)[0] except librdiff.FileError as e: _logger.exception("invalid user path [%s]", decode_s(path_b, 'replace')) return self._compile_error_template(unicode(e)) # Check if any action to process. params = {} action = kwargs.get('action') if action: try: if action == "delete": params.update(self._handle_delete(repo_obj, **kwargs)) elif action == "set_encoding": params.update(self._handle_set_encoding(repo_obj, **kwargs)) else: _logger.info("unknown action: %s", action) raise cherrypy.NotFound("Unknown action") except ValueError as e: params['error'] = unicode(e) except HTTPRedirect as e: # Re-raise HTTPRedirect exception. raise e except Exception as e: _logger.warn("unknown error processing action", exc_info=True) params['error'] = _("Unknown error") # Get page data. try: params.update(self._get_parms_for_page(repo_obj)) except librdiff.FileError: _logger.exception("can't create page params") return self._compile_error_template(unicode(e)) # Generate page. return self._compile_template("settings.html", **params)
def default(self, path=b"", date=None, kind=None, usetar=None): self.assertIsInstance(path, bytes) self.assertIsInstance(date, str) self.assertTrue(kind is None or kind in ARCHIVERS) self.assertTrue(usetar is None or isinstance(usetar, str)) logger.debug("restoring [%r][%s]", path, date) # Check user access to repo / path. (repo_obj, path_obj) = self.validate_user_path(path) # Get the restore date try: RdiffTime(int(date)) except: logger.warning("invalid date %s", date) raise cherrypy.HTTPError(400, _("Invalid date.")) # Get if backup in progress # status = repo_obj.status # if status[0] != 'ok': # raise cherrypy.HTTPError(500, _(status[1] + ' ' + _("""Restores are disabled."""))) # Determine the kind. kind = kind or 'zip' if usetar is not None: kind = 'tar.gz' # Restore file(s) filename, fileobj = path_obj.restore(int(date), kind=kind) # Define content-disposition. cherrypy.response.headers["Content-Disposition"] = _content_disposition(filename) # Set content-type based on filename extension content_type = _content_type(filename) cherrypy.response.headers['Content-Type'] = content_type # Stream the data. # Make use of _serve_fileobj() because the fsstat() function on a pipe # return a size of 0 for Content-Length. This behavior brake all the flow. return _serve_fileobj(fileobj, content_type=content_type, content_length=None)
def _handle_delete(self, repo_obj, **kwargs): """ Delete the repository. """ # Validate the name confirm_name = kwargs.get('confirm_name') if confirm_name != repo_obj.display_name: raise ValueError(_("confirmation doesn't matches")) # Update the repository encoding _logger.info("deleting repository [%s]", repo_obj) repo_obj.delete() # Refresh repository list username = self.app.currentuser.username repos = self.app.userdb.get_repos(username) repos.remove(b"/" + repo_obj.path) self.app.userdb.set_repos(username, repos) raise HTTPRedirect("/")
def add_user(self, user, password=None): """ Used to add a new user with an optional password. """ assert password is None or isinstance(password, unicode) # Check if user already exists. db = self.find_user_database(user) if db: raise ValueError(_("user %s already exists" % (user,))) # Find a database where to add the user db = self._get_supporting_database('add_user') logger.info("adding new user [%s] to database [%s]", user, db) db.add_user(user) self._notify('added', user, password) # Find a password store where to set password if password: # Check if database support set password, otherwise try to set password as usual. if hasattr(db, 'set_password'): db.set_password(user, password) else: self.set_password(user, password)
def _get_parms_for_page(self, repo_obj, path_obj, restore, limit): # Build "parent directories" links # TODO This Should to me elsewhere. It contains logic related to librdiff encoding. parents = [] parents.append({"path": b"", "name": repo_obj.display_name}) parent_path_b = b"" for part_b in path_obj.path.split(b'/'): if part_b: parent_path_b = os.path.join(parent_path_b, part_b) display_name = repo_obj._decode(repo_obj.unquote(part_b)) parents.append({"path": parent_path_b, "name": display_name}) # Set up warning about in-progress backups, if necessary warning = False status = repo_obj.status if status[0] != 'ok': warning = status[1] + ' ' + _( "The displayed data may be inconsistent.") dir_entries = [] restore_dates = [] if restore: restore_dates = path_obj.change_dates[:-limit - 1:-1] else: # Get list of actual directory entries dir_entries = path_obj.dir_entries[::-1] return { "limit": limit, "repo_name": repo_obj.display_name, "repo_path": repo_obj.path, "path": path_obj.path, "dir_entries": dir_entries, "isdir": path_obj.isdir, "parents": parents, "restore_dates": restore_dates, "warning": warning }
def _get_parms_for_page(self): """ Build the params for the locations templates. """ # Get user's locations. user_root = self.app.currentuser.user_root user_repos = self.app.currentuser.repos repos = [] for user_repo in user_repos: try: # Get reference to a repo object repo_obj = librdiff.RdiffRepo(user_root, user_repo) path = repo_obj.path name = repo_obj.display_name status = repo_obj.status last_backup_date = repo_obj.last_backup_date except librdiff.FileError: logger.exception("invalid user path %s" % user_repo) path = encodefilename(user_repo.strip('/')) name = user_repo.strip('/') status = ( 'failed', _('The repository cannot be found or is badly damaged.')) last_backup_date = 0 # Create an entry to represent the repository repos.append({ "path": path, "name_split": name.strip('/').split('/'), "last_backup_date": last_backup_date, 'status': status, }) params = { "repos": repos, "templates_before_content": list(), } # Return the complete list of params. return params
def check_crendential(l, r): # Check results if len(r) != 1: logger.debug("user [%s] not found in LDAP", username) return None # Bind using the user credentials. Throws an exception in case of # error. l.simple_bind_s(r[0][0], password) l.unbind_s() logger.info("user [%s] found in LDAP", username) # Verify the shadow expire shadow_expire = self._attr_shadow_expire(r) if self.check_shadow_expire and shadow_expire: # Convert nb. days into seconds. shadow_expire = shadow_expire * 24 * 60 * 60 if shadow_expire < time.time(): logger.warn("user account %s expired: %s", username, shadow_expire) raise RdiffError(_('User account %s expired.' % username)) # Return the username return self._decode(r[0][1][self.attribute][0])
def users(self, userfilter=u"", usersearch=u"", action=u"", username=u"", email=u"", password=u"", user_root=u"", is_admin=u""): # Check if user is an administrator if not self.app.currentuser or not self.app.currentuser.is_admin: raise cherrypy.HTTPError(403) self.assertIsInstance(userfilter, str) self.assertIsInstance(usersearch, str) # If we're just showing the initial page, just do that params = {} if self._is_submit(): try: params = self._users_handle_action(action, username, email, password, user_root, is_admin) except RdiffWarning as e: params['warning'] = str(e) except RdiffError as e: params['error'] = str(e) except Exception as e: logger.warning("unknown error", exc_info=True) params['error'] = _("Unknown error") # Get page parameters params.update(self._users_get_params_for_page(userfilter, usersearch)) # Build users page return self._compile_template("admin_users.html", **params)