def create(self, template, **kwargs): "Create the message from the template and parameters for it." params = dict(site=settings['SITE_NAME'], support=settings.get('SITE_SUPPORT_EMAIL','[undefined]')) params.update(kwargs) self['subject'] = str(template['subject']).format(**params) self['text'] = str(template['text']).format(**params)
def create(self, template, **kwargs): "Create the message from the template and parameters for it." params = dict(site=settings['SITE_NAME'], support=settings.get('SITE_SUPPORT_EMAIL','[undefined]')) params.update(kwargs) self['subject'] = unicode(template['subject']).format(**params) self['text'] = unicode(template['text']).format(**params)
def load_order_messages(db): "Load the order messages document." for key in ['ORDER_MESSAGES_FILEPATH', 'INITIAL_ORDER_MESSAGES_FILEPATH']: filepath = settings.get(key) if filepath: break else: raise KeyError('no order messages file specified') print('Order messages from', filepath) with open(filepath) as infile: doc = yaml.safe_load(infile) doc['_id'] = 'order_messages' doc[constants.DOCTYPE] = constants.META print('saving order messages in database') db.save(doc)
def get(self): self.check_admin() # Don't show the password url = settings['DB_SERVER'] match = re.search(r':([^/].+)@', url) if match: url = list(url) url[match.start(1):match.end(1)] = '***' url = ''.join(url) params = [ ('Settings', settings['SETTINGS_FILEPATH']), ('Database server', url), ('Database', settings['DATABASE']), ('Site name', settings['SITE_NAME']), ('Site directory', settings['SITE_DIR']), ('Tornado debug', settings['TORNADO_DEBUG']), ('logging debug', settings['LOGGING_DEBUG']), ('order statuses', settings['ORDER_STATUSES_FILEPATH']), ('order transitions', settings['ORDER_TRANSITIONS_FILEPATH']), ('universities', settings.get('UNIVERSITIES_FILEPATH')), ('country codes', settings.get('COUNTRY_CODES_FILEPATH')), ('subject terms', settings.get('SUBJECT_TERMS_FILEPATH')), ] self.render('settings.html', params=params)
def main(): parser = utils.get_command_line_parser(description='OrderPortal server.') (options, args) = parser.parse_args() utils.load_settings(filepath=options.settings) utils.initialize() url = tornado.web.url handlers = [ url(r'/', Home, name='home'), url(r'/status', Status, name="status") ] try: regexp = settings['ORDER_IDENTIFIER_REGEXP'] except KeyError: pass else: handlers.append( url(r"/order/({0})".format(regexp), Order, name='order_id')) handlers.append( url(r"/api/v1/order/({0})".format(regexp), OrderApiV1, name='order_id_api')) handlers.extend([ url(r'/order/([0-9a-f]{32})', Order, name='order'), url(r'/api/v1/order/([0-9a-f]{32})', OrderApiV1, name='order_api'), url(r'/order/([^/]+).csv', OrderCsv, name='order_csv'), url(r'/order/([^/]+).xlsx', OrderXlsx, name='order_xlsx'), url(r'/order/([^/]+).zip', OrderZip, name='order_zip'), url(r'/order/([0-9a-f]{32})/logs', OrderLogs, name='order_logs'), url(r'/order', OrderCreate, name='order_create'), url(r'/api/v1/order', OrderCreateApiV1, name='order_create_api'), url(r'/order/([0-9a-f]{32})/edit', OrderEdit, name='order_edit'), url(r'/order/([0-9a-f]{32})/transition/(\w+)', OrderTransition, name='order_transition'), url(r'/api/v1/order/([0-9a-f]{32})/transition/(\w+)', OrderTransitionApiV1, name='order_transition_api'), url(r'/order/([0-9a-f]{32})/owner', OrderOwner, name='order_owner'), url(r'/order/([0-9a-f]{32})/clone', OrderClone, name='order_clone'), url(r'/order/([0-9a-f]{32})/file', OrderFile, name='order_file_add'), url(r'/order/([0-9a-f]{32})/file/([^/]+)', OrderFile, name='order_file'), url(r'/order/([0-9a-f]{32})/report', OrderReport, name='order_report'), url(r'/api/v1/order/([0-9a-f]{32})/report', OrderReportApiV1, name='order_report_api'), url(r'/order/([0-9a-f]{32})/report/edit', OrderReportEdit, name='order_report_edit'), url(r'/orders', Orders, name='orders'), url(r'/api/v1/orders', OrdersApiV1, name='orders_api'), url(r'/orders.csv', OrdersCsv, name='orders_csv'), url(r'/orders.xlsx', OrdersXlsx, name='orders_xlsx'), url(r'/accounts', Accounts, name='accounts'), url(r'/api/v1/accounts', AccountsApiV1, name='accounts_api'), url(r'/accounts.csv', AccountsCsv, name='accounts_csv'), url(r'/accounts.xlsx', AccountsXlsx, name='accounts_xlsx'), url(r'/account/([^/]+)', Account, name='account'), url(r'/api/v1/account/([^/]+)', AccountApiV1, name='account_api'), url(r'/account/([^/]+)/orders', AccountOrders, name='account_orders'), url(r'/api/v1/account/([^/]+)/orders', AccountOrdersApiV1, name='account_orders_api'), url(r'/account/([^/]+)/groups/orders', AccountGroupsOrders, name='account_groups_orders'), url(r'/api/v1/account/([^/]+)/groups/orders', AccountGroupsOrdersApiV1, name='account_groups_orders_api'), url(r'/account/([^/]+)/logs', AccountLogs, name='account_logs'), url(r'/account/([^/]+)/messages', AccountMessages, name='account_messages'), url(r'/account/([^/]+)/edit', AccountEdit, name='account_edit'), url(r'/group', GroupCreate, name='group_create'), url(r'/group/([0-9a-f]{32})', Group, name='group'), url(r'/group/([0-9a-f]{32})/edit', GroupEdit, name='group_edit'), url(r'/group/([0-9a-f]{32})/accept', GroupAccept, name='group_accept'), url(r'/group/([0-9a-f]{32})/decline', GroupDecline, name='group_decline'), url(r'/group/([0-9a-f]{32})/logs', GroupLogs, name='group_logs'), url(r'/groups', Groups, name='groups'), url(r'/search', Search, name='search'), url(r'/login', Login, name='login'), url(r'/logout', Logout, name='logout'), url(r'/reset', Reset, name='reset'), url(r'/password', Password, name='password'), url(r'/register', Register, name='register'), url(r'/registered', Registered, name='registered'), url(r'/account/([^/]+)/enable', AccountEnable, name='account_enable'), url(r'/account/([^/]+)/disable', AccountDisable, name='account_disable'), url(r'/account/([^/]+)/updateinfo', AccountUpdateInfo, name='account_update_info'), url(r'/forms', Forms, name='forms'), url(r'/form/([0-9a-f]{32})', Form, name='form'), url(r'/api/v1/form/([0-9a-f]{32})', FormApiV1, name='form_api'), url(r'/form/([0-9a-f]{32})/logs', FormLogs, name='form_logs'), url(r'/form', FormCreate, name='form_create'), url(r'/form/([0-9a-f]{32})/edit', FormEdit, name='form_edit'), url(r'/form/([0-9a-f]{32})/clone', FormClone, name='form_clone'), url(r'/form/([0-9a-f]{32})/pending', FormPending, name='form_pending'), url(r'/form/([0-9a-f]{32})/testing', FormTesting, name='form_testing'), url(r'/form/([0-9a-f]{32})/enable', FormEnable, name='form_enable'), url(r'/form/([0-9a-f]{32})/disable', FormDisable, name='form_disable'), url(r'/form/([0-9a-f]{32})/field', FormFieldCreate, name='field_create'), url(r'/form/([0-9a-f]{32})/field/([a-zA-Z][_a-zA-Z0-9]*)', FormFieldEdit, name='field_edit'), url(r'/form/([0-9a-f]{32})/field/([a-zA-Z][_a-zA-Z0-9]*)/descr', FormFieldEditDescr, name='field_edit_descr'), url(r'/form/([0-9a-f]{32})/orders', FormOrders, name='form_orders'), url(r'/form/([0-9a-f]{32})/aggregate', FormOrdersAggregate, name='form_orders_aggregate'), url(r'/news', News, name='news'), url(r'/new/([0-9a-f]{32})', NewsEdit, name='news_edit'), url(r'/new', NewsCreate, name='news_create'), url(r'/events', Events, name='events'), url(r'/event/([0-9a-f]{32})', Event, name='event'), url(r'/event', EventCreate, name='event_create'), url(r'/contact', Contact, name='contact'), url(r'/about', About, name='about'), url(r'/software', Software, name='software'), url(r'/infos', Infos, name='infos'), url(r'/info', InfoCreate, name='info_create'), url(r'/info/([^/]+)', Info, name='info'), url(r'/info/([^/]+)/edit', InfoEdit, name='info_edit'), url(r'/info/([^/]+)/logs', InfoLogs, name='info_logs'), url(r'/files', Files, name='files'), url(r'/file', FileCreate, name='file_create'), url(r'/file/([^/]+)', File, name='file'), url(r'/file/([^/]+)/meta', FileMeta, name='file_meta'), url(r'/file/([^/]+)/download', FileDownload, name='file_download'), url(r'/file/([^/]+)/edit', FileEdit, name='file_edit'), url(r'/api/v1/file/([^/]+)/edit', FileEditApiV1, name='file_edit_api'), url(r'/file/([0-9a-f]{32})/logs', FileLogs, name='file_logs'), url(r'/log/([0-9a-f]{32})', Log, name='log'), url(r'/([0-9a-f]{32})', Entity, name='entity'), url(r'/admin/global_modes', GlobalModes, name='global_modes'), url(r'/admin/settings', Settings, name='settings'), url(r'/admin/text/([^/]+)', Text, name='text'), url(r'/admin/texts', Texts, name='texts'), url(r'/admin/order_statuses', OrderStatuses, name='order_statuses'), url(r'/admin/order_messages', AdminOrderMessages, name='admin_order_messages'), url(r'/admin/account_messages', AdminAccountMessages, name='admin_account_messages'), url(r'/site/([^/]+)', tornado.web.StaticFileHandler, {'path': utils.expand_filepath(settings['SITE_DIR'])}, name='site'), ]) handlers.append(url(r'/api/v1/(.*)', NoSuchEntityApiV1)) handlers.append(url(r'/(.*)', NoSuchEntity)) application = tornado.web.Application( handlers=handlers, debug=settings.get('TORNADO_DEBUG', False), cookie_secret=settings['COOKIE_SECRET'], xsrf_cookies=True, ui_modules=uimodules, template_path=os.path.join(settings['ROOT_DIR'], 'templates'), static_path=os.path.join(settings['ROOT_DIR'], 'static'), login_url=(settings['BASE_URL_PATH_PREFIX'] or '') + '/login') # Add href URLs for the status icons. # This depends on order status setup. for key, value in settings['ORDER_STATUSES_LOOKUP'].items(): value['href'] = application.reverse_url('site', key + '.png') application.listen(settings['PORT'], xheaders=True) pid = os.getpid() url = settings['BASE_URL'] if settings['BASE_URL_PATH_PREFIX']: url += settings['BASE_URL_PATH_PREFIX'] logging.info("web server %s (PID %s)", url, pid) if options.pidfile: with open(options.pidfile, 'w') as pf: pf.write(str(pid)) tornado.ioloop.IOLoop.instance().start()
def post(self): """Login to a account account. Set a secure cookie. Forward to account edit page if first login. Log failed login attempt. Disable account if too many recent. """ try: email = self.get_argument('email') password = self.get_argument('password') except tornado.web.MissingArgumentError: self.see_other('home', error='Missing email or password argument.') return msg = 'Sorry, no such account or invalid password.' try: account = self.get_account(email) except ValueError as msg: self.see_other('home', error=str(msg)) return if utils.hashed_password(password) != account.get('password'): utils.log(self.db, self, account, changed=dict(login_failure=account['email'])) view = self.db.view('log/login_failure', startkey=[account['_id'], utils.timestamp(-1)], endkey=[account['_id'], utils.timestamp()]) # Disable account if too many recent login failures. if len(list(view)) > settings['LOGIN_MAX_FAILURES']: logging.warning( "account %s has been disabled due to" " too many login failures", account['email']) with AccountSaver(doc=account, rqh=self) as saver: saver['status'] = constants.DISABLED saver.erase_password() msg = "Too many failed login attempts: Your account has been" \ " disabled. Contact the site administrator %s." % \ settings.get('SITE_SUPPORT_EMAIL', '') # Prepare email message try: template = settings['ACCOUNT_MESSAGES'][constants.DISABLED] except KeyError: pass else: with MessageSaver(rqh=self) as saver: saver.create(template) # Recipient is hardwired here. saver.send([account['email']]) self.see_other('home', error=msg) return try: if not account.get('status') == constants.ENABLED: raise ValueError except ValueError: msg = "Account is disabled. Contact the site administrator %s." % \ settings.get('SITE_SUPPORT_EMAIL', '') self.see_other('home', error=msg) return if not self.global_modes['allow_login'] \ and account['role'] != constants.ADMIN: self.see_other('home', error='Login is currently disabled.') return self.set_secure_cookie(constants.USER_COOKIE, account['email'], expires_days=settings['LOGIN_MAX_AGE_DAYS']) logging.info("Basic auth login: account %s", account['email']) with AccountSaver(doc=account, rqh=self) as saver: saver['login'] = utils.timestamp() # Set login timestamp. if account.get('update_info'): self.see_other( 'account_edit', account['email'], message='Please review and update your account information.') return next = self.get_argument('next', None) if next is None: self.see_other('home') else: # Not quite right: should be an absolute URL to redirect. # But seems to work anyway. self.redirect(next)
def main(): parser = utils.get_command_line_parser(description='OrderPortal server.') (options, args) = parser.parse_args() utils.load_settings(filepath=options.settings) url = tornado.web.url handlers = [url(r'/', Home, name='home')] try: regexp = settings['ORDER_IDENTIFIER_REGEXP'] except KeyError: pass else: handlers.append(url(r"/order/({0})".format(regexp), Order, name='order_id')) handlers.append(url(r"/api/v1/order/({0})".format(regexp), OrderApiV1, name='order_id_api')) handlers.extend([ url(r'/order/([0-9a-f]{32})', Order, name='order'), url(r'/api/v1/order/([0-9a-f]{32})', OrderApiV1, name='order_api'), url(r'/order/([^/]+).csv', OrderCsv, name='order_csv'), url(r'/order/([^/]+).zip', OrderZip, name='order_zip'), url(r'/order/([0-9a-f]{32})/logs', OrderLogs, name='order_logs'), url(r'/order', OrderCreate, name='order_create'), url(r'/api/v1/order', OrderCreateApiV1, name='order_create_api'), url(r'/order/([0-9a-f]{32})/edit', OrderEdit, name='order_edit'), url(r'/order/([0-9a-f]{32})/transition/(\w+)', OrderTransition, name='order_transition'), url(r'/api/v1/order/([0-9a-f]{32})/transition/(\w+)', OrderTransitionApiV1, name='order_transition_api'), url(r'/order/([0-9a-f]{32})/clone', OrderClone, name='order_clone'), url(r'/order/([0-9a-f]{32})/file', OrderFile, name='order_file_add'), url(r'/order/([0-9a-f]{32})/file/([^/]+)',OrderFile,name='order_file'), url(r'/order/([0-9a-f]{32})/report', OrderReport, name='order_report'), url(r'/api/v1/order/([0-9a-f]{32})/report', OrderReportApiV1, name='order_report_api'), url(r'/order/([0-9a-f]{32})/report/edit', OrderReportEdit, name='order_report_edit'), url(r'/orders', Orders, name='orders'), url(r'/api/v1/orders', OrdersApiV1, name='orders_api'), url(r'/orders.csv', OrdersCsv, name='orders_csv'), url(r'/accounts', Accounts, name='accounts'), url(r'/api/v1/accounts', AccountsApiV1, name='accounts_api'), url(r'/accounts.csv', AccountsCsv, name='accounts_csv'), url(r'/account/([^/]+)', Account, name='account'), url(r'/api/v1/account/([^/]+)', AccountApiV1, name='account_api'), url(r'/account/([^/]+)/orders', AccountOrders, name='account_orders'), url(r'/api/v1/account/([^/]+)/orders', AccountOrdersApiV1, name='account_orders_api'), url(r'/account/([^/]+)/groups/orders', AccountGroupsOrders, name='account_groups_orders'), url(r'/api/v1/account/([^/]+)/groups/orders', AccountGroupsOrdersApiV1, name='account_groups_orders_api'), url(r'/account/([^/]+)/logs', AccountLogs, name='account_logs'), url(r'/account/([^/]+)/messages', AccountMessages, name='account_messages'), url(r'/account/([^/]+)/edit', AccountEdit, name='account_edit'), url(r'/group', GroupCreate, name='group_create'), url(r'/group/([0-9a-f]{32})', Group, name='group'), url(r'/group/([0-9a-f]{32})/edit', GroupEdit, name='group_edit'), url(r'/group/([0-9a-f]{32})/accept', GroupAccept, name='group_accept'), url(r'/group/([0-9a-f]{32})/decline', GroupDecline, name='group_decline'), url(r'/group/([0-9a-f]{32})/logs', GroupLogs, name='group_logs'), url(r'/groups', Groups, name='groups'), url(r'/search', Search, name='search'), url(r'/login', Login, name='login'), url(r'/logout', Logout, name='logout'), url(r'/reset', Reset, name='reset'), url(r'/password', Password, name='password'), url(r'/register', Register, name='register'), url(r'/registered', Registered, name='registered'), url(r'/account/([^/]+)/enable', AccountEnable, name='account_enable'), url(r'/account/([^/]+)/disable', AccountDisable,name='account_disable'), url(r'/account/([^/]+)/updateinfo', AccountUpdateInfo, name='account_update_info'), url(r'/forms', Forms, name='forms'), url(r'/form/([0-9a-f]{32})', Form, name='form'), url(r'/api/v1/form/([0-9a-f]{32})', FormApiV1, name='form_api'), url(r'/form/([0-9a-f]{32})/logs', FormLogs, name='form_logs'), url(r'/form', FormCreate, name='form_create'), url(r'/form/([0-9a-f]{32})/edit', FormEdit, name='form_edit'), url(r'/form/([0-9a-f]{32})/clone', FormClone, name='form_clone'), url(r'/form/([0-9a-f]{32})/pending', FormPending, name='form_pending'), url(r'/form/([0-9a-f]{32})/testing', FormTesting, name='form_testing'), url(r'/form/([0-9a-f]{32})/enable', FormEnable, name='form_enable'), url(r'/form/([0-9a-f]{32})/disable', FormDisable, name='form_disable'), url(r'/form/([0-9a-f]{32})/field', FormFieldCreate,name='field_create'), url(r'/form/([0-9a-f]{32})/field/([a-zA-Z][_a-zA-Z0-9]*)', FormFieldEdit, name='field_edit'), url(r'/form/([0-9a-f]{32})/field/([a-zA-Z][_a-zA-Z0-9]*)/descr', FormFieldEditDescr, name='field_edit_descr'), url(r'/form/([0-9a-f]{32})/orders', FormOrders, name='form_orders'), url(r'/form/([0-9a-f]{32})/orders.csv', FormOrdersCsv, name='form_orders_csv'), url(r'/news', News, name='news'), url(r'/new/([0-9a-f]{32})', NewsEdit, name='news_edit'), url(r'/new', NewsCreate, name='news_create'), url(r'/events', Events, name='events'), url(r'/event/([0-9a-f]{32})', Event, name='event'), url(r'/event', EventCreate, name='event_create'), url(r'/contact', Contact, name='contact'), url(r'/about', About, name='about'), url(r'/software', Software, name='software'), url(r'/infos', Infos, name='infos'), url(r'/info', InfoCreate, name='info_create'), url(r'/info/([^/]+)', Info, name='info'), url(r'/info/([^/]+)/edit', InfoEdit, name='info_edit'), url(r'/info/([^/]+)/logs', InfoLogs, name='info_logs'), url(r'/files', Files, name='files'), url(r'/file', FileCreate, name='file_create'), url(r'/file/([^/]+)', File, name='file'), url(r'/file/([^/]+)/meta', FileMeta, name='file_meta'), url(r'/file/([^/]+)/download', FileDownload, name='file_download'), url(r'/file/([^/]+)/edit', FileEdit, name='file_edit'), url(r'/api/v1/file/([^/]+)/edit', FileEditApiV1, name='file_edit_api'), url(r'/file/([0-9a-f]{32})/logs', FileLogs, name='file_logs'), url(r'/log/([0-9a-f]{32})', Log, name='log'), url(r'/([0-9a-f]{32})', Entity, name='entity'), url(r'/nsc/package/([0-9a-f]{32})', NscOrderPkgV1, name='nsc_order_package'), url(r'/admin/global_modes', GlobalModes, name='global_modes'), url(r'/admin/settings', Settings, name='settings'), url(r'/admin/text/([^/]+)', Text, name='text'), url(r'/admin/texts', Texts, name='texts'), url(r'/admin/order_statuses', OrderStatuses, name='order_statuses'), url(r'/admin/order_messages', AdminOrderMessages, name='admin_order_messages'), url(r'/admin/account_messages', AdminAccountMessages, name='admin_account_messages'), url(r'/site/([^/]+)', tornado.web.StaticFileHandler, {'path': utils.expand_filepath(settings['SITE_DIR'])}, name='site'), ]) handlers.append(url(r'/api/v1/(.*)', NoSuchEntityApiV1)) handlers.append(url(r'/(.*)', NoSuchEntity)) application = tornado.web.Application( handlers=handlers, debug=settings.get('TORNADO_DEBUG', False), cookie_secret=settings['COOKIE_SECRET'], xsrf_cookies=True, ui_modules=uimodules, template_path=os.path.join(settings['ROOT_DIR'], 'html'), static_path=os.path.join(settings['ROOT_DIR'], 'static'), login_url=r'/login') # Add href URLs for the status icons. # This depends on order status setup. for key, value in settings['ORDER_STATUSES_LOOKUP'].iteritems(): value['href'] = application.reverse_url('site', key + '.png') application.listen(settings['PORT'], xheaders=True) pid = os.getpid() logging.info("web server PID %s on port %s", pid, settings['PORT']) if options.pidfile: with open(options.pidfile, 'w') as pf: pf.write(str(pid)) tornado.ioloop.IOLoop.instance().start()
def set_params(self, **kwargs): "Set the parameters to use for the message text." self.params = dict( site=settings['SITE_NAME'], support=settings.get('SITE_SUPPORT_EMAIL', '[not defined]')) self.params.update(kwargs)
def init_database(dumpfilepath=None): db = utils.get_db(create=True) print('wiping out database...') wipeout_database(db) print('wiped out database') load_designs(db) print('loaded designs') # No specific items set here; done on-the-fly in e.g. get_next_number db.save(dict(_id='order', orderportal_doctype=constants.META)) print('created meta documents') if dumpfilepath: dumpfilepath = utils.expand_filepath(dumpfilepath) try: print('reading dump file...') undump(db, dumpfilepath) except IOError: print('Warning: could not load', dumpfilepath) else: print('no dump file loaded') # Load initial order messages from file if missing in db try: db['order_messages'] except couchdb.ResourceNotFound: try: filepath = settings['INITIAL_ORDER_MESSAGES_FILEPATH'] except KeyError: print('Warning: no initial order messages') else: try: with open(utils.expand_filepath(filepath)) as infile: messages = yaml.safe_load(infile) except IOError: print('Warning: could not load', filepath) messages = dict() messages['_id'] = 'order_messages' messages[constants.DOCTYPE] = constants.META db.save(messages) # Load initial account messages from file if missing in db try: db['account_messages'] except couchdb.ResourceNotFound: try: filepath = settings['INITIAL_ACCOUNT_MESSAGES_FILEPATH'] except KeyError: print('Warning: no initial account messages') else: try: with open(utils.expand_filepath(filepath)) as infile: messages = yaml.safe_load(infile) except IOError: print('Warning: could not load', filepath) messages = dict() messages['_id'] = 'account_messages' messages[constants.DOCTYPE] = constants.META db.save(messages) # Load texts from the init text file only if missing id db filepath = settings.get('INITIAL_TEXTS_FILEPATH') if filepath: print('loading missing texts from', filepath) try: with open(utils.expand_filepath(filepath)) as infile: texts = yaml.safe_load(infile) except IOError: print('Warning: could not load', filepath) texts = dict() else: texts = dict() for name in constants.TEXTS: if len(list(db.view('text/name', key=name))) == 0: with admin.TextSaver(db=db) as saver: saver['name'] = name saver['text'] = texts.get(name, '')