def execution_context_stacking(self, fixture): """When an ExecutionContext overrides a deeper one on the call stack, it will retain the same id.""" some_context = ExecutionContext() vassert(some_context is not fixture.context) vassert(some_context.id == fixture.context.id) with some_context: vassert(ExecutionContext.get_context_id() == some_context.id)
def new_context(self, config=None, session=None): context = ExecutionContext() context.set_config(config or self.config) context.set_system_control(self.system_control) with context: context.set_session(session or self.session) return context
def test_contents(): """A Session, Config or SystemControl may be set on the ExecutionContext.""" some_context = ExecutionContext() session = EmptyStub() config = EmptyStub() system_control = EmptyStub() some_context.session = session some_context.config = config some_context.system_control = system_control assert some_context.session is session assert some_context.config is config assert some_context.system_control is system_control
def replace_elixir(self): # reahl-declarative is new, and replaces reahl-elixir-impl orm_control = ExecutionContext.get_context().system_control.orm_control self.schedule('cleanup', orm_control.remove_schema_version_for, egg_name='reahl-web-elixirimpl', fail_if_not_found=False)
def sign_timed_value(cls, value_string, timestamp_string): timed_value = cls.get_delimited_encoded_string(value_string, timestamp_string) key = ExecutionContext.get_context().config.web.csrf_key return hmac.new(key.encode('utf-8'), msg=timed_value.encode('utf-8'), digestmod=hashlib.sha1).hexdigest()
def clear_all_view_data(cls, view): web_session = ExecutionContext.get_context().session items = Session.query(cls).filter_by(web_session=web_session, view_path=view.full_path, ui_name=view.user_interface.name) for stale in items: Session.delete(stale)
def start_thread(self): assert not self.running self.running = True self.httpd_thread = Thread(target=functools.partial( self.main_loop, ExecutionContext.get_context())) self.httpd_thread.daemon = True self.httpd_thread.start()
def assemble(self, login_bookmark=None, get_queues=None): self.get_queues = get_queues self.web_session = ExecutionContext.get_context().session self.first_log_in = ViewPreCondition( LoginSession.for_current_session().is_logged_in, exception=Detour(login_bookmark)) self.workflow_interface = WorkflowInterface() self.inbox = Inbox(self.get_queues()) inbox_view_factory = self.define_view('/', title=_('Inbox')) inbox_view_factory.set_slot('main_slot', InboxWidget.factory(self.inbox)) task_view_factory = self.define_view('/task', view_class=TaskView, task=PersistedField( Task, required=True)) task_view_factory.add_precondition(self.first_log_in) inbox_view_factory.add_precondition(self.first_log_in) self.define_transition(self.workflow_interface.events.take_task, inbox_view_factory, task_view_factory) self.define_transition(self.workflow_interface.events.go_to_task, inbox_view_factory, task_view_factory) self.define_transition(self.workflow_interface.events.defer_task, task_view_factory, inbox_view_factory) self.define_transition(self.workflow_interface.events.release_task, task_view_factory, inbox_view_factory)
def test_namespaces(): ExecutionContext().install() state_dict = {} a = Field() a.bind('x', a) # Case: namespaces change the name of the Field b = a.in_namespace('deeper') assert a.name == 'x' assert b.name == 'deeper-x' # Case: namespaces can be nested c = b.in_namespace('even') assert c.name == 'even-deeper-x' # Case: a Field *in* different namespace, but made from another share the same data a.initial_value = EmptyStub() a.input_status = EmptyStub() a.validation_error = EmptyStub() a.user_input = EmptyStub() a.parsed_input = EmptyStub() assert a.initial_value is b.initial_value is c.initial_value assert a.input_status is b.input_status is c.input_status assert a.validation_error is b.validation_error is c.validation_error assert a.user_input is b.user_input is c.user_input assert a.parsed_input is b.parsed_input is c.parsed_input
def get_widget_class_for(self, task): config = ExecutionContext.get_context().config for widget_class in config.workflowui.task_widgets: if issubclass(widget_class, TaskWidget) and widget_class.displays(task): return widget_class raise ProgrammerError('no Widget found to display %s' % task)
def test_login_queries(party_account_fixture, web_fixture): """""" context = ExecutionContext.get_context() config = context.config user_session = context.session login_session = LoginSession.for_session(context.session) system_account = party_account_fixture.system_account web_fixture.request.scheme = 'https' context.request.cookies[ config.web.secure_key_name] = user_session.secure_salt assert config.web.idle_secure_lifetime < config.web.idle_lifetime assert config.web.idle_lifetime < config.web.idle_lifetime_max # Case: user logs in user_session.last_activity = None login_session.set_as_logged_in(system_account, False) assert login_session.is_logged_in() assert login_session.is_logged_in(secured=True) # Case: user logs out login_session.log_out() assert not login_session.is_logged_in() assert not login_session.is_logged_in(secured=True) # Case: user activity is older than secure lifetime assert (config.web.idle_lifetime - config.web.idle_secure_lifetime) > 50 login_session.set_as_logged_in(system_account, False) user_session.last_activity = datetime.now() - timedelta( seconds=config.web.idle_secure_lifetime + 50) assert login_session.is_logged_in() assert not login_session.is_logged_in(secured=True) # Case: user activity is older than all lifetimes assert (config.web.idle_lifetime - config.web.idle_secure_lifetime) > 50 login_session.set_as_logged_in(system_account, False) user_session.last_activity = datetime.now() - timedelta( seconds=config.web.idle_lifetime + 50) assert not login_session.is_logged_in() assert not login_session.is_logged_in(secured=True) # Case: user activity is older than non-secure lifetime, but keep_me_logged_in is set assert (config.web.idle_lifetime - config.web.idle_secure_lifetime) > 50 assert (config.web.idle_lifetime_max - config.web.idle_lifetime) > 50 login_session.set_as_logged_in(system_account, True) user_session.last_activity = datetime.now() - timedelta( seconds=config.web.idle_lifetime + 50) assert login_session.is_logged_in() assert not login_session.is_logged_in(secured=True) # Case: user activity is older than non-secure lifetime max, but keep_me_logged_in is set assert (config.web.idle_lifetime - config.web.idle_secure_lifetime) > 50 assert (config.web.idle_lifetime_max - config.web.idle_lifetime) > 50 login_session.set_as_logged_in(system_account, True) Session.flush() user_session.last_activity = datetime.now() - timedelta( seconds=config.web.idle_lifetime_max + 50) assert not login_session.is_logged_in() assert not login_session.is_logged_in(secured=True)
def test_reahl_additions(): ExecutionContext().install() try: metadata.bind = 'sqlite:///:memory:' metadata.create_all() address = Address() Session.add(address) email_field = address.fields.email_address # While a programmer would not usually write code like this, # it is useful to show how the framework can use Fields and Events # to obtain more information about a certain Field/Event: assert email_field.label == 'Email' # Fields are used (amongst other things) to validate user input: with expected(Exception): email_field.from_input('invalid email address') with expected(NoException): assert address.email_address == None email_field.from_input('*****@*****.**') assert address.email_address == '*****@*****.**' # After input was given, the field is set on the object it belongs to: # (The value set is a marshalled version of the user input. In this case it is just # a string again, but it could have been, for example an EmailAddress object, # and Integer, or a Date.) assert address.email_address == '*****@*****.**' finally: metadata.bind = None
def secure_cookie_is_valid(self): context = ExecutionContext.get_context() try: salt = context.request.cookies[context.config.web.secure_key_name] return self.secure_salt == salt except KeyError: return False
def filesystem_path(self, relative_path): context = ExecutionContext.get_context() static_root = context.config.web.static_root if relative_path.endswith('/'): relative_path += 'index.d.html' return self.i18nise_filename( os.path.join(static_root, *relative_path.split('/')))
def handle_request(self, request): context = ExecutionContext.get_context() assert context.session is UserSessionStub.session # By the time user code executes, the session is set assert monitor.times_called == 1 # The database has been committed before user code started executing assert context.session.last_activity_time_set assert not UserSessionStub.session.key_is_set return Response()
def test_global_state(): """A Field can store its data in a global dict so that it can be recreated later with the same underlying data.""" ExecutionContext().install() state_dict = {} a = Field() a.bind('x', a) a.input_status = EmptyStub() a.validation_error = EmptyStub() a.user_input = EmptyStub() a.parsed_input = EmptyStub() a.activate_global_field_data_store(state_dict) a.initial_value = EmptyStub() b = Field() b.bind('x', b) assert a.initial_value is not b.initial_value assert a.input_status is not b.input_status assert a.validation_error is not b.validation_error assert a.user_input is not b.user_input assert a.parsed_input is not b.parsed_input b.activate_global_field_data_store(state_dict) assert a.initial_value is b.initial_value assert a.input_status is b.input_status assert a.validation_error is b.validation_error assert a.user_input is b.user_input assert a.parsed_input is b.parsed_input
def save_for(cls, view, form=None, **kwargs): assert (not form) or (form.view is view) channel_name = form.channel_name if form else None web_session = ExecutionContext.get_context().session instance = cls(web_session=web_session, view_path=view.full_path, ui_name=view.user_interface.name, channel_name=channel_name, **kwargs) Session.add(instance) return instance
def set_session_key(self, response): context = ExecutionContext.get_context() session_cookie = self.as_key() response.set_cookie(context.config.web.session_key_name, urllib.parse.quote(session_cookie), path='/', samesite='Strict') if self.is_secured(): response.set_cookie(context.config.web.secure_key_name, urllib.parse.quote(self.secure_salt), secure=True, path='/', max_age=context.config.web.idle_secure_lifetime, samesite='Strict')
def get_session_key(cls): context = ExecutionContext.get_context() try: raw_cookie = context.request.cookies[context.config.web.session_key_name] return urllib.parse.unquote(raw_cookie) except KeyError: return None
def i18nise_filename(self, for_default_locale): current_locale = ExecutionContext.get_context().interface_locale head, tail = os.path.splitext(for_default_locale) head, d = os.path.splitext(head) for_current_locale = head+'.%s' % current_locale+d+tail if os.path.isfile(for_current_locale): return for_current_locale return for_default_locale
def is_expired(self): now = self.get_now() csrf_timeout_seconds = ExecutionContext.get_context( ).config.web.csrf_timeout_seconds cutoff_timestamp = ( now - datetime.timedelta(seconds=csrf_timeout_seconds)).timestamp() return self.timestamp < cutoff_timestamp
def create_context(self, config_directory): try: self.context = ExecutionContext.for_config_directory(config_directory) except DistributionNotFound as ex: ex.args = ('%s (In development? Did you forget to do a "reahl setup -- develop -N"?)' % ex.args[0],) raise self.context.install() self.context.system_control = SystemControl(self.context.config)
def find_for(cls, view, form=None): assert (not form) or (form.view is view) web_session = ExecutionContext.get_context().session channel_name = form.channel_name if form else None return Session.query(cls).filter_by(web_session=web_session, view_path=view.full_path, ui_name=view.user_interface.name, channel_name=channel_name)
class ListConfig(ProductionCommand): """Lists current configuration settings.""" keyword = 'listconfig' def assemble(self): super().assemble() self.parser.add_argument('-v', '--values', action='store_true', dest='print_values', help='prints the currently configured value') self.parser.add_argument('-f', '--files', action='store_true', dest='print_files', help='prints the filename where the setting should be defined') self.parser.add_argument('-d', '--defaults', action='store_true', dest='print_defaults', help='prints the default value') self.parser.add_argument('-m', '--missing', action='store_true', dest='print_missing_only', help='prints the missing values only') self.parser.add_argument('-i', '--info', action='store_true', dest='print_description', help='prints a description') def create_context(self, config_directory): self.context = ExecutionContext(name=self.__class__.__name__) def execute(self, args): super().execute(args) self.context.install() print('Listing config for %s' % self.directory) config = StoredConfiguration(self.directory) config.configure(validate=False) for config_file, key, value, setting in config.list_all(): to_print = '%-35s' % key if args.print_files: to_print += '\t%s' % config_file if args.print_values: to_print += '\t%s' % value if args.print_defaults: if setting.defaulted: message = str(setting.default) if setting.dangerous: message += ' (DANGEROUS DEFAULT)' elif setting.automatic: message = 'AUTOMATIC' else: message = 'NO DEFAULT' to_print += '\t%s' % message if args.print_description: to_print += '\t%s' % setting.description if args.print_missing_only and not isinstance(value, MissingValue): pass else: print(to_print)
def send_mail(self, destination, subject_config_key, mail_config_key): data = self.get_data_for_substitution() config = ExecutionContext.get_context().config admin_email = config.accounts.admin_email subject = Template(config.get_from_string(subject_config_key)).safe_substitute(data) message_text = Template(config.get_from_string(mail_config_key)).safe_substitute(data) message = MailMessage(admin_email, [destination], subject, message_text) mailer = config.accounts.mailer_class.from_context() mailer.send_message(message)
def __init__(self, system_account, new_email): requirements = [VerifyEmailRequest(email=new_email, subject_config='accounts.email_change_subject', email_config='accounts.email_change_email')] config = ExecutionContext.get_context().config deadline = datetime.now() + timedelta(days=config.accounts.request_verification_timeout) self.system_account = system_account super(ChangeAccountEmail, self).__init__(requirements=requirements, deadline=deadline)
def reahl_scope(): try: return ExecutionContext.get_context_id() except NoContextFound: message = 'Database code can normally only be executed by code executed as part of handling a Request.' message += ' Such code is then executed within the context of, for example, a database transaction.' message += ' Looks like you attempted to execute database code from the wrong place, since no such context' message += ' could be found.' raise ProgrammerError(message)
def set_csrf_token_in_rendered_form_to_expired(self, browser): valid_token = self.get_csrf_token_in_rendered_form(browser) reconstructed_token = CSRFToken.from_coded_string(valid_token) allowed_timeout = ExecutionContext.get_context( ).config.web.csrf_timeout_seconds now = datetime.datetime.now(tz=datetime.timezone.utc) stale_time = now - datetime.timedelta(seconds=allowed_timeout + 1) reconstructed_token.timestamp = stale_time.timestamp() stale_token_string = reconstructed_token.as_signed_string() self.set_csrf_token_in_rendered_form(browser, stale_token_string)
def replace_elixir(self): # reahl-declarative is new, and replaces reahl-elixir-impl orm_control = ExecutionContext.get_context().system_control.orm_control self.schedule('cleanup', orm_control.initialise_schema_version_for, egg_name='reahl-web-declarative', egg_version=self.version) self.schedule('cleanup', orm_control.remove_schema_version_for, egg_name='reahl-web-elixirimpl')
def start_thread(self): assert not self.running self.running = True try: context = ExecutionContext.get_context() except NoContextFound: context = None self.httpd_thread = Thread(target=functools.partial(self.main_loop, context)) self.httpd_thread.daemon = True self.httpd_thread.start()