class DWWikiEngine(object): def __init__(self): # just in case locale.setlocale(locale.LC_ALL, 'en_US.utf-8') # This object is created once for all users self.user_manager = UserManager(settings.SERVICE_DB, settings.PWD_SALT) # locale path self.locale_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), 'locale') # Streamer lists self.block_streamers = list() self.inline_streamers = list() # Here we add our parsers. This should be made # easily extendable for custom parsers #self.streamer.add_block_parser(sqltablestreamer.SqlTableStreamer()) #self.streamer.add_block_parser(sqlbarchartstreamer.SqlBarChartStreamer()) #self.streamer.add_block_parser(sqllinechartstreamer.SqlLineChartStreamer()) #self.streamer.add_inline_parser(SqlInlineStreamer()) # security #self.guard = SecurityGuard('fileaccess.cfg', 'userlist.cfg') def register_block_streamer(self, streamer): self.block_streamers.append(streamer) def register_inline_streamer(self, streamer): self.inline_streamers.append(streamer) def get_streamer(self): """ Creates a new streamer per each request for thread safety. And returns it. """ res = mdstreamer.MdStreamer() # Here we add all our streamers. for streamer in self.block_streamers: res.add_block_parser(streamer.factory()) for streamer in self.inline_streamers: res.add_inline_parser(streamer.factory()) #res.add_block_parser(SqlTableStreamer()) #res.add_block_parser(SqlBarChartStreamer()) #res.add_block_parser(SqlLineChartStreamer()) #res.add_inline_parser(SqlInlineStreamer()) return res def check_access(self, action, *vpath): """ Check access to some resource with security guard. Returns True if granted, False otherwise """ user = cherrypy.session.get('user', '') rep_dir = self.reports_dir() #access_file = os.path.join(rep_dir, 'fileaccess.conf') quick_guard = SecurityGuard(settings.ACCESS_FILE, self.user_manager) res = quick_guard.request_access(user, action, *vpath) return res def goto_login(self, *vpath, **params): """ Redirects to login if user is not logged in """ base = cherrypy.config.get('tools.proxy.base', '') get_params = cherrypy.request.request_line.split()[1] #user = cherrypy.session.get('user', '') #if user == '': # redirect here to login # url requested in the first place will be put # into session variable report_requested_at_login # the root report is not needed if len(vpath) == 0: cherrypy.session['report_requested_at_login'] = '' else: cherrypy.session['report_requested_at_login'] = base + get_params # login to specific language lang = settings.DEFAULT_LANGUAGE #if (len(vpath) > 0) and (vpath[0] in settings.SUPPORTED_LANGUAGES): # lang = vpath[0] #raise cherrypy.HTTPRedirect(base + '/' + lang + "/login") raise cherrypy.HTTPRedirect(base + '/login') #else: # user logged in and access denied #raise cherrypy.HTTPError(403, "Access to requested URL denied.") def check_access_or_goto_login(self, action, *vpath): """ Check access to resource. If access denied but user is logged in, redirect to login page with message. Does not return anything, just raises an exception if needed """ base = cherrypy.config.get('tools.proxy.base', '') get_params = cherrypy.request.request_line.split()[1] #user = cherrypy.session.get('user', '') result = self.check_access(action, *vpath) if result == False: if user == '': # redirect here to login # url requested in the first place will be put # into session variable report_requested_at_login # the root report is not needed if len(vpath) == 0: cherrypy.session['report_requested_at_login'] = '' else: cherrypy.session[ 'report_requested_at_login'] = base + get_params raise cherrypy.HTTPRedirect(base + "/login") else: # user logged in and access denied raise cherrypy.HTTPError(403, "Access to requested URL denied.") def set_lang(self, lang): """Sets current language for user based on sesseion variable """ t = gettext.translation('messages', localedir=self.locale_path, languages=[lang]) # all strings will be in utf-8. no unicode t.install(unicode=False) def set_lang_from_vpath(self, *vpath): # "set current language depending on language element in path" lang = settings.DEFAULT_LANGUAGE # if len(vpath) > 0: # # we may have language mark # if vpath[0] in settings.SUPPORTED_LANGUAGES: # lang = vpath[0] # # The language for messages is set self.set_lang(lang) def reports_dir(self): """ Returns absolute path for reports storage """ return cherrypy.config['dwwiki.wikidir'] def find_real_file(self, *vpath): """ Finds real file from http path returns path to a real file or None if it's not found. Accounts for lang """ final_path = None # Extract the path real_path = self.reports_dir() # this should exist lang_path = real_path if len(vpath) > 0: for el in vpath: real_path = os.path.join(real_path, el) lang_path = os.path.join(lang_path, el) # for el in vpath: # # try for language # if el in settings.SUPPORTED_LANGUAGES: # lang_path = os.path.join(lang_path, el) # else: # real_path = os.path.join(real_path, el) # lang_path = os.path.join(lang_path, el) # make it absolute just in case real_path = os.path.abspath(real_path) lang_path = os.path.abspath(lang_path) # first try the lang path if os.path.isdir(lang_path): lang_path = os.path.join(lang_path, 'index.markdown') else: lang_path += '.markdown' if not os.path.exists(lang_path): # fall back to path without lang if os.path.isdir(real_path): # The path can possibly lead not to a file as such, # but to a directory. In that case we should return index.markdown # if one is present in a directory real_path = os.path.join(real_path, 'index.markdown') else: # it can be a file but witout extension or a nonexistent path real_path += '.markdown' if not os.path.exists(real_path): real_path = None final_path = real_path else: # lang_path exists final_path = lang_path return final_path @cherrypy.expose def default(self, *vpath, **params): base = cherrypy.config.get('tools.proxy.base', '') #redirect_to_lang = cherrypy.config.get('dwwiki.langredirect', False) #if len(vpath) == 0 and redirect_to_lang is True: # lang = settings.DEFAULT_LANGUAGE # target = base + '/' + lang + '/' # raise cherrypy.HTTPRedirect(target) # This is path starting from base and including all params get_params = cherrypy.request.request_line.split()[1] user = cherrypy.session.get('user', '') # The language for messages is set self.set_lang_from_vpath(*vpath) # ------------------------------------- # Next thing is to establish the action the user requires. # The user may want to do one of the following: # 1. Execute a report. It means there should be just a name of report # without .markdown extension. Some parameters that are used in # report generation may follow. E.g.: # http://dwworks.ru/dwwiki/samplereport?year=2016 # # 2. View report source. Just report source without any editing. # In this case it should be just a path to report file. Like this: # http://dwworks.ru/dwwiki/samplereport.markdown # And no other params? # # 3. Edit report. That means view report in the editor. If the user # has permission to 'read' report, it should be displayed, even if # the user doesn't have permission to 'write' it. On an attempt # to actually save it, it would be denied. # Here we use a special reserved parameter - 'action'. Like this: # http://dwworks.ru/dwwiki/samplereport?action=edit # # All above are meant to use GET request method. # If we have a POST request method, it means the user tries to # possible actions - 'action=xxx' understood by server URL_PARAM_ACTION = 'action' URL_ACTION_EDIT = 'edit' MARKDOWN_EXT = '.markdown' # what should we do? # execute # FINAL_ACTION_UNKNOWN = 0 FINAL_ACTION_EXECUTE = 1 FINAL_ACTION_VIEW_SOURCE = 2 FINAL_ACTION_VIEW_EDITOR = 3 # var final_action = FINAL_ACTION_UNKNOWN if cherrypy.request.method == 'GET': # if we have no params and file name ends with # .markdown, that means read raw text if len(vpath) > 0: # try for login/logout # try for read fn = vpath[-1] if fn == 'login': method = getattr(self, 'login', None) return method(*vpath, **params) elif fn == 'logout': method = getattr(self, 'logout', None) return method(*vpath, **params) if fn[-len(MARKDOWN_EXT):] == MARKDOWN_EXT: final_action = FINAL_ACTION_VIEW_SOURCE # no result yet if final_action == FINAL_ACTION_UNKNOWN: # try for edit edit = params.get(URL_PARAM_ACTION, '') if edit == URL_ACTION_EDIT: final_action = FINAL_ACTION_VIEW_EDITOR # nothing. try execute else: final_action = FINAL_ACTION_EXECUTE # Now just feed the request to specific handlers if final_action == FINAL_ACTION_EXECUTE: method = getattr(self, 'mkdown', None) return method(*vpath, **params) elif final_action == FINAL_ACTION_VIEW_SOURCE: method = getattr(self, 'display_report_source', None) return method(*vpath, **params) elif final_action == FINAL_ACTION_VIEW_EDITOR: method = getattr(self, 'display_report_editor', None) return method(*vpath, **params) # end of GET method elif cherrypy.request.method == 'POST': if len(vpath) > 0: # try for login/logout # try for read fn = vpath[-1] if fn == 'login': method = getattr(self, 'login', None) return method(*vpath, **params) # maybe a user tried to save the edited report # in which case we have in our request line 'action=edit' # because the edited form is posted to the same URL # it was received from # params** here contains both params in url, like 'action=edit' and 'month=2' # and form fields too. like editor='report source text' action_param = params.get('action', '') if action_param == 'edit': method = getattr(self, 'save_report_editor', None) return method(*vpath, **params) else: # POST request without action=edit is not allowed # exception raise cherrypy.HTTPError( 405, "Post method with these parameters not implemented.") # declare streaming for default page default._cp_config = {'response.stream': True} # This routine shows the login page #@cherrypy.expose def login(self, *vpath, **params): self.set_lang_from_vpath(*vpath) #user = cherrypy.session['user'] #if user == None: user = '******' base = cherrypy.config.get('tools.proxy.base', '') if cherrypy.request.method == 'GET': # show the form # format subheader # get requested login target_report = params.get('target', '') # in case it requests just the top of the server if target_report == '/': target_report = '' if target_report == base + '/': target_report = '' page_template = utils.get_report_template('loginform.html', *vpath) page_menu = utils.make_page_menu(langs=True) #subheader = htmlconstants.PAGE_LOGO.format(base=base) #result = loginform.LOGIN_PAGE_HEADER.format(base=base) #result += subheader # check for invalid logins notice = '' if cherrypy.session.get('last_login_incorrect', False): # show login incorrect notice = loginform.LOGIN_INCORRECT.format( invalid_username=_('LC_INVALID_USERNAME')) page = page_template.format( base=base, title="login", notice=notice, lc_login_to_dw_wiki=_('LC_LOGIN_TO_DW_WIKI'), lc_user_name=_('LC_USER_NAME'), lc_password=_('LC_PASSWORD'), lc_navigate_to=_('LC_NAVIGATE_TO'), lc_log_in=_('LC_LOG_IN'), target_report=target_report, page_menu=page_menu) return page elif cherrypy.request.method == 'POST': base = cherrypy.config.get('tools.proxy.base', '') get_params = cherrypy.request.request_line.split()[1] # try to log the user in user = '' psswrd = '' target = '' if 'user' in params: user = params['user'] if 'psswrd' in params: psswrd = params['psswrd'] if 'target' in params: target = params['target'] user_access = False ud = self.user_manager.get_user_data(user) if ud is not None: user_access = self.user_manager.check_password(ud, psswrd) if user_access is True: # ok. store user in session and redirect to report cherrypy.session['user'] = user if target == '': #lang = settings.DEFAULT_LANGUAGE #if len(vpath) > 0: # if vpath[0] in settings.SUPPORTED_LANGUAGES: # lang = vpath[0] #target = base + '/' + lang + '/' target = base + '/' # remove target from session. cherrypy.session['target'] = '' # remove invalid logins cherrypy.session['invalid_logins'] = 0 # remove last login incorrect cherrypy.session['last_login_incorrect'] = False raise cherrypy.HTTPRedirect(target) else: # TODO unknown user/password. # redirect to itself again. target remains in session # Track invalid logins to lock user invalid_logins = 0 if 'invalid_logins' in cherrypy.session: invalid_logins = cherrypy.session['invalid_logins'] invalid_logins += 1 cherrypy.session['invalid_logins'] = invalid_logins cherrypy.session['last_login_incorrect'] = True #target = base + '/login' # TODO base and get_params base may include some get params #raise cherrypy.HTTPRedirect(base + get_params) raise cherrypy.HTTPRedirect(get_params) raise cherrypy.HTTPError(405, "Method not implemented.") else: raise cherrypy.HTTPError(405, "Method not implemented.") login._cp_config = {'response.stream': True} # routine used for logout @cherrypy.expose def logout(self, *vpath, **params): base = cherrypy.config.get('tools.proxy.base', '/') user = cherrypy.session.get('user', '') # if user is set, redirect to base. # Anyway, just redirect in any case cherrypy.session['user'] = '' # remove target from session. cherrypy.session['target'] = '' cherrypy.lib.sessions.expire() # TODO generally we should log all login-logout attempts lang = '/' raise cherrypy.HTTPRedirect(base) # if len(vpath) > 0: # if vpath[0] in settings.SUPPORTED_LANGUAGES: # lang = '/' + vpath[0] + '/' # raise cherrypy.HTTPRedirect(base + lang) def display_report_source(self, *vpath, **params): """ Display a markdown file as is. Security should be checked before calling this, for it is not checked. Nor does this function check for correct file extension. Just returns it as text/plain """ real_path = self.reports_dir() # vpath should point to existing file. Otherwise we # generate 404 not found if len(vpath) > 0: for el in vpath: real_path = os.path.join(real_path, el) # make it absolute just in case real_path = os.path.abspath(real_path) if os.path.isdir(real_path): raise cherrypy.HTTPError(404, "Requested URL does not exist") else: # real_path is a file if not os.path.exists(real_path): raise cherrypy.HTTPError(404, "Requested URL does not exist") else: # file exists # TODO exceptions. file may not open cherrypy.response.headers[ 'Content-Type'] = 'text/plain; charset=utf-8' input_file = codecs.open(real_path, mode="r", encoding="utf-8") result = input_file.read() input_file.close() return result def save_report_editor(self, *vpath, **params): """ Saves a reports that has been edited and returns may do one of the following: 1. Return to a page the editor was called from 2. Just save the report, inform user and stay on the same page 3. Inform user that a report could not be saved for some reason and stay on the same page, displaying edited text """ # try to save # check for allowance first new_file = False base = cherrypy.config.get('tools.proxy.base', '') get_params = cherrypy.request.request_line.split()[1] user = cherrypy.session.get('user', '') access_granted = self.check_access('write', *vpath) if access_granted: # Find a real file if there is one # A real filename is stored in url real_path = self.find_real_file(*vpath) if real_path is None: # we are about to create a new file rd = self.reports_dir() for el in vpath[:-1]: rd = os.path.join(rd, el) if os.path.exists(rd) and os.path.isdir(rd): # check if the name is good fn = vpath[-1] fn = fn.replace('.markdown', '') # now check # 1-new-report is good # NewReport is bad, as is 1_new_report p = re.compile(u'^[a-z,0-9]+[a-z,0-9,-]+[a-z,0-9]+$') m = p.match(fn) if m is None: raise cherrypy.HTTPError(404, _("LC_URL_DOES_NOT_EXIST")) # file name is good. directory exists. go ahead, create a new file # set flags here new_file = True new_file_name = fn + '.markdown' real_path = os.path.join(rd, new_file_name) else: raise cherrypy.HTTPError(404, _("LC_URL_DOES_NOT_EXIST")) # now access granted, file exists or will be created. Check if we have an editor param if not 'editor' in params: raise cherrypy.HTTPError(400, "Bad request") # now editor text is present. edit_text = params.get('editor', u'').encode('utf-8') # now remove CRLF and set LF instead edit_text = edit_text.replace("\r\n", '\n') # edit_text now is unicode # check if it's empty #if edit_text.strip() == '': #raise cherrypy.HTTPError(405, "Report text is an empty string. This is not allowed.") # now at last now we can save it # file may not be allowed for writing # with current server permissions try: # TODO here we must call version control after writing file f = open(real_path, 'w') f.write(edit_text) f.close() except Exception, e: # TODO don't return exception. Return back to editor raise cherrypy.HTTPError(405, _('LC_ERROR_WRITING_TO_FILE')) # Now redirect to the same URL the editor was called from # to test the file contents #back_link = base + get_params back_link = get_params # remove action=edit back_link = back_link.replace('&action=edit', '') back_link = back_link.replace('?action=edit', '') if back_link[-1:] in ['?', '&']: back_link = back_link[0:-1] # set the message cherrypy.session['info_message'] = _( 'LC_INFO_REPORT_CHANGES_SAVED') # do the redirection # if just save, redirect to itself if 'save' in params: #raise cherrypy.HTTPRedirect(base + get_params) raise cherrypy.HTTPRedirect(get_params) # this is saveandclose else: raise cherrypy.HTTPRedirect(back_link) else:
class DWWikiEngine(object): def __init__(self): # just in case locale.setlocale(locale.LC_ALL, 'en_US.utf-8') # This object is created once for all users self.user_manager = UserManager(settings.SERVICE_DB, settings.PWD_SALT) # locale path self.locale_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'locale') # Streamer lists self.block_streamers = list() self.inline_streamers = list() # Here we add our parsers. This should be made # easily extendable for custom parsers #self.streamer.add_block_parser(sqltablestreamer.SqlTableStreamer()) #self.streamer.add_block_parser(sqlbarchartstreamer.SqlBarChartStreamer()) #self.streamer.add_block_parser(sqllinechartstreamer.SqlLineChartStreamer()) #self.streamer.add_inline_parser(SqlInlineStreamer()) # security #self.guard = SecurityGuard('fileaccess.cfg', 'userlist.cfg') def register_block_streamer(self, streamer): self.block_streamers.append(streamer) def register_inline_streamer(self, streamer): self.inline_streamers.append(streamer) def get_streamer(self): """ Creates a new streamer per each request for thread safety. And returns it. """ res = mdstreamer.MdStreamer() # Here we add all our streamers. for streamer in self.block_streamers: res.add_block_parser(streamer.factory()) for streamer in self.inline_streamers: res.add_inline_parser(streamer.factory()) #res.add_block_parser(SqlTableStreamer()) #res.add_block_parser(SqlBarChartStreamer()) #res.add_block_parser(SqlLineChartStreamer()) #res.add_inline_parser(SqlInlineStreamer()) return res def check_access(self, action, *vpath): """ Check access to some resource with security guard. Returns True if granted, False otherwise """ user = cherrypy.session.get('user', '') rep_dir = self.reports_dir() #access_file = os.path.join(rep_dir, 'fileaccess.conf') quick_guard = SecurityGuard(settings.ACCESS_FILE, self.user_manager) res = quick_guard.request_access(user, action, *vpath) return res def goto_login(self, *vpath, **params): """ Redirects to login if user is not logged in """ base = cherrypy.config.get('tools.proxy.base', '') get_params = cherrypy.request.request_line.split()[1] #user = cherrypy.session.get('user', '') #if user == '': # redirect here to login # url requested in the first place will be put # into session variable report_requested_at_login # the root report is not needed if len(vpath) == 0: cherrypy.session['report_requested_at_login'] = '' else: cherrypy.session['report_requested_at_login'] = base + get_params # login to specific language lang = settings.DEFAULT_LANGUAGE #if (len(vpath) > 0) and (vpath[0] in settings.SUPPORTED_LANGUAGES): # lang = vpath[0] #raise cherrypy.HTTPRedirect(base + '/' + lang + "/login") raise cherrypy.HTTPRedirect(base + '/login') #else: # user logged in and access denied #raise cherrypy.HTTPError(403, "Access to requested URL denied.") def check_access_or_goto_login(self, action, *vpath): """ Check access to resource. If access denied but user is logged in, redirect to login page with message. Does not return anything, just raises an exception if needed """ base = cherrypy.config.get('tools.proxy.base','') get_params = cherrypy.request.request_line.split()[1] #user = cherrypy.session.get('user', '') result = self.check_access(action, *vpath) if result == False: if user == '': # redirect here to login # url requested in the first place will be put # into session variable report_requested_at_login # the root report is not needed if len(vpath) == 0: cherrypy.session['report_requested_at_login'] = '' else: cherrypy.session['report_requested_at_login'] = base + get_params raise cherrypy.HTTPRedirect(base + "/login") else: # user logged in and access denied raise cherrypy.HTTPError(403, "Access to requested URL denied.") def set_lang(self, lang): """Sets current language for user based on sesseion variable """ t = gettext.translation('messages', localedir=self.locale_path, languages=[lang]) # all strings will be in utf-8. no unicode t.install(unicode=False) def set_lang_from_vpath(self, *vpath): # "set current language depending on language element in path" lang = settings.DEFAULT_LANGUAGE # if len(vpath) > 0: # # we may have language mark # if vpath[0] in settings.SUPPORTED_LANGUAGES: # lang = vpath[0] # # The language for messages is set self.set_lang(lang) def reports_dir(self): """ Returns absolute path for reports storage """ return cherrypy.config['dwwiki.wikidir'] def find_real_file(self, *vpath): """ Finds real file from http path returns path to a real file or None if it's not found. Accounts for lang """ final_path = None # Extract the path real_path = self.reports_dir() # this should exist lang_path = real_path if len(vpath) > 0: for el in vpath: real_path = os.path.join(real_path, el) lang_path = os.path.join(lang_path, el) # for el in vpath: # # try for language # if el in settings.SUPPORTED_LANGUAGES: # lang_path = os.path.join(lang_path, el) # else: # real_path = os.path.join(real_path, el) # lang_path = os.path.join(lang_path, el) # make it absolute just in case real_path = os.path.abspath(real_path) lang_path = os.path.abspath(lang_path) # first try the lang path if os.path.isdir(lang_path): lang_path = os.path.join(lang_path, 'index.markdown') else: lang_path += '.markdown' if not os.path.exists(lang_path): # fall back to path without lang if os.path.isdir(real_path): # The path can possibly lead not to a file as such, # but to a directory. In that case we should return index.markdown # if one is present in a directory real_path = os.path.join(real_path, 'index.markdown') else: # it can be a file but witout extension or a nonexistent path real_path += '.markdown' if not os.path.exists(real_path): real_path = None final_path = real_path else: # lang_path exists final_path = lang_path return final_path @cherrypy.expose def default(self, *vpath, **params): base = cherrypy.config.get('tools.proxy.base','') #redirect_to_lang = cherrypy.config.get('dwwiki.langredirect', False) #if len(vpath) == 0 and redirect_to_lang is True: # lang = settings.DEFAULT_LANGUAGE # target = base + '/' + lang + '/' # raise cherrypy.HTTPRedirect(target) # This is path starting from base and including all params get_params = cherrypy.request.request_line.split()[1] user = cherrypy.session.get('user', '') # The language for messages is set self.set_lang_from_vpath(*vpath) # ------------------------------------- # Next thing is to establish the action the user requires. # The user may want to do one of the following: # 1. Execute a report. It means there should be just a name of report # without .markdown extension. Some parameters that are used in # report generation may follow. E.g.: # http://dwworks.ru/dwwiki/samplereport?year=2016 # # 2. View report source. Just report source without any editing. # In this case it should be just a path to report file. Like this: # http://dwworks.ru/dwwiki/samplereport.markdown # And no other params? # # 3. Edit report. That means view report in the editor. If the user # has permission to 'read' report, it should be displayed, even if # the user doesn't have permission to 'write' it. On an attempt # to actually save it, it would be denied. # Here we use a special reserved parameter - 'action'. Like this: # http://dwworks.ru/dwwiki/samplereport?action=edit # # All above are meant to use GET request method. # If we have a POST request method, it means the user tries to # possible actions - 'action=xxx' understood by server URL_PARAM_ACTION = 'action' URL_ACTION_EDIT = 'edit' MARKDOWN_EXT = '.markdown' # what should we do? # execute # FINAL_ACTION_UNKNOWN = 0 FINAL_ACTION_EXECUTE = 1 FINAL_ACTION_VIEW_SOURCE = 2 FINAL_ACTION_VIEW_EDITOR = 3 # var final_action = FINAL_ACTION_UNKNOWN if cherrypy.request.method == 'GET': # if we have no params and file name ends with # .markdown, that means read raw text if len(vpath) > 0: # try for login/logout # try for read fn = vpath[-1] if fn == 'login': method = getattr(self, 'login', None) return method(*vpath, **params) elif fn == 'logout': method = getattr(self, 'logout', None) return method(*vpath, **params) if fn[-len(MARKDOWN_EXT):] == MARKDOWN_EXT: final_action = FINAL_ACTION_VIEW_SOURCE # no result yet if final_action == FINAL_ACTION_UNKNOWN: # try for edit edit = params.get(URL_PARAM_ACTION, '') if edit == URL_ACTION_EDIT: final_action = FINAL_ACTION_VIEW_EDITOR # nothing. try execute else: final_action = FINAL_ACTION_EXECUTE # Now just feed the request to specific handlers if final_action == FINAL_ACTION_EXECUTE: method = getattr(self, 'mkdown', None) return method(*vpath, **params) elif final_action == FINAL_ACTION_VIEW_SOURCE: method = getattr(self, 'display_report_source', None) return method(*vpath, **params) elif final_action == FINAL_ACTION_VIEW_EDITOR: method = getattr(self, 'display_report_editor', None) return method(*vpath, **params) # end of GET method elif cherrypy.request.method == 'POST': if len(vpath) > 0: # try for login/logout # try for read fn = vpath[-1] if fn == 'login': method = getattr(self, 'login', None) return method(*vpath, **params) # maybe a user tried to save the edited report # in which case we have in our request line 'action=edit' # because the edited form is posted to the same URL # it was received from # params** here contains both params in url, like 'action=edit' and 'month=2' # and form fields too. like editor='report source text' action_param = params.get('action', '') if action_param == 'edit': method = getattr(self, 'save_report_editor', None) return method(*vpath, **params) else: # POST request without action=edit is not allowed # exception raise cherrypy.HTTPError(405, "Post method with these parameters not implemented.") # declare streaming for default page default._cp_config = {'response.stream': True} # This routine shows the login page #@cherrypy.expose def login(self, *vpath, **params): self.set_lang_from_vpath(*vpath) #user = cherrypy.session['user'] #if user == None: user = '******' base = cherrypy.config.get('tools.proxy.base','') if cherrypy.request.method == 'GET': # show the form # format subheader # get requested login target_report = params.get('target', '') # in case it requests just the top of the server if target_report == '/': target_report = '' if target_report == base + '/': target_report = '' page_template = utils.get_report_template('loginform.html', *vpath) page_menu = utils.make_page_menu(langs=True) #subheader = htmlconstants.PAGE_LOGO.format(base=base) #result = loginform.LOGIN_PAGE_HEADER.format(base=base) #result += subheader # check for invalid logins notice = '' if cherrypy.session.get('last_login_incorrect',False): # show login incorrect notice = loginform.LOGIN_INCORRECT.format(invalid_username=_('LC_INVALID_USERNAME')) page = page_template.format(base=base, title="login", notice=notice, lc_login_to_dw_wiki=_('LC_LOGIN_TO_DW_WIKI'), lc_user_name=_('LC_USER_NAME'), lc_password=_('LC_PASSWORD'), lc_navigate_to=_('LC_NAVIGATE_TO'), lc_log_in=_('LC_LOG_IN'), target_report=target_report, page_menu=page_menu) return page elif cherrypy.request.method == 'POST': base = cherrypy.config.get('tools.proxy.base','') get_params = cherrypy.request.request_line.split()[1] # try to log the user in user = '' psswrd = '' target = '' if 'user' in params: user = params['user'] if 'psswrd' in params: psswrd = params['psswrd'] if 'target' in params: target = params['target'] user_access = False ud = self.user_manager.get_user_data(user) if ud is not None: user_access = self.user_manager.check_password(ud, psswrd) if user_access is True: # ok. store user in session and redirect to report cherrypy.session['user'] = user if target == '': #lang = settings.DEFAULT_LANGUAGE #if len(vpath) > 0: # if vpath[0] in settings.SUPPORTED_LANGUAGES: # lang = vpath[0] #target = base + '/' + lang + '/' target = base + '/' # remove target from session. cherrypy.session['target'] = '' # remove invalid logins cherrypy.session['invalid_logins'] = 0 # remove last login incorrect cherrypy.session['last_login_incorrect'] = False raise cherrypy.HTTPRedirect(target) else: # TODO unknown user/password. # redirect to itself again. target remains in session # Track invalid logins to lock user invalid_logins = 0 if 'invalid_logins' in cherrypy.session: invalid_logins = cherrypy.session['invalid_logins'] invalid_logins += 1 cherrypy.session['invalid_logins'] = invalid_logins cherrypy.session['last_login_incorrect'] = True #target = base + '/login' # TODO base and get_params base may include some get params #raise cherrypy.HTTPRedirect(base + get_params) raise cherrypy.HTTPRedirect(get_params) raise cherrypy.HTTPError(405, "Method not implemented.") else: raise cherrypy.HTTPError(405, "Method not implemented.") login._cp_config = {'response.stream': True} # routine used for logout @cherrypy.expose def logout(self, *vpath, **params): base = cherrypy.config.get('tools.proxy.base','/') user = cherrypy.session.get('user', '') # if user is set, redirect to base. # Anyway, just redirect in any case cherrypy.session['user'] = '' # remove target from session. cherrypy.session['target'] = '' cherrypy.lib.sessions.expire() # TODO generally we should log all login-logout attempts lang = '/' raise cherrypy.HTTPRedirect(base) # if len(vpath) > 0: # if vpath[0] in settings.SUPPORTED_LANGUAGES: # lang = '/' + vpath[0] + '/' # raise cherrypy.HTTPRedirect(base + lang) def display_report_source(self, *vpath, **params): """ Display a markdown file as is. Security should be checked before calling this, for it is not checked. Nor does this function check for correct file extension. Just returns it as text/plain """ real_path = self.reports_dir() # vpath should point to existing file. Otherwise we # generate 404 not found if len(vpath) > 0: for el in vpath: real_path = os.path.join(real_path, el) # make it absolute just in case real_path = os.path.abspath(real_path) if os.path.isdir(real_path): raise cherrypy.HTTPError(404, "Requested URL does not exist") else: # real_path is a file if not os.path.exists(real_path): raise cherrypy.HTTPError(404, "Requested URL does not exist") else: # file exists # TODO exceptions. file may not open cherrypy.response.headers['Content-Type'] = 'text/plain; charset=utf-8' input_file = codecs.open(real_path, mode="r", encoding="utf-8") result = input_file.read() input_file.close() return result def save_report_editor(self, *vpath, **params): """ Saves a reports that has been edited and returns may do one of the following: 1. Return to a page the editor was called from 2. Just save the report, inform user and stay on the same page 3. Inform user that a report could not be saved for some reason and stay on the same page, displaying edited text """ # try to save # check for allowance first new_file = False base = cherrypy.config.get('tools.proxy.base','') get_params = cherrypy.request.request_line.split()[1] user = cherrypy.session.get('user', '') access_granted = self.check_access('write', *vpath) if access_granted: # Find a real file if there is one # A real filename is stored in url real_path = self.find_real_file(*vpath) if real_path is None: # we are about to create a new file rd = self.reports_dir() for el in vpath[:-1]: rd = os.path.join(rd, el) if os.path.exists(rd) and os.path.isdir(rd): # check if the name is good fn = vpath[-1] fn = fn.replace('.markdown', '') # now check # 1-new-report is good # NewReport is bad, as is 1_new_report p = re.compile(u'^[a-z,0-9]+[a-z,0-9,-]+[a-z,0-9]+$') m = p.match(fn) if m is None: raise cherrypy.HTTPError(404, _("LC_URL_DOES_NOT_EXIST")) # file name is good. directory exists. go ahead, create a new file # set flags here new_file = True new_file_name = fn + '.markdown' real_path = os.path.join(rd, new_file_name) else: raise cherrypy.HTTPError(404, _("LC_URL_DOES_NOT_EXIST")) # now access granted, file exists or will be created. Check if we have an editor param if not 'editor' in params: raise cherrypy.HTTPError(400, "Bad request") # now editor text is present. edit_text = params.get('editor', u'').encode('utf-8') # now remove CRLF and set LF instead edit_text = edit_text.replace("\r\n", '\n') # edit_text now is unicode # check if it's empty #if edit_text.strip() == '': #raise cherrypy.HTTPError(405, "Report text is an empty string. This is not allowed.") # now at last now we can save it # file may not be allowed for writing # with current server permissions try: # TODO here we must call version control after writing file f = open(real_path, 'w') f.write(edit_text) f.close() except Exception, e: # TODO don't return exception. Return back to editor raise cherrypy.HTTPError(405, _('LC_ERROR_WRITING_TO_FILE')) # Now redirect to the same URL the editor was called from # to test the file contents #back_link = base + get_params back_link = get_params # remove action=edit back_link = back_link.replace('&action=edit', '') back_link = back_link.replace('?action=edit', '') if back_link[-1:] in ['?', '&']: back_link = back_link[0:-1] # set the message cherrypy.session['info_message'] = _('LC_INFO_REPORT_CHANGES_SAVED') # do the redirection # if just save, redirect to itself if 'save' in params: #raise cherrypy.HTTPRedirect(base + get_params) raise cherrypy.HTTPRedirect(get_params) # this is saveandclose else: raise cherrypy.HTTPRedirect(back_link) else: