def check_credentials(username, passphrase): """Verifies credentials for username and passphrase. Returns None on success or a string describing the error on failure""" start = time.clock() if not username or not passphrase: error = "No username or password." cfg.log(error) return error if username in cfg.users: u = cfg.users[username] else: u = None # hash the password whether the user exists, to foil timing # side-channel attacks pass_hash = hashlib.md5(passphrase).hexdigest() if u is None or u['passphrase'] != pass_hash: error = "Bad user-name or password." else: error = None if error: cfg.log(error) return error
def add_user(username, passphrase, name='', email='', expert=False): """Add a new user with specified username and passphrase. """ error = None if not username: error = "Must specify a username!" if not passphrase: error = "Must specify a passphrase!" if error is None: if username in map(lambda x: x[0], cfg.users.get_all()): error = "User already exists!" else: try: pass_hash = bcrypt.encrypt(passphrase) except PasswordSizeError: error = "Password is too long." if error is None: di = { 'username':username, 'name':name, 'email':email, 'expert':'on' if expert else 'off', 'groups':['expert'] if expert else [], 'passphrase':pass_hash, 'salt':pass_hash[7:29], # for bcrypt } new_user = User(di) cfg.users.set(username,new_user) if error: cfg.log(error) return error
def process_form(self, username=None, name=None, email=None, md5_password=None, **kwargs): msg = Message() if not username: msg.add = _("Must specify a username!") if not md5_password: msg.add = _("Must specify a password!") if username in cfg.users.get_all(): msg.add = _("User already exists!") else: try: di = {"username": username, "name": name, "email": email, "passphrase": md5_password} new_user = User(di) cfg.users.set(username, new_user) except: msg.add = _("Error storing user!") if not msg: msg.add = _("%s saved." % username) cfg.log(msg.text) main = self.main(username, name, email, msg=msg.text) return self.fill_template( title="Manage Users and Groups", main=main, sidebar_left=self.sidebar_left, sidebar_right=self.sidebar_right )
def fill_template(self, *args, **kwargs): if not 'js' in kwargs: try: kwargs['js'] = self.js except AttributeError: pass cfg.log("%%%%%%%%%%% %s" % kwargs) return u.page_template(*args, **kwargs)
def forms(self, url, *args, **kwargs): for form in cfg.forms: if url in form.url: cfg.log('Pulling together form for url %s (which matches %s)' % (url, form.url)) parts = get_parts(form, None, *args, **kwargs) return parts return {'sidebar_left':left, 'sidebar_right':right, 'main':main}
def main(self, message='', **kwargs): sys_store = filedict_con(cfg.store_file, 'sys') defaults = { 'time_zone': "slurp('/etc/timezone').rstrip()", 'hostname': "gethostname()", } for k, c in defaults.items(): if not k in kwargs: try: kwargs[k] = sys_store[k] except KeyError: exec( "if not '%(k)s' in kwargs: sys_store['%(k)s'] = kwargs['%(k)s'] = %(c)s" % { 'k': k, 'c': c }) ## Get the list of supported timezones and the index in that list of the current one module_file = __file__ if module_file.endswith(".pyc"): module_file = module_file[:-1] time_zones = json.loads( slurp( os.path.join(os.path.dirname(os.path.realpath(module_file)), "time_zones"))) for i in range(len(time_zones)): if kwargs['time_zone'] == time_zones[i]: time_zone_id = i break ## A little sanity checking. Make sure the current timezone is in the list. try: cfg.log('kwargs tz: %s, from_table: %s' % (kwargs['time_zone'], time_zones[time_zone_id])) except NameError: cfg.log.critical("Unknown Time Zone: %s" % kwargs['time_zone']) raise cherrypy.HTTPError( 500, "Unknown Time Zone: %s" % kwargs['time_zone']) ## And now, the form. form = Form(title=_("General Config"), action="/sys/config/general/index", name="config_general_form", message=message) form.html(self.help()) form.dropdown(_("Time Zone"), name="time_zone", vals=time_zones, select=time_zone_id) form.html( "<p>Your hostname is the local name by which other machines on your LAN can reach you.</p>" ) form.text_input('Hostname', name='hostname', value=kwargs['hostname']) form.submit(_("Submit")) return form.render()
def setup(): parse_arguments() try: if cfg.pidfile: from cherrypy.process.plugins import PIDFile PIDFile(cherrypy.engine, cfg.pidfile).subscribe() except AttributeError: pass try: cfg.exmachina = ExMachinaClient( secret_key=cfg.exmachina_secret_key or None) except socket.error: cfg.exmachina = None print "couldn't connect to exmachina daemon, but continuing anyways..." os.chdir(cfg.file_root) cherrypy.config.update({'error_page.404': error_page_404}) cherrypy.config.update({'error_page.500': error_page_500}) cfg.log = Logger() load_modules() cfg.html_root = Root() cfg.users = plugin_mount.UserStoreModule.get_plugins()[0] cfg.page_plugins = plugin_mount.PagePlugin.get_plugins() cfg.log("Loaded %d page plugins" % len(cfg.page_plugins)) cfg.forms = plugin_mount.FormPlugin.get_plugins() # Add an extra server server = _cpserver.Server() server.socket_host = '127.0.0.1' server.socket_port = 52854 server.subscribe() # Configure default server cherrypy.config.update({'server.socket_host': cfg.host, 'server.socket_port': cfg.port, 'server.thread_pool':10, 'tools.staticdir.root': cfg.file_root, 'tools.sessions.on':True, 'tools.auth.on':True, 'tools.sessions.storage_type':"file", 'tools.sessions.timeout':90, 'tools.sessions.storage_path':"%s/cherrypy_sessions" % cfg.data_dir, }) config = {'/': {'tools.staticdir.root': '%s/static' % cfg.file_root, 'tools.proxy.on':True,}, '/static': {'tools.staticdir.on': True, 'tools.staticdir.dir':"."}, '/favicon.ico':{'tools.staticfile.on':True, 'tools.staticfile.filename': "%s/static/theme/favicon.ico" % cfg.file_root} } cherrypy.tree.mount(cfg.html_root, '/', config=config) cherrypy.engine.signal_handler.subscribe()
def forms(self, url, *args, **kwargs): for form in cfg.forms: if url in form.url: cfg.log('Pulling together form for url %s (which matches %s)' % (url, form.url)) parts = get_parts(form, None, *args, **kwargs) return parts return {'sidebar_left': left, 'sidebar_right': right, 'main': main}
def check_credentials(username, passphrase): """Verifies credentials for username and passphrase. Returns None on success or a string describing the error on failure""" u = cfg.users[username] if u is None: cfg.log("Unknown user: %s" % username) return u"Username %s is unknown to me." % username if u['passphrase'] != hashlib.md5(passphrase).hexdigest(): return u"Incorrect passphrase."
def check_credentials(username, passphrase): """Verifies credentials for username and passphrase. Returns None on success or a string describing the error on failure""" u = cfg.users.get(username) if u is None: cfg.log("Unknown user: %s" % username) return u"Username %s is unknown to me." % username if u['passphrase'] != hashlib.md5(passphrase).hexdigest(): return u"Incorrect passphrase."
def process_form(self, username=None, name=None, email=None, password=None, **kwargs): msg = Message() error = add_user(username, password, name, email, False) if error: msg.text = error else: msg.add = _("%s saved." % username) cfg.log(msg.text) main = self.main(username, name, email, msg=msg.text) return self.fill_template(title="Manage Users and Groups", main=main, sidebar_left=self.sidebar_left, sidebar_right=self.sidebar_right)
def index(self): ## TODO: firstboot hijacking root should probably be in the firstboot module with a hook in plinth.py with sqlite_db(cfg.store_file, table="firstboot") as db: if not 'state' in db: raise cherrypy.InternalRedirect('/firstboot') elif db['state'] < 5: cfg.log("First Boot state = %d" % db['state']) raise cherrypy.InternalRedirect('/firstboot/state%d' % db['state']) if cherrypy.session.get(cfg.session_key, None): raise cherrypy.InternalRedirect('/router') else: raise cherrypy.InternalRedirect('/help/about')
def setup(): os.chdir(cfg.file_root) cherrypy.config.update({'error_page.404': error_page_404}) cherrypy.config.update({'error_page.500': error_page_500}) write_cherrypy_config() cfg.log = Logger() load_modules() cfg.html_root = Root() cfg.page_plugins = plugin_mount.PagePlugin.get_plugins() cfg.log("Loaded %d page plugins" % len(cfg.page_plugins)) cfg.users = plugin_mount.UserStoreModule.get_plugins()[0] cfg.forms = plugin_mount.FormPlugin.get_plugins()
def load_modules(): """Import all the symlinked .py files in the modules directory and all the .py files in directories linked in the modules directory (but don't dive deeper than that). Also, ignore the installed directory.""" for name in os.listdir("modules"): if name.endswith(".py") and not name.startswith('.'): cfg.log.info("importing modules/%s" % name) try: __import__("modules.%s" % (name[:-3])) except ImportError, e: cfg.log.error(_("Couldn't import modules/%s: %s") % (name, e)) else: cfg.log("skipping %s" % name)
def main(self, message='', **kwargs): if not cfg.users.expert(): return '<p>' + _('Only members of the expert group are allowed to see and modify the system setup.') + '</p>' sys_store = filedict_con(cfg.store_file, 'sys') hostname = get_hostname() # this layer of persisting configuration in sys_store could/should be # removed -BLN defaults = {'time_zone': "slurp('/etc/timezone').rstrip()", 'hostname': "hostname", } for k,c in defaults.items(): if not k in kwargs: try: kwargs[k] = sys_store[k] except KeyError: exec("if not '%(k)s' in kwargs: sys_store['%(k)s'] = kwargs['%(k)s'] = %(c)s" % {'k':k, 'c':c}) # over-ride the sys_store cached value kwargs['hostname'] = hostname ## Get the list of supported timezones and the index in that list of the current one module_file = __file__ if module_file.endswith(".pyc"): module_file = module_file[:-1] time_zones = json.loads(slurp(os.path.join(os.path.dirname(os.path.realpath(module_file)), "time_zones"))) for i in range(len(time_zones)): if kwargs['time_zone'] == time_zones[i]: time_zone_id = i break ## A little sanity checking. Make sure the current timezone is in the list. try: cfg.log('kwargs tz: %s, from_table: %s' % (kwargs['time_zone'], time_zones[time_zone_id])) except NameError: cfg.log.critical("Unknown Time Zone: %s" % kwargs['time_zone']) raise cherrypy.HTTPError(500, "Unknown Time Zone: %s" % kwargs['time_zone']) ## And now, the form. form = Form(title=_("General Config"), action="/sys/config/general/index", name="config_general_form", message=message ) form.html(self.help()) form.dropdown(_("Time Zone"), name="time_zone", vals=time_zones, select=time_zone_id) form.html("<p>Your hostname is the local name by which other machines on your LAN can reach you.</p>") form.text_input('Hostname', name='hostname', value=kwargs['hostname']) form.submit(_("Submit")) return form.render()
def main(self, expert=None, message='', **kwargs): """Note that kwargs contains '':"submit" if this is coming from a submitted form. If kwargs is empty, it's a fresh form with no user input, which means it should just reflect the state of the stored data.""" if not kwargs and expert == None: expert = cfg.users.expert() cfg.log("Expert mode is %s" % expert) form = Form(title=_("Expert Mode"), action="/sys/config/experts", name="expert_mode_form", message=message ) form.html(self.help()) form.checkbox(_("Expert Mode"), name="expert", checked=expert) form.submit(_("Submit")) return form.render()
def index(self): ## TODO: firstboot hijacking root should probably be in the firstboot module with a hook in plinth.py with sqlite_db(cfg.store_file, table="firstboot") as db: if not 'state' in db: # if we created a new user db, make sure it can't be read by everyone userdb_fname = '{}.sqlite3'.format(cfg.user_db) os.chmod(userdb_fname, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP) # cherrypy.InternalRedirect throws a 301, causing the # browser to cache the redirect, preventing the user from # navigating to /plinth until the browser is restarted. raise cherrypy.HTTPRedirect('firstboot', 307) elif db['state'] < 5: cfg.log("First Boot state = %d" % db['state']) raise cherrypy.InternalRedirect('firstboot/state%d' % db['state']) if cherrypy.session.get(cfg.session_key, None): raise cherrypy.InternalRedirect('router') else: raise cherrypy.InternalRedirect('help/about')
def process_form(self, username=None, name=None, email=None, md5_password=None, **kwargs): msg = Message() if not username: msg.add = _("Must specify a username!") if not md5_password: msg.add = _("Must specify a password!") if username in cfg.users.get_all(): msg.add = _("User already exists!") else: try: di = {'username':username, 'name':name, 'email':email, 'passphrase':md5_password} new_user = User(di) cfg.users.set(username,new_user) except: msg.add = _("Error storing user!") if not msg: msg.add = _("%s saved." % username) cfg.log(msg.text) main = self.main(username, name, email, msg=msg.text) return self.fill_template(title="Manage Users and Groups", main=main, sidebar_left=self.sidebar_left, sidebar_right=self.sidebar_right)
def check_credentials(username, passphrase): """Verifies credentials for username and passphrase. Returns None on success or a string describing the error on failure""" if not username or not passphrase: error = "No username or password." cfg.log(error) return error # hash the password whether the user exists, to foil timing # side-channel attacks try: if username in cfg.users: u = cfg.users[username] pass_hash = bcrypt.encrypt(passphrase, salt=u['salt']) else: u = None pass_hash = bcrypt.encrypt(passphrase) except PasswordSizeError: error = "Password is too long." cfg.log(error) return error if u is None or u['passphrase'] != pass_hash: error = "Bad user-name or password." else: error = None if error: cfg.log(error) return error
def main(self, message="", **kwargs): sys_store = filedict_con(cfg.store_file, "sys") defaults = {"time_zone": "slurp('/etc/timezone').rstrip()", "hostname": "gethostname()"} for k, c in defaults.items(): if not k in kwargs: try: kwargs[k] = sys_store[k] except KeyError: exec("if not '%(k)s' in kwargs: sys_store['%(k)s'] = kwargs['%(k)s'] = %(c)s" % {"k": k, "c": c}) ## Get the list of supported timezones and the index in that list of the current one module_file = __file__ if module_file.endswith(".pyc"): module_file = module_file[:-1] time_zones = json.loads(slurp(os.path.join(os.path.dirname(os.path.realpath(module_file)), "time_zones"))) for i in range(len(time_zones)): if kwargs["time_zone"] == time_zones[i]: time_zone_id = i break ## A little sanity checking. Make sure the current timezone is in the list. try: cfg.log("kwargs tz: %s, from_table: %s" % (kwargs["time_zone"], time_zones[time_zone_id])) except NameError: cfg.log.critical("Unknown Time Zone: %s" % kwargs["time_zone"]) raise cherrypy.HTTPError(500, "Unknown Time Zone: %s" % kwargs["time_zone"]) ## And now, the form. form = Form( title=_("General Config"), action="/sys/config/general/index", name="config_general_form", message=message ) form.html(self.help()) form.dropdown(_("Time Zone"), name="time_zone", vals=time_zones, select=time_zone_id) form.html("<p>Your hostname is the local name by which other machines on your LAN can reach you.</p>") form.text_input("Hostname", name="hostname", value=kwargs["hostname"]) form.submit(_("Submit")) return form.render()
def process_form(self, username=None, password=None, **kwargs): msg = Message() if not username: msg.add = _("Must specify a username!") if not password: msg.add = _("Must specify a password!") if username and password: output, error = actions.superuser_run( "xmpp-register", [username, password]) if error: raise Exception("something is wrong: " + error) if "successfully registered" in output: msg.add = _("Registered account for %s." % username) else: msg.add = _("Failed to register account for %s: %s" % (username, output)) cfg.log(msg.text) main = self.main(username, msg=msg.text) return self.fill_template( title="XMPP Server Configuration", main=main, sidebar_left=self.sidebar_left, sidebar_right=self.sidebar_right)
def check_credentials(username, passphrase): """Verifies credentials for username and passphrase. Returns None on success or a string describing the error on failure""" start = time.clock() if not username or not passphrase: error = "No username or password." cfg.log(error) return error u = cfg.users[username] # hash the password whether the user exists, to foil timing # side-channel attacks pass_hash = hashlib.md5(passphrase).hexdigest() if u is None or u['passphrase'] != pass_hash: error = "Bad user-name or password." else: error = None if error: cfg.log(error) return error
def setup(): parse_arguments() try: if cfg.pidfile: from cherrypy.process.plugins import PIDFile PIDFile(cherrypy.engine, cfg.pidfile).subscribe() except AttributeError: pass os.chdir(cfg.file_root) cherrypy.config.update({'error_page.404': error_page_404}) cherrypy.config.update({'error_page.500': error_page_500}) cfg.log = Logger() load_modules() cfg.html_root = Root() cfg.users = plugin_mount.UserStoreModule.get_plugins()[0] cfg.page_plugins = plugin_mount.PagePlugin.get_plugins() cfg.log("Loaded %d page plugins" % len(cfg.page_plugins)) cfg.forms = plugin_mount.FormPlugin.get_plugins() # Add an extra server server = _cpserver.Server() server.socket_host = '127.0.0.1' server.socket_port = 52854 server.subscribe() # Configure default server cherrypy.config.update({ 'server.socket_host': '127.0.0.1', 'server.socket_port': cfg.port, 'server.thread_pool': 10, 'tools.staticdir.root': cfg.file_root, 'tools.sessions.on': True, 'tools.auth.on': True, 'tools.sessions.storage_type': "file", 'tools.sessions.timeout': 90, 'tools.sessions.storage_path': "%s/data/cherrypy_sessions" % cfg.file_root, }) config = { '/': { 'tools.staticdir.root': '%s/static' % cfg.file_root, 'tools.proxy.on': True, }, '/static': { 'tools.staticdir.on': True, 'tools.staticdir.dir': "." }, '/favicon.ico': { 'tools.staticfile.on': True, 'tools.staticfile.filename': "%s/static/theme/favicon.ico" % cfg.file_root } } cherrypy.tree.mount(cfg.html_root, '/', config=config) cherrypy.engine.signal_handler.subscribe()
def __init__(self, *args, **kwargs): """If cfg.html_root is none, then this is the html_root.""" if not cfg.html_root: cfg.log('Setting html root to %s' % self.__class__.__name__) cfg.html_root = self
def setup(): parse_arguments() try: if cfg.pidfile: from cherrypy.process.plugins import PIDFile PIDFile(cherrypy.engine, cfg.pidfile).subscribe() except AttributeError: pass try: from exmachina import ExMachinaClient except ImportError: cfg.exmachina = None else: try: cfg.exmachina = ExMachinaClient( secret_key=cfg.exmachina_secret_key or None) except socket.error: cfg.exmachina = None print "couldn't connect to exmachina daemon, but continuing anyways..." os.chdir(cfg.python_root) cherrypy.config.update({'error_page.404': error_page_404}) cherrypy.config.update({'error_page.500': error_page_500}) cfg.log = Logger() load_modules() cfg.html_root = Root() cfg.users = plugin_mount.UserStoreModule.get_plugins()[0] cfg.page_plugins = plugin_mount.PagePlugin.get_plugins() cfg.log("Loaded %d page plugins" % len(cfg.page_plugins)) cfg.forms = plugin_mount.FormPlugin.get_plugins() # Add an extra server server = _cpserver.Server() server.socket_host = '127.0.0.1' server.socket_port = 52854 server.subscribe() # Configure default server cherrypy.config.update({ 'server.socket_host': cfg.host, 'server.socket_port': cfg.port, 'server.thread_pool': 10, 'tools.staticdir.root': cfg.file_root, 'tools.sessions.on': True, 'tools.auth.on': True, 'tools.sessions.storage_type': "file", 'tools.sessions.timeout': 90, 'tools.sessions.storage_path': "%s/cherrypy_sessions" % cfg.data_dir, }) config = { '/': { 'tools.staticdir.root': '%s/static' % cfg.file_root, 'tools.proxy.on': True, }, '/static': { 'tools.staticdir.on': True, 'tools.staticdir.dir': "." }, '/favicon.ico': { 'tools.staticfile.on': True, 'tools.staticfile.filename': "%s/static/theme/favicon.ico" % cfg.file_root } } cherrypy.tree.mount(cfg.html_root, '/', config=config) cherrypy.engine.signal_handler.subscribe()
def __init__(self, *args, **kwargs): for u in self.url: exec "cfg.html_root.%s = self" % "%s.%s" % ('.'.join( u.split("/")[1:]), self.__class__.__name__) cfg.log("Registered page: %s.%s" % ('.'.join(u.split("/")[1:]), self.__class__.__name__))
def __init__(self, *args, **kwargs): for u in self.url: exec "cfg.html_root.%s = self" % "%s.%s" % ('.'.join(u.split("/")[1:]), self.__class__.__name__) cfg.log("Registered page: %s.%s" % ('.'.join(u.split("/")[1:]), self.__class__.__name__))