def test_webyosai_requires_user(web_yosai, mock_web_registry, valid_thedude_username_password_token, valid_thedude_totp_token): """ confirm user approved and denied """ # yeah, it's a dumb example but I chose the big lebowski cuz I love the movie @WebYosai.requires_user def transport_ransom(the_ringer, destination): return 'transported' with WebYosai.context(web_yosai, mock_web_registry): subject = WebYosai.get_current_subject() try: subject.login(valid_thedude_username_password_token) except AdditionalAuthenticationRequired: subject.login(valid_thedude_totp_token) result = transport_ransom('the_ringer', 'the_nihilists') assert result == 'transported' subject.logout() with pytest.raises(mock_web_registry.mock_exception): transport_ransom('the_ringer', 'the_nihilists')
def test_webyosai_requires_role(web_yosai, mock_web_registry, valid_thedude_username_password_token, valid_thedude_totp_token): """ confirm role approved and denied """ # yeah, it's a dumb example but I chose the big lebowski cuz I love the movie # the dude is a courier so he can transport the ringer but he's not a thief # so he can't access ransom @WebYosai.requires_role(['courier']) def transport_ransom(the_ringer, destination): return 'transported' @WebYosai.requires_role(['thief']) def access_ransom(the_ringer): return 'accessed' with WebYosai.context(web_yosai, mock_web_registry): subject = WebYosai.get_current_subject() try: subject.login(valid_thedude_username_password_token) except AdditionalAuthenticationRequired: subject.login(valid_thedude_totp_token) result = transport_ransom('the_ringer', 'the_nihilists') assert result == 'transported' with pytest.raises(mock_web_registry.mock_exception): access_ransom('the_ringer')
def test_remember_me_with_expired_session( web_yosai, mock_web_registry, monkeypatch, remembered_valid_thedude_username_password_token, remembered_valid_thedude_totp_token): """ Send a request that contains an idle expired session_id and remember_me cookie. A new session is created and the user remembered. Confirm user identity. """ monkeypatch.setattr(web_yosai.security_manager.session_manager, 'idle_timeout', 1000) # milliseconds with WebYosai.context(web_yosai, mock_web_registry): subject = WebYosai.get_current_subject() try: subject.login(remembered_valid_thedude_username_password_token) except AdditionalAuthenticationRequired: subject.login(remembered_valid_thedude_totp_token) old_session_id = subject.get_session().session_id time.sleep(2) subject = WebYosai.get_current_subject() new_session_id = subject.get_session().session_id assert old_session_id != new_session_id
def test_run_as_raises(web_yosai, mock_web_registry, walter_identifier): with WebYosai.context(web_yosai, mock_web_registry): new_web_subject = WebYosai.get_current_subject() # a login is required , so this should raise: with pytest.raises(ValueError): new_web_subject.run_as(walter_identifier)
def test_run_as_pop(walter_identifier, jackie_identifier, web_yosai, mock_web_registry, jackie_testpermissions, walter_testpermissions, valid_thedude_username_password_token, valid_thedude_totp_token): jp = jackie_testpermissions wp = walter_testpermissions with WebYosai.context(web_yosai, mock_web_registry): new_web_subject = WebYosai.get_current_subject() try: new_web_subject.login(valid_thedude_username_password_token) except AdditionalAuthenticationRequired: new_web_subject.login(valid_thedude_totp_token) new_web_subject.run_as(jackie_identifier) jackieresults = new_web_subject.is_permitted(jp['perms']) assert jackieresults == jp['expected_results'] new_web_subject.run_as(walter_identifier) walterresults = new_web_subject.is_permitted(wp['perms']) assert walterresults == wp['expected_results'] new_web_subject.pop_identity() assert new_web_subject.identifiers == jackie_identifier new_web_subject.logout()
def test_session_idle_expiration_clears_cache( thedude_identifier, thedude_testpermissions, caplog, web_yosai, mock_web_registry, valid_thedude_username_password_token, valid_thedude_totp_token): cache_handler = web_yosai.security_manager.session_manager.session_handler.\ session_store.cache_handler tp = thedude_testpermissions with WebYosai.context(web_yosai, mock_web_registry): new_web_subject = WebYosai.get_current_subject() try: new_web_subject.login(valid_thedude_username_password_token) except AdditionalAuthenticationRequired: new_web_subject.login(valid_thedude_totp_token) new_web_subject.is_permitted(tp['perms']) # caches authz_info session = new_web_subject.get_session() session = cache_handler.get('session', identifier=session.session_id) twenty_ago = (60 * 20 * 1000) session.last_access_time = session.last_access_time - twenty_ago cache_handler.set('session', session.session_id, session) session = cache_handler.get('session', identifier=session.session_id) session = new_web_subject.get_session() with pytest.raises(ExpiredSessionException): session.last_access_time # this triggers the expiration out = caplot.text assert ('Clearing cached authc_info for [thedude]' in out and 'Clearing cached authz_info for [thedude]' in out) new_web_subject.logout()
def test_has_role(web_yosai, mock_web_registry, thedude_testroles, event_bus, valid_thedude_username_password_token, valid_thedude_totp_token): tr = thedude_testroles event_detected = None def event_listener(identifiers=None, items=None, logical_operator=None, topic=EVENT_TOPIC): nonlocal event_detected event_detected = items event_bus.subscribe(event_listener, 'AUTHORIZATION.RESULTS') with WebYosai.context(web_yosai, mock_web_registry): new_web_subject = WebYosai.get_current_subject() try: new_web_subject.login(valid_thedude_username_password_token) except AdditionalAuthenticationRequired: new_web_subject.login(valid_thedude_totp_token) result = new_web_subject.has_role(tr['roles']) assert (tr['expected_results'] == result and frozenset(event_detected) == result) new_web_subject.logout()
def test_session_attributes(web_yosai, mock_web_registry, monkeypatch, valid_thedude_username_password_token, valid_thedude_totp_token): """ Developer-defined session attribute schema is to serialize correctly """ value1 = {'attribute1': 'value1'} values = {'attribute2': 'value2', 'attribute3': 'value3'} with WebYosai.context(web_yosai, mock_web_registry): subject = WebYosai.get_current_subject() old_session = subject.get_session() old_session.set_attribute('attribute1', 'value1') old_session.set_attributes(values) try: subject.login(valid_thedude_username_password_token) except AdditionalAuthenticationRequired: subject.login(valid_thedude_totp_token) new_session = subject.get_session() values.update(value1) assert (new_session.get_attributes(values.keys()) == values.keys()) class Value4: pass with pytest.raises(CBOREncodeError): new_session.set_attribute('attribute4', Value4()) # not serializable
def test_subject_invalid_login(web_yosai, invalid_walter_username_password_token, mock_web_registry): with pytest.raises(AuthenticationException): with WebYosai.context(web_yosai, mock_web_registry): subject = WebYosai.get_current_subject() subject.login(invalid_walter_username_password_token)
def test_create_yosai_instance(): """ Create a new WebYosai instance from env_var settings and from file_path settings. This subsequently creates a configured WebSecurityManager. web_yosai is configured using the file_path approach """ first_yosai = WebYosai(env_var='YOSAI_SETTINGS') file_path = os.environ.get('YOSAI_SETTINGS') second_yosai = WebYosai(file_path=file_path) assert first_yosai.security_manager and second_yosai.security_manager
def test_new_session_at_login(web_yosai, mock_web_registry, valid_thedude_username_password_token, valid_thedude_totp_token): """ At login, an anonymous session is deleted from cache and a new session is created. """ with WebYosai.context(web_yosai, mock_web_registry): subject = WebYosai.get_current_subject() old_session_id = subject.get_session().session_id try: subject.login(valid_thedude_username_password_token) except AdditionalAuthenticationRequired: subject.login(valid_thedude_totp_token) new_session_id = subject.get_session().session_id assert old_session_id != new_session_id
def test_login_clears_cache( thedude_identifier, caplog, web_yosai, mock_web_registry, valid_thedude_username_password_token, valid_thedude_totp_token): with WebYosai.context(web_yosai, mock_web_registry): new_web_subject = WebYosai.get_current_subject() try: new_web_subject.login(valid_thedude_username_password_token) except AdditionalAuthenticationRequired: new_web_subject.login(valid_thedude_totp_token) out = caplog.text assert 'Clearing cached authz_info for [thedude]' in out new_web_subject.logout()
def test_web_yosai_get_current_webregistry(web_yosai, monkeypatch): mock_stack = ['webregistry'] monkeypatch.setattr(global_webregistry_context, 'stack', mock_stack) result = WebYosai.get_current_webregistry() assert result == 'webregistry'
def test_stopped_session(web_yosai, mock_web_registry, valid_thedude_username_password_token, valid_thedude_totp_token): """ When a user logs out, the user's session is stopped. """ with WebYosai.context(web_yosai, mock_web_registry): subject = WebYosai.get_current_subject() try: subject.login(valid_thedude_username_password_token) except AdditionalAuthenticationRequired: subject.login(valid_thedude_totp_token) subject.logout() assert (mock_web_registry.current_session_id is None and mock_web_registry.session_id_history[0][0] == 'SET' and mock_web_registry.session_id_history[1][0] == 'SET' and mock_web_registry.session_id_history[2][0] == 'DELETE')
def test_authenticated_subject_has_role_collective( web_yosai, mock_web_registry, thedude_testroles, valid_thedude_username_password_token, valid_thedude_totp_token): tr = thedude_testroles with WebYosai.context(web_yosai, mock_web_registry): new_web_subject = WebYosai.get_current_subject() try: new_web_subject.login(valid_thedude_username_password_token) except AdditionalAuthenticationRequired: new_web_subject.login(valid_thedude_totp_token) assert ((new_web_subject.has_role_collective(tr['roles'], all) is False) and (new_web_subject.has_role_collective(tr['roles'], any) is True)) new_web_subject.logout()
def test_web_context(web_yosai, mock_web_registry): """ When entering a new WebYosai context, a yosai instance is pushed onto a yosai_context stack and a web_registry is pushed onto a yosai_webregistry_context stack. When closing the context: the pushed yosai instance is popped from the yosai_context stack, the pushed web_registry is popped from the yosai_webregistry_context, and the current executing subject is popped from the global_subject_context stack. elements tested include: get_current_yosai get_current_webregistry get_current_subject """ # first ensure that the threadlocal is empty assert (global_subject_context.stack == [] and global_yosai_context.stack == [] and global_webregistry_context.stack == []) with WebYosai.context(web_yosai, mock_web_registry): assert (global_subject_context.stack == [] and global_yosai_context.stack == [web_yosai] and global_webregistry_context.stack == [mock_web_registry]) # this tests context exit assert (global_subject_context.stack == [] and global_yosai_context.stack == [] and global_webregistry_context.stack == [])
def tween(request): web_registry = PyramidWebRegistry(request) with WebYosai.context(yosai, web_registry): response = handler(request) return response
def launchpad(context, request): subject = WebYosai.get_current_subject() # check_roles looks like: [('role_name', Boolean), ...] check_roles = subject.has_role(['physician', 'patient', 'nurse_practitioner']) roles = [role for role, check in filter(lambda x: x[1], check_roles)] return {'roles': roles}
def yosai_from_settings(settings): """ Convenience method to construct a ``Yosai`` instance, referencing paste-deploy INI settings to obtain the envvar or filepath to yosai settings. :raises: KeyError when neither yosai env_var nor file_path are defined :returns: a Yosai instance """ env_var = settings['yosai.settings_filepath_envvar'] if env_var: return WebYosai(env_var=env_var) file_path = settings['yosai.settings_filepath'] if file_path: return WebYosai(file_path=file_path) raise ValueError('pyramid_yosai must have either an env_var or file_path')
def test_forget_remembered_identity( web_yosai, mock_web_registry, monkeypatch, remembered_valid_thedude_username_password_token, remembered_valid_thedude_totp_token): """ Logout and ensure that the identity is forgotten through removal of the remember_me cookie. """ with WebYosai.context(web_yosai, mock_web_registry): subject = WebYosai.get_current_subject() try: subject.login(remembered_valid_thedude_username_password_token) except AdditionalAuthenticationRequired: subject.login(remembered_valid_thedude_totp_token) assert mock_web_registry.current_remember_me is not None subject.logout() assert mock_web_registry.current_remember_me is None
def home(request): subject = WebYosai.get_current_subject() if subject.authenticated: next_url = request.route_url('launchpad') else: next_url = request.route_url('login') return HTTPFound(location=next_url)
def test_authenticated_subject_is_permitted_collective( web_yosai, mock_web_registry, thedude_testpermissions, valid_thedude_username_password_token, valid_thedude_totp_token): tp = thedude_testpermissions with WebYosai.context(web_yosai, mock_web_registry): new_web_subject = WebYosai.get_current_subject() try: new_web_subject.login(valid_thedude_username_password_token) except AdditionalAuthenticationRequired: new_web_subject.login(valid_thedude_totp_token) assert ((new_web_subject.is_permitted_collective(tp['perms'], any) is True) and (new_web_subject.is_permitted_collective(tp['perms'], all) is False)) new_web_subject.logout() with pytest.raises(ValueError): new_web_subject.is_permitted_collective(tp['perms'], any)
def test_remember_me_at_login(web_yosai, mock_web_registry, remembered_valid_thedude_username_password_token, remembered_valid_thedude_totp_token): """ Remember a user at login. The remember_me cookie is to be set at login when remember_me setting is True in UsernamePasswordToken. Confirm user identity. """ with WebYosai.context(web_yosai, mock_web_registry): new_subject = WebYosai.get_current_subject() assert mock_web_registry.current_remember_me is None try: new_subject.login(remembered_valid_thedude_username_password_token) except AdditionalAuthenticationRequired: new_subject.login(remembered_valid_thedude_totp_token) assert mock_web_registry.current_remember_me is not None
def test_absolute_timeout(web_yosai, mock_web_registry, monkeypatch, valid_thedude_username_password_token, valid_thedude_totp_token): """ A session that absolute timeouts will raise an exception at validation and the sessionmanager deletes the expired session from cache. """ monkeypatch.setattr(web_yosai.security_manager.session_manager, 'absolute_timeout', 1000) # milliseconds with WebYosai.context(web_yosai, mock_web_registry): subject = WebYosai.get_current_subject() try: subject.login(valid_thedude_username_password_token) except AdditionalAuthenticationRequired: subject.login(valid_thedude_totp_token) sleep(2) try: subject = WebYosai.get_current_subject() except mock_web_registry.mock_exception: assert (mock_web_registry.current_session_id is None and mock_web_registry.session_id_history[0][0] == 'SET')
def test_logout_clears_cache( thedude_identifier, web_yosai, mock_web_registry,thedude_testpermissions, caplog, valid_thedude_username_password_token, valid_thedude_totp_token): tp = thedude_testpermissions with WebYosai.context(web_yosai, mock_web_registry): new_web_subject = WebYosai.get_current_subject() try: new_web_subject.login(valid_thedude_username_password_token) except AdditionalAuthenticationRequired: new_web_subject.login(valid_thedude_totp_token) new_web_subject.is_permitted(tp['perms']) # caches authz_info new_web_subject.logout() out = caplog.text assert ('Clearing cached authc_info for [thedude]' in out and 'Clearing cached authz_info for [thedude]' in out)
def pending_rx(context, request): if request.method == "POST": approve_rx_requests(request.dbsession, request.POST) next_url = request.route_url('pending_rx') return HTTPFound(next_url) else: current_username = WebYosai.get_current_subject().identifiers.primary_identifier results = get_pending_physician_requests(request.dbsession, current_username).all() return {'results': results}
def test_authenticated_subject_is_permitted( web_yosai, mock_web_registry, thedude_testpermissions, valid_thedude_username_password_token, valid_thedude_totp_token): tp = thedude_testpermissions with WebYosai.context(web_yosai, mock_web_registry): new_web_subject = WebYosai.get_current_subject() try: new_web_subject.login(valid_thedude_username_password_token) except AdditionalAuthenticationRequired: new_web_subject.login(valid_thedude_totp_token) results = new_web_subject.is_permitted(tp['perms']) assert results == tp['expected_results'] new_web_subject.logout() with pytest.raises(ValueError): new_web_subject.is_permitted(tp['perms'])
def test_csrf_token_management(web_yosai, mock_web_registry, monkeypatch, valid_thedude_username_password_token, valid_thedude_totp_token): """ CSRF Token generation and retrieval from session state """ with WebYosai.context(web_yosai, mock_web_registry): subject = WebYosai.get_current_subject() old_session = subject.get_session() old_token = old_session.get_csrf_token() try: subject.login(valid_thedude_username_password_token) except AdditionalAuthenticationRequired: subject.login(valid_thedude_totp_token) new_session = subject.get_session() new_token = new_session.new_csrf_token() assert new_token != old_token
def test_flash_messages_management(web_yosai, mock_web_registry, monkeypatch, valid_thedude_username_password_token, valid_thedude_totp_token): """ flash messages saving and retrieval from session state """ with WebYosai.context(web_yosai, mock_web_registry): subject = WebYosai.get_current_subject() old_session = subject.get_session() msg = 'Flash Message One, Default Queue' msg2 = 'Flash Message Two, Default Queue' old_session.flash(msg) old_session.flash(msg2) msg3 = 'Flash Message Two' old_session.flash(msg3, queue='queue2') default_queue_flash_peek = old_session.peek_flash() default_queue_flash_pop = old_session.pop_flash() try: subject.login(valid_thedude_username_password_token) except AdditionalAuthenticationRequired: subject.login(valid_thedude_totp_token) new_session = subject.get_session() default_queue_flash_peek_new = new_session.peek_flash() default_queue_flash_pop_new = new_session.pop_flash() queue2_flash_peek_new = new_session.peek_flash('queue2') queue2_flash_pop_new = new_session.pop_flash('queue2') assert (default_queue_flash_peek == [msg, msg2] and default_queue_flash_pop == [msg, msg2] and default_queue_flash_peek_new == [] and default_queue_flash_pop_new is None and queue2_flash_peek_new == [msg3] and queue2_flash_pop_new == [msg3])
def request_rx(context, request): rx_request_form = RxRequestForm(request.POST) if request.method == "POST" and rx_request_form.validate(): add_rx_request(request.dbsession, rx_request_form.data['prescription']) # request.session.flash('RX Request Submitted.') next_url = request.route_url('request_rx') return HTTPFound(next_url) else: current_username = WebYosai.get_current_subject().identifiers.primary_identifier results = get_pending_patient_requests(request.dbsession, current_username).all() return {'rx_request_form': rx_request_form, 'results': results, 'user': current_username}
def write_rx(context, request): write_rx_form = WriteRXForm(request.POST) if request.method == 'POST' and write_rx_form.validate(): medicine = write_rx_form.data['medicine'].id perm = 'prescription:write:{0}'.format(medicine) current_user = WebYosai.get_current_subject() try: current_user.check_permission([perm]) except IdentifiersNotSetException: msg = ("Attempting to perform a user-only operation. The " "current Subject is NOT a user (they haven't been " "authenticated or remembered from a previous login). " "ACCESS DENIED.") raise HTTPUnauthorized(msg) except AuthorizationException: status_msg = "Access Denied. Insufficient Permission." return {'status_msg': status_msg} current_username = current_user.identifiers.primary_identifier create_rx(request.dbsession, current_username, write_rx_form.data['medicine'], write_rx_form.data['patient'], write_rx_form.data['title'], write_rx_form.data['fill_qty'], write_rx_form.data['num_fills']) # When a prescription gets created, a new resource-level permission could be # created in the yosai database, allowing resource-level authorization # for that new rx. However, time has not yet allowed support for adding new # resource to the yosai db. Adding this to TO-DO. #resource = ResourceModel(name=prescription.id) return {'status_msg': 'Successfully Wrote Rx'}
def login(request): login_form = LoginForm(request.POST) if request.method == "POST" and login_form.validate(): authc_token = UsernamePasswordToken(username=login_form.username.data, password=login_form.password.data, remember_me=login_form.remember_me.data) try: subject = WebYosai.get_current_subject() subject.login(authc_token) next_url = request.route_url('launchpad') return HTTPFound(location=next_url) except AuthenticationException: # request.session.flash('Invalid Login Credentials.') return {'login_form': login_form} else: return {'login_form': login_form}
def generate_csrf_token(self, context=None): # the context param isn't needed with yosai subject = WebYosai.get_current_subject() session = subject.get_session() return session.get_csrf_token()
def web_yosai(session_attributes): return WebYosai(env_var='YOSAI_WEB_SETTINGS', session_attributes=session_attributes)
def test_web_yosai_get_current_webregistry_raises(web_yosai, monkeypatch): mock_stack = [] monkeypatch.setattr(global_webregistry_context, 'stack', mock_stack) with pytest.raises(YosaiContextException): WebYosai.get_current_webregistry()