def test_client_secret_post_authentication(oidc_settings, app, simple_oidc_client, simple_user): utils.login(app, simple_user) redirect_uri = simple_oidc_client.redirect_uris.split()[0] params = { 'client_id': simple_oidc_client.client_id, 'scope': 'openid profile email', 'redirect_uri': redirect_uri, 'state': 'xxx', 'nonce': 'yyy', 'response_type': 'code', } authorize_url = make_url('oidc-authorize', params=params) response = app.get(authorize_url) response = response.form.submit('accept') location = urlparse.urlparse(response['Location']) query = urlparse.parse_qs(location.query) code = query['code'][0] token_url = make_url('oidc-token') response = app.post(token_url, params={ 'grant_type': 'authorization_code', 'code': code, 'redirect_uri': redirect_uri, 'client_id': simple_oidc_client.client_id, 'client_secret': simple_oidc_client.client_secret, }) assert 'error' not in response.json assert 'access_token' in response.json assert 'expires_in' in response.json assert 'id_token' in response.json assert response.json['token_type'] == 'Bearer'
def assertEqualsURL(self, url1, url2, **kwargs): '''Check that url1 is equals to url2 augmented with parameters kwargs in its query string. The string '*' is a special value, when used it just check that the given parameter exist in the first url, it does not check the exact value. ''' splitted1 = urlparse.urlsplit(url1) url2 = utils.make_url(url2, params=kwargs) splitted2 = urlparse.urlsplit(url2) for i, (elt1, elt2) in enumerate(zip(splitted1, splitted2)): if i == 3: elt1 = urlparse.parse_qs(elt1, True) elt2 = urlparse.parse_qs(elt2, True) for k, v in elt1.items(): elt1[k] = set(v) for k, v in elt2.items(): if v == ['*']: elt2[k] = elt1.get(k, v) else: elt2[k] = set(v) self.assertTrue( elt1 == elt2, "URLs are not equal: %s != %s" % (splitted1, splitted2))
def test_registration(app, db, settings, mailoutbox, external_redirect): next_url, good_next_url = external_redirect settings.LANGUAGE_CODE = 'en-us' settings.A2_VALIDATE_EMAIL_DOMAIN = can_resolve_dns() # disable existing attributes models.Attribute.objects.update(disabled=True) User = get_user_model() url = utils.make_url('registration_register', params={REDIRECT_FIELD_NAME: next_url}) response = app.get(url) response.form.set('email', '*****@*****.**') response = response.form.submit() assert urlparse(response['Location']).path == reverse('registration_complete') response = response.follow() assert '2 days' in response.content assert '*****@*****.**' in response.content assert len(mailoutbox) == 1 link = get_link_from_mail(mailoutbox[0]) # test password validation response = app.get(link) response.form.set('password1', 'toto') response.form.set('password2', 'toto') response = response.form.submit() assert '8 characters' in response.content # set valid password response.form.set('password1', 'T0==toto') response.form.set('password2', 'T0==toto') response = response.form.submit() if good_next_url: assert 'You have just created an account.' in response.content assert next_url in response.content else: assert urlparse(response['Location']).path == '/' response = response.follow() assert 'You have just created an account.' in response.content assert User.objects.count() == 1 assert len(mailoutbox) == 2 assert 'was successful' in mailoutbox[1].body new_user = User.objects.get() assert new_user.email == '*****@*****.**' assert new_user.username is None assert new_user.check_password('T0==toto') assert new_user.is_active assert not new_user.is_staff assert not new_user.is_superuser assert str(app.session['_auth_user_id']) == str(new_user.pk) response = app.get('/login/') response.form.set('username', '*****@*****.**') response.form.set('password', 'T0==toto') response = response.form.submit(name='login-password-submit') assert urlparse(response['Location']).path == reverse('auth_homepage')
def redirect_logout_list(self, request, next=None): from .models import OIDCProvider tokens = request.session.get('auth_oidc', {}).get('tokens', []) urls = [] if tokens: for token in tokens: provider = OIDCProvider.objects.get(pk=token['provider_pk']) # ignore providers wihtout SLO if not provider.end_session_endpoint: continue params = {} if 'id_token' in token['token_response']: params['id_token_hint'] = token['token_response'][ 'id_token'] if 'access_token' in token[ 'token_response'] and provider.token_revocation_endpoint: self.revoke_token(provider, token['token_response']['access_token']) params[ 'post_logout_redirect_uri'] = request.build_absolute_uri( reverse('auth_logout')) urls.append( make_url(provider.end_session_endpoint, params=params)) return urls
def get_success_url(self): try: redirect_url, next_field = app_settings.A2_REGISTRATION_REDIRECT except Exception: redirect_url = app_settings.A2_REGISTRATION_REDIRECT next_field = REDIRECT_FIELD_NAME if self.token and self.token.get(REDIRECT_FIELD_NAME): url = self.token[REDIRECT_FIELD_NAME] if redirect_url: url = make_url(redirect_url, params={next_field: url}) else: if redirect_url: url = redirect_url else: url = make_url(self.success_url) return url
def get(self, request): referrer = request.META['HTTP_REFERER'] next_url = request.REQUEST.get('service') or make_url('auth_homepage') if referrer: model = Service.objects.for_service(referrer) if model: return logout_view(request, next_url=next_url, check_referer=False, do_local=False) return redirect(request, next_url)
def test_registration_realm(app, db, settings, mailoutbox): settings.LANGUAGE_CODE = 'en-us' settings.A2_VALIDATE_EMAIL_DOMAIN = can_resolve_dns() settings.A2_REGISTRATION_REALM = 'realm' settings.A2_REDIRECT_WHITELIST = ['http://relying-party.org/'] settings.A2_REQUIRED_FIELDS = ['username'] # disable existing attributes models.Attribute.objects.update(disabled=True) User = get_user_model() next_url = 'http://relying-party.org/' url = utils.make_url('registration_register', params={REDIRECT_FIELD_NAME: next_url}) response = app.get(url) response.form.set('email', '*****@*****.**') response = response.form.submit() assert urlparse(response['Location']).path == reverse('registration_complete') response = response.follow() assert '2 days' in response.content assert '*****@*****.**' in response.content assert '2 days' in response.content assert len(mailoutbox) == 1 link = get_link_from_mail(mailoutbox[0]) # register response = app.get(link) response.form.set('username', 'toto') response.form.set('password1', 'T0==toto') response.form.set('password2', 'T0==toto') response = response.form.submit() assert 'You have just created an account.' in response.content assert next_url in response.content assert len(mailoutbox) == 2 assert 'was successful' in mailoutbox[1].body # verify user has expected attributes new_user = User.objects.get() assert new_user.username == 'toto@realm' assert new_user.email == '*****@*****.**' assert new_user.check_password('T0==toto') assert new_user.is_active assert not new_user.is_staff assert not new_user.is_superuser assert str(app.session['_auth_user_id']) == str(new_user.pk) # test login response = app.get('/login/') response.form.set('username', '*****@*****.**') response.form.set('password', 'T0==toto') response = response.form.submit(name='login-password-submit') assert urlparse(response['Location']).path == reverse('auth_homepage')
def get(self, request): referrer = request.META['HTTP_REFERER'] next_url = request.GET.get('service') or make_url('auth_homepage') if referrer: model = Service.objects.for_service(referrer) if model: return logout_view(request, next_url=next_url, check_referer=False, do_local=False) return redirect(request, next_url)
def authenticate(self, request, st): ''' Redirect to an login page, pass a cookie to the login page to associate the login event with the service ticket, if renew was asked ''' nonce = st.ticket_id next_url = make_url('a2-idp-cas-continue', params={ SERVICE_PARAM: st.service_url, NONCE_FIELD_NAME: nonce}) return login_require(request, next_url=next_url, params={NONCE_FIELD_NAME: nonce})
def authenticate(self, request, st): ''' Redirect to an login page, pass a cookie to the login page to associate the login event with the service ticket, if renew was asked ''' nonce = st.ticket_id next_url = make_url('a2-idp-cas-continue', params={ SERVICE_PARAM: st.service_url, NONCE_FIELD_NAME: nonce }) return login_require(request, next_url=next_url, params={NONCE_FIELD_NAME: nonce})
def logout(request): post_logout_redirect_uri = request.GET.get('post_logout_redirect_uri') state = request.GET.get('state') if post_logout_redirect_uri: providers = models.OIDCClient.objects.filter( post_logout_redirect_uris__contains=post_logout_redirect_uri) for provider in providers: if post_logout_redirect_uri in provider.post_logout_redirect_uris.split(): break else: messages.warning(request, _('Invalid post logout URI')) return redirect(request, settings.LOGIN_REDIRECT_URL) if state: post_logout_redirect_uri = make_url(post_logout_redirect_uri, params={'state': state}) # FIXME: do something with id_token_hint id_token_hint = request.GET.get('id_token_hint') return a2_logout(request, next_url=post_logout_redirect_uri, do_local=False, check_referer=False)
def test_registration_service_slug(oidc_settings, app, simple_oidc_client, simple_user, hooks, mailoutbox): redirect_uri = simple_oidc_client.redirect_uris.split()[0] params = { 'client_id': simple_oidc_client.client_id, 'scope': 'openid profile email', 'redirect_uri': redirect_uri, 'state': 'xxx', 'nonce': 'yyy', 'response_type': 'code', } authorize_url = make_url('oidc-authorize', params=params) response = app.get(authorize_url) location = urlparse.urlparse(response['Location']) query = urlparse.parse_qs(location.query) assert query['service'] == ['client'] response = response.follow().click('Register') location = urlparse.urlparse(response.request.url) query = urlparse.parse_qs(location.query) assert query['service'] == ['client'] response.form.set('email', '*****@*****.**') response = response.form.submit() assert len(mailoutbox) == 1 link = utils.get_link_from_mail(mailoutbox[0]) response = app.get(link) response.form.set('first_name', 'John') response.form.set('last_name', 'Doe') response.form.set('password1', 'T0==toto') response.form.set('password2', 'T0==toto') response = response.form.submit() assert hooks.event[0]['kwargs']['name'] == 'sso-request' assert hooks.event[0]['kwargs']['service'].slug == 'client' assert hooks.event[1]['kwargs']['name'] == 'registration' assert hooks.event[1]['kwargs']['service'] == 'client' assert hooks.event[2]['kwargs']['name'] == 'login' assert hooks.event[2]['kwargs']['how'] == 'email' assert hooks.event[2]['kwargs']['service'] == 'client'
def test_make_url(self): from authentic2.utils import make_url self.assertEqualsURL(make_url('../coin'), '../coin') self.assertEqualsURL(make_url('../boob', params={'next': '..'}), '../boob?next=..') self.assertEqualsURL(make_url('../boob', params={'next': '..'}, append={'xx': 'yy'}), '../boob?xx=yy&next=..') self.assertEqualsURL(make_url('../boob', params={'next': '..'}, append={'next': 'yy'}), '../boob?next=..&next=yy') self.assertEqualsURL(make_url('auth_login', params={'next': '/zob'}), '/login/?next=%2Fzob') self.assertEqualsURL(make_url('auth_login', params={'next': '/zob'}, fragment='a2-panel'), '/login/?next=%2Fzob#a2-panel')
def test_registration_redirect_tuple(app, db, settings, mailoutbox, external_redirect): next_url, good_next_url = external_redirect settings.A2_REGISTRATION_REDIRECT = 'http://cms/welcome/', 'target' settings.A2_VALIDATE_EMAIL_DOMAIN = can_resolve_dns() new_next_url = settings.A2_REGISTRATION_REDIRECT[0] if good_next_url: new_next_url += '?target=' + urlquote(next_url) # disable existing attributes models.Attribute.objects.update(disabled=True) url = utils.make_url('registration_register', params={REDIRECT_FIELD_NAME: next_url}) response = app.get(url) response.form.set('email', '*****@*****.**') response = response.form.submit() response = response.follow() link = get_link_from_mail(mailoutbox[0]) response = app.get(link) response.form.set('password1', 'T0==toto') response.form.set('password2', 'T0==toto') response = response.form.submit() assert new_next_url in response.content
def do_test_sso(self, make_authn_request_kwargs={}, check_federation=True, cancel=False, default_name_id_format='persistent', authorized_service=True): self.setup(default_name_id_format=default_name_id_format) client = Client() # Launch an AuthnRequest url, body, request_id = self.make_authn_request( **make_authn_request_kwargs) response = client.get(url) self.assertRedirectsComplex( response, reverse('auth_login'), **{ 'nonce': '*', SERVICE_FIELD_NAME: self.slug, REDIRECT_FIELD_NAME: make_url('a2-idp-saml-continue', params={ NONCE_FIELD_NAME: request_id, }), }) nonce = urlparse.parse_qs( urlparse.urlparse(response['Location']).query)['nonce'][0] url = response['Location'] response = client.get(url) self.assertEqual(response.status_code, 200) self.assertEqual(response['Content-Type'].split(';')[0], 'text/html') self.assertInHTML( u'<button class="cancel-button" name="cancel">%s</button>' % _('Cancel'), response.content, count=1) if cancel: response = client.post(url, { 'cancel': 1, }) self.assertRedirectsComplex(response, reverse('a2-idp-saml-continue'), cancel='*', nonce=nonce) response = client.get(response['Location']) self.assertEqual(response.status_code, 200) self.assertEqual(response['Content-type'].split(';')[0], 'text/html') doc = parse(StringIO.StringIO(response.content)).getroot() self.assertEqual(len(doc.forms), 1, msg='the number of forms is not 1') self.assertEqual(doc.forms[0].get('action'), '%s/sso/POST' % self.base_url) self.assertIn('SAMLResponse', doc.forms[0].fields) saml_response = doc.forms[0].fields['SAMLResponse'] try: base64.b64decode(saml_response) except TypeError: self.fail('SAMLResponse is not base64 encoded: %s' % saml_response) with self.assertRaises(lasso.ProfileRequestDeniedError): assertion = self.parse_authn_response(saml_response) elif not authorized_service: self.liberty_provider.add_authorized_role(self.role_authorized) # User without the authorized role role_goth = Role.objects.create(name='Goth Kids', slug='goth-kids') self.user.roles.all().delete() self.user.roles.add(role_goth) response = client.post( url, { 'username': self.email, 'password': self.password, 'login-password-submit': 1 }) response = client.get(response['Location']) assert 'https://whatever.com/loser/' in response.content # User with the authorized role self.user.roles.add(self.role_authorized) response = client.post( url, { 'username': self.email, 'password': self.password, 'login-password-submit': 1 }) response = client.get(response['Location']) assert 'SAMLResponse' in response.content else: response = client.post( url, { 'username': self.email, 'password': self.password, 'login-password-submit': 1, }) self.assertRedirectsComplex(response, reverse('a2-idp-saml-continue'), nonce=nonce) response = client.get(response['Location']) self.assertEqual(response.status_code, 200) self.assertEqual(response['Content-type'].split(';')[0], 'text/html') doc = parse(StringIO.StringIO(response.content)).getroot() self.assertEqual(len(doc.forms), 1, msg='the number of forms is not 1') self.assertEqual(doc.forms[0].get('action'), '%s/sso/POST' % self.base_url) self.assertIn('SAMLResponse', doc.forms[0].fields) saml_response = doc.forms[0].fields['SAMLResponse'] try: base64.b64decode(saml_response) except TypeError: self.fail('SAMLResponse is not base64 encoded: %s' % saml_response) login = self.parse_authn_response(saml_response) assertion = login.assertion session_not_on_or_after = login.assertion.authnStatement[ 0].sessionNotOnOrAfter assert session_not_on_or_after is not None assert (datetime.datetime.strptime(session_not_on_or_after, '%Y-%m-%dT%H:%M:%SZ') > datetime.datetime.utcnow()) assertion_xml = assertion.exportToXml() namespaces = { 'saml': lasso.SAML2_ASSERTION_HREF, } constraints = () # check nameid if check_federation: format = make_authn_request_kwargs.get('format') if not format: if self.default_sp_options_idp_policy.default_name_id_format == 'username': self.assertEqual( login.assertion.subject.nameID.format, lasso.SAML2_NAME_IDENTIFIER_FORMAT_UNSPECIFIED) self.assertEqual( login.assertion.subject.nameID.content, self.user.username.encode('utf-8')) elif self.default_sp_options_idp_policy.default_name_id_format == 'uuid': self.assertEqual( login.assertion.subject.nameID.format, lasso.SAML2_NAME_IDENTIFIER_FORMAT_UNSPECIFIED) self.assertEqual( login.assertion.subject.nameID.content, self.user.uuid.encode('utf-8')) elif format == lasso.SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT: federation = saml_models.LibertyFederation.objects.get() constraints += ( ('/saml:Assertion/saml:Subject/saml:NameID', federation.name_id_content), ('/saml:Assertion/saml:Subject/saml:NameID/@Format', lasso.SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT), ('/saml:Assertion/saml:Subject/saml:NameID/@SPNameQualifier', '%s/' % self.base_url), ) elif (format == lasso.SAML2_NAME_IDENTIFIER_FORMAT_EMAIL or (not format and default_name_id_format == 'email')): constraints += ( ('/saml:Assertion/saml:Subject/saml:NameID', self.email), ('/saml:Assertion/saml:Subject/saml:NameID/@Format', lasso.SAML2_NAME_IDENTIFIER_FORMAT_EMAIL), ) constraints += ( ("/saml:Assertion/saml:AttributeStatement/saml:Attribute[@Name='first-name']/" "@NameFormat", lasso.SAML2_ATTRIBUTE_NAME_FORMAT_BASIC), ("/saml:Assertion/saml:AttributeStatement/saml:Attribute[@Name='first-name']/" "@FriendlyName", 'First name'), ("/saml:Assertion/saml:AttributeStatement/saml:Attribute[@Name='first-name']/" "saml:AttributeValue", 'John'), ("/saml:Assertion/saml:AttributeStatement/saml:Attribute[@Name='last-name']/" "@NameFormat", lasso.SAML2_ATTRIBUTE_NAME_FORMAT_BASIC), ("/saml:Assertion/saml:AttributeStatement/saml:Attribute[@Name='last-name']/" "@FriendlyName", 'Last name'), ("/saml:Assertion/saml:AttributeStatement/saml:Attribute[@Name='last-name']/" "saml:AttributeValue", 'Doe'), ("/saml:Assertion/saml:AttributeStatement/saml:Attribute[@Name='superuser']/" "@NameFormat", lasso.SAML2_ATTRIBUTE_NAME_FORMAT_BASIC), ("/saml:Assertion/saml:AttributeStatement/saml:Attribute[@Name='superuser']/" "@FriendlyName", 'Superuser status'), ("/saml:Assertion/saml:AttributeStatement/saml:Attribute[@Name='superuser']/" "saml:AttributeValue", 'true'), ("/saml:Assertion/saml:AttributeStatement/saml:Attribute[@Name='code_code']/" "@NameFormat", lasso.SAML2_ATTRIBUTE_NAME_FORMAT_BASIC), ("/saml:Assertion/saml:AttributeStatement/saml:Attribute[@Name='code_code']/" "@FriendlyName", 'code'), ("/saml:Assertion/saml:AttributeStatement/saml:Attribute[@Name='code_code']/" "saml:AttributeValue", '1234'), ("/saml:Assertion/saml:AttributeStatement/saml:Attribute[@Name='mobile']/" "@NameFormat", lasso.SAML2_ATTRIBUTE_NAME_FORMAT_BASIC), ("/saml:Assertion/saml:AttributeStatement/saml:Attribute[@Name='mobile']/" "@FriendlyName", 'mobile'), ("/saml:Assertion/saml:AttributeStatement/saml:Attribute[@Name='mobile']/" "saml:AttributeValue", '5678'), ("/saml:Assertion/saml:AttributeStatement/saml:Attribute[@Name='verified_attributes']/" "@NameFormat", lasso.SAML2_ATTRIBUTE_NAME_FORMAT_BASIC), ("/saml:Assertion/saml:AttributeStatement/saml:Attribute[@Name='verified_attributes']/" "@FriendlyName", 'Verified attributes'), ("/saml:Assertion/saml:AttributeStatement/saml:Attribute[@Name='verified_attributes']/" "saml:AttributeValue", set(['code_code', 'mobile'])), ) self.assertXPathConstraints(assertion_xml, constraints, namespaces)
def test_invalid_request(caplog, oidc_settings, oidc_client, simple_user, app): redirect_uri = oidc_client.redirect_uris.split()[0] if oidc_client.authorization_flow == oidc_client.FLOW_AUTHORIZATION_CODE: fragment = False response_type = 'code' elif oidc_client.authorization_flow == oidc_client.FLOW_IMPLICIT: fragment = True response_type = 'id_token token' else: raise NotImplementedError # client_id authorize_url = make_url('oidc-authorize', params={}) response = app.get(authorize_url, status=400) assert 'missing parameter \'client_id\'' in response.content # redirect_uri authorize_url = make_url('oidc-authorize', params={ 'client_id': oidc_client.client_id, }) response = app.get(authorize_url, status=400) assert 'missing parameter \'redirect_uri\'' in response.content # invalid client_id authorize_url = make_url('oidc-authorize', params={ 'client_id': 'xxx', 'redirect_uri': redirect_uri, }) response = app.get(authorize_url, status=400) assert 'unknown client_id' in response.content # missing response_type authorize_url = make_url('oidc-authorize', params={ 'client_id': oidc_client.client_id, 'redirect_uri': redirect_uri, }) response = app.get(authorize_url) assert_oidc_error(response, 'invalid_request', 'missing parameter \'response_type\'', fragment=fragment) logrecord = [rec for rec in caplog.records if rec.funcName == 'authorization_error'][0] assert logrecord.levelname == 'WARNING' assert logrecord.redirect_uri == 'https://example.com/callback' assert 'missing parameter \'response_type\'' in logrecord.message # missing scope authorize_url = make_url('oidc-authorize', params={ 'client_id': oidc_client.client_id, 'redirect_uri': redirect_uri, 'response_type': 'code', }) response = app.get(authorize_url) assert_oidc_error(response, 'invalid_request', 'missing parameter \'scope\'', fragment=fragment) # invalid max_age authorize_url = make_url('oidc-authorize', params={ 'client_id': oidc_client.client_id, 'redirect_uri': redirect_uri, 'response_type': 'code', 'scope': 'openid', 'max_age': 'xxx', }) response = app.get(authorize_url) assert_oidc_error(response, 'invalid_request', 'max_age is not', fragment=fragment) authorize_url = make_url('oidc-authorize', params={ 'client_id': oidc_client.client_id, 'redirect_uri': redirect_uri, 'response_type': 'code', 'scope': 'openid', 'max_age': '-1', }) response = app.get(authorize_url) assert_oidc_error(response, 'invalid_request', 'max_age is not', fragment=fragment) # invalid redirect_uri authorize_url = make_url('oidc-authorize', params={ 'client_id': oidc_client.client_id, 'redirect_uri': 'xxx', 'response_type': 'code', 'scope': 'openid', }) response = app.get(authorize_url) assert_oidc_error(response, 'invalid_request', 'unauthorized redirect_uri', fragment=fragment) # unsupported response_type authorize_url = make_url('oidc-authorize', params={ 'client_id': oidc_client.client_id, 'redirect_uri': redirect_uri, 'response_type': 'xxx', 'scope': 'openid', }) response = app.get(authorize_url) if oidc_client.authorization_flow == oidc_client.FLOW_AUTHORIZATION_CODE: assert_oidc_error(response, 'unsupported_response_type', 'only code is supported') elif oidc_client.authorization_flow == oidc_client.FLOW_IMPLICIT: assert_oidc_error(response, 'unsupported_response_type', 'only "id_token token" or "id_token" are supported', fragment=fragment) # openid scope is missing authorize_url = make_url('oidc-authorize', params={ 'client_id': oidc_client.client_id, 'redirect_uri': redirect_uri, 'response_type': response_type, 'scope': 'profile', }) response = app.get(authorize_url) assert_oidc_error(response, 'invalid_request', 'openid scope is missing', fragment=fragment) # use of an unknown scope authorize_url = make_url('oidc-authorize', params={ 'client_id': oidc_client.client_id, 'redirect_uri': redirect_uri, 'response_type': response_type, 'scope': 'openid email profile zob', }) response = app.get(authorize_url) assert_oidc_error(response, 'invalid_scope', fragment=fragment) # restriction on scopes oidc_settings.A2_IDP_OIDC_SCOPES = ['openid'] authorize_url = make_url('oidc-authorize', params={ 'client_id': oidc_client.client_id, 'redirect_uri': redirect_uri, 'response_type': response_type, 'scope': 'openid email', }) response = app.get(authorize_url) assert_oidc_error(response, 'invalid_scope', fragment=fragment) del oidc_settings.A2_IDP_OIDC_SCOPES # cancel authorize_url = make_url('oidc-authorize', params={ 'client_id': oidc_client.client_id, 'redirect_uri': redirect_uri, 'response_type': response_type, 'scope': 'openid email profile', 'cancel': '1', }) response = app.get(authorize_url) assert_oidc_error(response, 'access_denied', error_description='user did not authenticate', fragment=fragment) # prompt=none authorize_url = make_url('oidc-authorize', params={ 'client_id': oidc_client.client_id, 'redirect_uri': redirect_uri, 'response_type': response_type, 'scope': 'openid email profile', 'prompt': 'none', }) response = app.get(authorize_url) assert_oidc_error(response, 'login_required', error_description='prompt is none', fragment=fragment) utils.login(app, simple_user) # prompt=none max_age=0 authorize_url = make_url('oidc-authorize', params={ 'client_id': oidc_client.client_id, 'redirect_uri': redirect_uri, 'response_type': response_type, 'scope': 'openid email profile', 'max_age': '0', 'prompt': 'none', }) response = app.get(authorize_url) assert_oidc_error(response, 'login_required', error_description='prompt is none', fragment=fragment) # max_age=0 authorize_url = make_url('oidc-authorize', params={ 'client_id': oidc_client.client_id, 'redirect_uri': redirect_uri, 'response_type': response_type, 'scope': 'openid email profile', 'max_age': '0', }) response = app.get(authorize_url) assert urlparse.urlparse(response['Location']).path == reverse('auth_login') # prompt=login authorize_url = make_url('oidc-authorize', params={ 'client_id': oidc_client.client_id, 'redirect_uri': redirect_uri, 'response_type': response_type, 'scope': 'openid email profile', 'prompt': 'login', }) response = app.get(authorize_url) assert urlparse.urlparse(response['Location']).path == reverse('auth_login') # user refuse authorization authorize_url = make_url('oidc-authorize', params={ 'client_id': oidc_client.client_id, 'redirect_uri': redirect_uri, 'response_type': response_type, 'scope': 'openid email profile', 'prompt': 'none', }) response = app.get(authorize_url) if oidc_client.authorization_mode != oidc_client.AUTHORIZATION_MODE_NONE: assert_oidc_error(response, 'consent_required', error_description='prompt is none', fragment=fragment) # user refuse authorization authorize_url = make_url('oidc-authorize', params={ 'client_id': oidc_client.client_id, 'redirect_uri': redirect_uri, 'response_type': response_type, 'scope': 'openid email profile', }) response = app.get(authorize_url) if oidc_client.authorization_mode != oidc_client.AUTHORIZATION_MODE_NONE: response = response.form.submit('refuse') assert_oidc_error(response, 'access_denied', error_description='user denied access', fragment=fragment) # authorization exists authorize = OIDCAuthorization.objects.create( client=oidc_client, user=simple_user, scopes='openid profile email', expired=now() + datetime.timedelta(days=2)) response = app.get(authorize_url) if oidc_client.authorization_flow == oidc_client.FLOW_AUTHORIZATION_CODE: assert_authorization_response(response, code=None, fragment=fragment) elif oidc_client.authorization_flow == oidc_client.FLOW_IMPLICIT: assert_authorization_response(response, access_token=None, id_token=None, expires_in=None, token_type=None, fragment=fragment) # client ask for explicit authorization authorize_url = make_url('oidc-authorize', params={ 'client_id': oidc_client.client_id, 'redirect_uri': redirect_uri, 'response_type': response_type, 'scope': 'openid email profile', 'prompt': 'consent', }) response = app.get(authorize_url) assert 'a2-oidc-authorization-form' in response.content # check all authorization have been deleted, it's our policy assert OIDCAuthorization.objects.count() == 0 if oidc_client.authorization_mode == oidc_client.AUTHORIZATION_MODE_NONE: # authorization mode is none, but explicit consent is asked, we validate it response = response.form.submit('accept') # authorization has expired OIDCCode.objects.all().delete() authorize.expired = now() - datetime.timedelta(days=2) authorize.save() response = app.get(authorize_url) assert 'a2-oidc-authorization-form' in response.content authorize.expired = now() + datetime.timedelta(days=2) authorize.scopes = 'openid profile' authorize.save() assert OIDCAuthorization.objects.count() == 1 response = response.form.submit('accept') assert OIDCAuthorization.objects.count() == 1 # old authorizations have been deleted assert OIDCAuthorization.objects.get().pk != authorize.pk # check expired codes if oidc_client.authorization_flow == oidc_client.FLOW_AUTHORIZATION_CODE: assert OIDCCode.objects.count() == 1 code = OIDCCode.objects.get() assert code.is_valid() # make code expire code.expired = now() - datetime.timedelta(seconds=120) assert not code.is_valid() code.save() location = urlparse.urlparse(response['Location']) query = urlparse.parse_qs(location.query) assert set(query.keys()) == set(['code']) assert query['code'] == [code.uuid] code = query['code'][0] token_url = make_url('oidc-token') response = app.post(token_url, params={ 'grant_type': 'authorization_code', 'code': code, 'redirect_uri': oidc_client.redirect_uris.split()[0], }, headers=client_authentication_headers(oidc_client), status=400) assert 'error' in response.json assert response.json['error'] == 'invalid_request' assert response.json['desc'] == 'code has expired or user is disconnected' # invalid logout logout_url = make_url('oidc-logout', params={ 'post_logout_redirect_uri': 'https://whatever.com/', }) response = app.get(logout_url) assert '_auth_user_id' in app.session assert 'Location' in response.headers # check code expiration after logout if oidc_client.authorization_flow == oidc_client.FLOW_AUTHORIZATION_CODE: code = OIDCCode.objects.get() code.expired = now() + datetime.timedelta(seconds=120) code.save() assert code.is_valid() utils.logout(app) code = OIDCCode.objects.get() assert not code.is_valid() response = app.post(token_url, params={ 'grant_type': 'authorization_code', 'code': code.uuid, 'redirect_uri': oidc_client.redirect_uris.split()[0], }, headers=client_authentication_headers(oidc_client), status=400) assert 'error' in response.json assert response.json['error'] == 'invalid_request' assert response.json['desc'] == 'code has expired or user is disconnected'
def test_role_control_access(login_first, oidc_settings, oidc_client, simple_user, app): # authorized_role role_authorized = get_role_model().objects.create( name='Goth Kids', slug='goth-kids', ou=get_default_ou()) oidc_client.add_authorized_role(role_authorized) redirect_uri = oidc_client.redirect_uris.split()[0] params = { 'client_id': oidc_client.client_id, 'scope': 'openid profile email', 'redirect_uri': redirect_uri, 'state': 'xxx', 'nonce': 'yyy', } if oidc_client.authorization_flow == oidc_client.FLOW_AUTHORIZATION_CODE: params['response_type'] = 'code' elif oidc_client.authorization_flow == oidc_client.FLOW_IMPLICIT: params['response_type'] = 'token id_token' authorize_url = make_url('oidc-authorize', params=params) if login_first: utils.login(app, simple_user) # user not authorized response = app.get(authorize_url) assert 'https://example.com/southpark/' in response.content # user authorized simple_user.roles.add(role_authorized) simple_user.save() response = app.get(authorize_url) if not login_first: response = response.follow() response.form.set('username', simple_user.username) response.form.set('password', simple_user.username) response = response.form.submit(name='login-password-submit') response = response.follow() if oidc_client.authorization_mode != oidc_client.AUTHORIZATION_MODE_NONE: response = response.form.submit('accept') assert OIDCAuthorization.objects.get() if oidc_client.authorization_flow == oidc_client.FLOW_AUTHORIZATION_CODE: code = OIDCCode.objects.get() location = urlparse.urlparse(response['Location']) if oidc_client.authorization_flow == oidc_client.FLOW_AUTHORIZATION_CODE: query = urlparse.parse_qs(location.query) code = query['code'][0] token_url = make_url('oidc-token') response = app.post(token_url, params={ 'grant_type': 'authorization_code', 'code': code, 'redirect_uri': oidc_client.redirect_uris.split()[0], }, headers=client_authentication_headers(oidc_client)) id_token = response.json['id_token'] elif oidc_client.authorization_flow == oidc_client.FLOW_IMPLICIT: query = urlparse.parse_qs(location.fragment) id_token = query['id_token'][0] if oidc_client.idtoken_algo == oidc_client.ALGO_RSA: key = JWKSet.from_json(app.get(reverse('oidc-certs')).content) elif oidc_client.idtoken_algo == oidc_client.ALGO_HMAC: key = JWK(kty='oct', k=base64.b64encode(oidc_client.client_secret.encode('utf-8'))) else: raise NotImplementedError jwt = JWT(jwt=id_token, key=key) claims = json.loads(jwt.claims) if login_first: assert claims['acr'] == '0' else: assert claims['acr'] == '1'
def test_authorization_code_sso(login_first, oidc_settings, oidc_client, simple_user, app): redirect_uri = oidc_client.redirect_uris.split()[0] params = { 'client_id': oidc_client.client_id, 'scope': 'openid profile email', 'redirect_uri': redirect_uri, 'state': 'xxx', 'nonce': 'yyy', } if oidc_client.authorization_flow == oidc_client.FLOW_AUTHORIZATION_CODE: params['response_type'] = 'code' elif oidc_client.authorization_flow == oidc_client.FLOW_IMPLICIT: params['response_type'] = 'token id_token' authorize_url = make_url('oidc-authorize', params=params) if login_first: utils.login(app, simple_user) response = app.get(authorize_url) if not login_first: response = response.follow() assert response.request.path == reverse('auth_login') response.form.set('username', simple_user.username) response.form.set('password', simple_user.username) response = response.form.submit(name='login-password-submit') response = response.follow() assert response.request.path == reverse('oidc-authorize') if oidc_client.authorization_mode != OIDCClient.AUTHORIZATION_MODE_NONE: assert 'a2-oidc-authorization-form' in response.content assert OIDCAuthorization.objects.count() == 0 assert OIDCCode.objects.count() == 0 assert OIDCAccessToken.objects.count() == 0 response = response.form.submit('accept') assert OIDCAuthorization.objects.count() == 1 authz = OIDCAuthorization.objects.get() assert authz.client == oidc_client assert authz.user == simple_user assert authz.scope_set() == set('openid profile email'.split()) assert authz.expired >= now() if oidc_client.authorization_flow == oidc_client.FLOW_AUTHORIZATION_CODE: assert OIDCCode.objects.count() == 1 code = OIDCCode.objects.get() assert code.client == oidc_client assert code.user == simple_user assert code.scope_set() == set('openid profile email'.split()) assert code.state == 'xxx' assert code.nonce == 'yyy' assert code.redirect_uri == redirect_uri assert code.session_key == app.session.session_key assert code.auth_time <= now() assert code.expired >= now() assert response['Location'].startswith(redirect_uri) location = urlparse.urlparse(response['Location']) if oidc_client.authorization_flow == oidc_client.FLOW_AUTHORIZATION_CODE: query = urlparse.parse_qs(location.query) assert set(query.keys()) == set(['code', 'state']) assert query['code'] == [code.uuid] code = query['code'][0] assert query['state'] == ['xxx'] token_url = make_url('oidc-token') response = app.post(token_url, params={ 'grant_type': 'authorization_code', 'code': code, 'redirect_uri': oidc_client.redirect_uris.split()[0], }, headers=client_authentication_headers(oidc_client)) assert 'error' not in response.json assert 'access_token' in response.json assert 'expires_in' in response.json assert 'id_token' in response.json assert response.json['token_type'] == 'Bearer' access_token = response.json['access_token'] id_token = response.json['id_token'] elif oidc_client.authorization_flow == oidc_client.FLOW_IMPLICIT: assert location.fragment query = urlparse.parse_qs(location.fragment) assert OIDCAccessToken.objects.count() == 1 access_token = OIDCAccessToken.objects.get() assert set(query.keys()) == set(['access_token', 'token_type', 'expires_in', 'id_token', 'state']) assert query['access_token'] == [access_token.uuid] assert query['token_type'] == ['Bearer'] assert query['state'] == ['xxx'] access_token = query['access_token'][0] id_token = query['id_token'][0] if oidc_client.idtoken_algo == oidc_client.ALGO_RSA: key = JWKSet.from_json(app.get(reverse('oidc-certs')).content) elif oidc_client.idtoken_algo == oidc_client.ALGO_HMAC: key = JWK(kty='oct', k=base64.b64encode(oidc_client.client_secret.encode('utf-8'))) else: raise NotImplementedError jwt = JWT(jwt=id_token, key=key) claims = json.loads(jwt.claims) assert set(claims) >= set(['iss', 'sub', 'aud', 'exp', 'iat', 'nonce', 'auth_time', 'acr']) assert claims['nonce'] == 'yyy' assert response.request.url.startswith(claims['iss']) assert claims['aud'] == oidc_client.client_id assert parse_timestamp(claims['iat']) <= now() assert parse_timestamp(claims['auth_time']) <= now() exp_delta = (parse_timestamp(claims['exp']) - now()).total_seconds() assert exp_delta > 0 if oidc_client.idtoken_duration: assert abs(exp_delta - oidc_client.idtoken_duration.total_seconds()) < 2 else: assert abs(exp_delta - 30) < 2 if login_first: assert claims['acr'] == '0' else: assert claims['acr'] == '1' assert claims['sub'] == make_sub(oidc_client, simple_user) assert claims['preferred_username'] == simple_user.username assert claims['given_name'] == simple_user.first_name assert claims['family_name'] == simple_user.last_name assert claims['email'] == simple_user.email assert claims['email_verified'] is False user_info_url = make_url('oidc-user-info') response = app.get(user_info_url, headers=bearer_authentication_headers(access_token)) assert response.json['sub'] == make_sub(oidc_client, simple_user) assert response.json['preferred_username'] == simple_user.username assert response.json['given_name'] == simple_user.first_name assert response.json['family_name'] == simple_user.last_name assert response.json['email'] == simple_user.email assert response.json['email_verified'] is False # when adding extra attributes OIDCClaim.objects.create(client=oidc_client, name='ou', value='django_user_ou_name', scopes='profile') OIDCClaim.objects.create(client=oidc_client, name='roles', value='a2_role_names', scopes='profile, role') simple_user.roles.add(get_role_model().objects.create( name='Whatever', slug='whatever', ou=get_default_ou())) response = app.get(user_info_url, headers=bearer_authentication_headers(access_token)) assert response.json['ou'] == simple_user.ou.name assert response.json['roles'][0] == 'Whatever' # check against a user without username simple_user.username = None simple_user.save() response = app.get(user_info_url, headers=bearer_authentication_headers(access_token)) assert 'preferred_username' not in response.json # Now logout if oidc_client.post_logout_redirect_uris: params = { 'post_logout_redirect_uri': oidc_client.post_logout_redirect_uris, 'state': 'xyz', } logout_url = make_url('oidc-logout', params=params) response = app.get(logout_url) assert 'You have been logged out' in response.content assert 'https://example.com/?state=xyz' in response.content assert '_auth_user_id' not in app.session else: response = app.get(make_url('account_management')) response = response.click('Logout') if oidc_client.frontchannel_logout_uri: iframes = response.pyquery('iframe[src="https://example.com/southpark/logout/"]') assert iframes if oidc_client.frontchannel_timeout: assert iframes.attr('onload').endswith(', %d)' % oidc_client.frontchannel_timeout) else: assert iframes.attr('onload').endswith(', 10000)')
def test_email_is_unique_double_registration(self): from django.contrib.auth import get_user_model from rest_framework import test from rest_framework import status # disable existing attributes models.Attribute.objects.update(disabled=True) user = self.reguser3 cred = self.reguser3_cred User = get_user_model() user_count = User.objects.count() client = test.APIClient() password = '******' username = '******' email = '*****@*****.**' return_url = 'http://sp.org/register/' payload = { 'email': email, 'username': username, 'ou': self.ou.slug, 'password': password, 'return_url': return_url, } outbox_level = len(mail.outbox) client.credentials(HTTP_AUTHORIZATION='Basic %s' % cred) response = client.post(reverse('a2-api-register'), content_type='application/json', data=json.dumps(payload)) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) self.assertIn('result', response.data) self.assertEqual(response.data['result'], 1) self.assertIn('token', response.data) token = response.data['token'] self.assertEqual(len(mail.outbox), outbox_level + 1) outbox_level = len(mail.outbox) # Second registration response2 = client.post(reverse('a2-api-register'), content_type='application/json', data=json.dumps(payload)) self.assertEqual(response2.status_code, status.HTTP_202_ACCEPTED) self.assertIn('result', response2.data) self.assertEqual(response2.data['result'], 1) self.assertIn('token', response2.data) token2 = response2.data['token'] self.assertEqual(len(mail.outbox), outbox_level + 1) activation_mail1, activation_mail2 = mail.outbox # User side - user click on first email client = Client() activation_url = get_link_from_mail(activation_mail1) response = client.get(activation_url) self.assertEqual(response.status_code, status.HTTP_200_OK) assert utils.make_url(return_url, params={'token': token}) in response.content self.assertEqual(User.objects.count(), user_count + 1) response = client.get(reverse('auth_homepage')) self.assertContains(response, username) last_user = User.objects.order_by('id').last() self.assertEqual(last_user.username, username) self.assertEqual(last_user.email, email) self.assertEqual(last_user.ou.slug, self.ou.slug) self.assertTrue(last_user.check_password(password)) # User click on second email client = Client() activation_url = get_link_from_mail(activation_mail2) response = client.get(activation_url) self.assertEqual(response.status_code, status.HTTP_302_FOUND) self.assertEqual(response['Location'], utils.make_url(return_url, params={'token': token2})) self.assertEqual(User.objects.count(), user_count + 1) response = client.get(reverse('auth_homepage')) self.assertContains(response, username) last_user2 = User.objects.order_by('id').last() self.assertEqual(User.objects.filter(email=payload['email']).count(), 1) self.assertEqual(last_user.id, last_user2.id) self.assertEqual(last_user2.username, username) self.assertEqual(last_user2.email, email) self.assertEqual(last_user2.ou.slug, self.ou.slug) self.assertTrue(last_user2.check_password(password)) # Test email is unique with case change client = test.APIClient() client.credentials(HTTP_AUTHORIZATION='Basic %s' % cred) payload = { 'email': email.upper(), 'username': username + '1', 'ou': self.ou.slug, 'password': password, 'return_url': return_url, } response = client.post(reverse('a2-api-register'), content_type='application/json', data=json.dumps(payload)) self.assertEqual(response.data['errors']['__all__'], [_('You already have an account')]) # Username is required payload = { 'email': '1' + email, 'ou': self.ou.slug, 'password': password, 'return_url': return_url, } response = client.post(reverse('a2-api-register'), content_type='application/json', data=json.dumps(payload)) self.assertEqual(response.data['errors']['__all__'], [_('Username is required in this ou')]) # Test username is unique payload = { 'email': '1' + email, 'username': username, 'ou': self.ou.slug, 'password': password, 'return_url': return_url, } response = client.post(reverse('a2-api-register'), content_type='application/json', data=json.dumps(payload)) self.assertEqual(response.data['errors']['__all__'], [_('You already have an account')])
def do_test_sso(self, make_authn_request_kwargs={}, check_federation=True, cancel=False, default_name_id_format='persistent'): self.setup(default_name_id_format=default_name_id_format) client = Client() # Launch an AuthnRequest url, body, request_id = self.make_authn_request( **make_authn_request_kwargs) response = client.get(url) self.assertRedirectsComplex(response, reverse('auth_login'), **{ 'nonce': '*', REDIRECT_FIELD_NAME: make_url('a2-idp-saml-continue', params={ NONCE_FIELD_NAME: request_id } ), }) nonce = urlparse.parse_qs( urlparse.urlparse( response['Location']).query)['nonce'][0] url = response['Location'] response = client.get(url) self.assertEqual(response.status_code, 200) self.assertEqual(response['Content-Type'].split(';')[0], 'text/html') self.assertInHTML(u'<input type="submit" name="cancel" ' 'value="%s"/>' % _('Cancel'), response.content, count=1) if cancel: response = client.post(url, { 'cancel': 1, }) self.assertRedirectsComplex(response, reverse('a2-idp-saml-continue'), cancel='*', nonce=nonce) response = client.get(response['Location']) self.assertEqual(response.status_code, 200) self.assertEqual(response['Content-type'].split(';')[0], 'text/html') doc = parse(StringIO.StringIO(response.content)).getroot() self.assertEqual(len(doc.forms), 1, msg='the number of forms is not 1') self.assertEqual(doc.forms[0].get('action'), '%s/sso/POST' % self.base_url) self.assertIn('SAMLResponse', doc.forms[0].fields) saml_response = doc.forms[0].fields['SAMLResponse'] try: saml_response_decoded = base64.b64decode(saml_response) except TypeError: self.fail('SAMLResponse is not base64 encoded: %s' % saml_response) with self.assertRaises(lasso.ProfileRequestDeniedError): assertion = self.parse_authn_response(saml_response) namespaces = {'samlp': lasso.SAML2_PROTOCOL_HREF} constraints = ( ("/samlp:Response/samlp:Status/samlp:StatusCode/@Value", lasso.SAML2_STATUS_CODE_RESPONDER), ("/samlp:Response/samlp:Status/samlp:StatusCode/samlp:StatusCode/@Value", lasso.SAML2_STATUS_CODE_REQUEST_DENIED), ("/samlp:Response/samlp:Status/samlp:StatusMessage", 'User canceled login process') ) self.assertXPathConstraints(saml_response_decoded, constraints, namespaces) else: response = client.post(url, { 'username': self.email, 'password': self.password, 'login-password-submit': 1, }) self.assertRedirectsComplex( response, reverse('a2-idp-saml-continue'), nonce=nonce) response = client.get(response['Location']) self.assertEqual(response.status_code, 200) self.assertEqual(response['Content-type'].split(';')[0], 'text/html') doc = parse(StringIO.StringIO(response.content)).getroot() self.assertEqual(len(doc.forms), 1, msg='the number of forms is not 1') self.assertEqual( doc.forms[0].get('action'), '%s/sso/POST' % self.base_url) self.assertIn('SAMLResponse', doc.forms[0].fields) saml_response = doc.forms[0].fields['SAMLResponse'] try: base64.b64decode(saml_response) except TypeError: self.fail('SAMLResponse is not base64 encoded: %s' % saml_response) login = self.parse_authn_response(saml_response) assertion = login.assertion assertion_xml = assertion.exportToXml() namespaces = { 'saml': lasso.SAML2_ASSERTION_HREF, } constraints = () # check nameid if check_federation: format = make_authn_request_kwargs.get('format') if not format: if self.default_sp_options_idp_policy.default_name_id_format == 'username': self.assertEqual(login.assertion.subject.nameID.format, lasso.SAML2_NAME_IDENTIFIER_FORMAT_UNSPECIFIED) self.assertEqual(login.assertion.subject.nameID.content, self.user.username.encode('utf-8')) elif self.default_sp_options_idp_policy.default_name_id_format == 'uuid': self.assertEqual(login.assertion.subject.nameID.format, lasso.SAML2_NAME_IDENTIFIER_FORMAT_UNSPECIFIED) self.assertEqual(login.assertion.subject.nameID.content, self.user.uuid.encode('utf-8')) elif format == lasso.SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT: federation = saml_models.LibertyFederation.objects.get() constraints += ( ('/saml:Assertion/saml:Subject/saml:NameID', federation.name_id_content), ('/saml:Assertion/saml:Subject/saml:NameID/@Format', lasso.SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT), ('/saml:Assertion/saml:Subject/saml:NameID/@SPNameQualifier', '%s/' % self.base_url), ) elif format == lasso.SAML2_NAME_IDENTIFIER_FORMAT_EMAIL or \ (not format and default_name_id_format == 'email'): constraints += ( ('/saml:Assertion/saml:Subject/saml:NameID', self.email), ('/saml:Assertion/saml:Subject/saml:NameID/@Format', lasso.SAML2_NAME_IDENTIFIER_FORMAT_EMAIL), ) constraints += ( ("/saml:Assertion/saml:AttributeStatement/saml:Attribute[@Name='first-name']/" "@NameFormat", lasso.SAML2_ATTRIBUTE_NAME_FORMAT_BASIC), ("/saml:Assertion/saml:AttributeStatement/saml:Attribute[@Name='first-name']/" "@FriendlyName", 'First name'), ("/saml:Assertion/saml:AttributeStatement/saml:Attribute[@Name='first-name']/" "saml:AttributeValue", 'John'), ("/saml:Assertion/saml:AttributeStatement/saml:Attribute[@Name='last-name']/" "@NameFormat", lasso.SAML2_ATTRIBUTE_NAME_FORMAT_BASIC), ("/saml:Assertion/saml:AttributeStatement/saml:Attribute[@Name='last-name']/" "@FriendlyName", 'Last name'), ("/saml:Assertion/saml:AttributeStatement/saml:Attribute[@Name='last-name']/" "saml:AttributeValue", 'Doe'), ("/saml:Assertion/saml:AttributeStatement/saml:Attribute[@Name='superuser']/" "@NameFormat", lasso.SAML2_ATTRIBUTE_NAME_FORMAT_BASIC), ("/saml:Assertion/saml:AttributeStatement/saml:Attribute[@Name='superuser']/" "@FriendlyName", 'Superuser status'), ("/saml:Assertion/saml:AttributeStatement/saml:Attribute[@Name='superuser']/" "saml:AttributeValue", 'true'), ) self.assertXPathConstraints(assertion_xml, constraints, namespaces)
def test_email_username_is_unique_double_registration(self): from django.contrib.auth import get_user_model from rest_framework import test from rest_framework import status # disable existing attributes models.Attribute.objects.update(disabled=True) cred = self.reguser3_cred User = get_user_model() user_count = User.objects.count() client = test.APIClient() password = '******' username = '******' email = '*****@*****.**' return_url = 'http://sp.org/register/' payload = { 'email': email, 'username': username, 'ou': self.ou.slug, 'password': password, 'return_url': return_url, } outbox_level = len(mail.outbox) client.credentials(HTTP_AUTHORIZATION='Basic %s' % cred) response = client.post(reverse('a2-api-register'), content_type='application/json', data=json.dumps(payload)) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) self.assertIn('result', response.data) self.assertEqual(response.data['result'], 1) self.assertIn('token', response.data) token = response.data['token'] self.assertEqual(len(mail.outbox), outbox_level + 1) outbox_level = len(mail.outbox) # Second registration payload['email'] = '*****@*****.**' response2 = client.post(reverse('a2-api-register'), content_type='application/json', data=json.dumps(payload)) self.assertEqual(response2.status_code, status.HTTP_202_ACCEPTED) self.assertIn('result', response2.data) self.assertEqual(response2.data['result'], 1) self.assertIn('token', response2.data) self.assertEqual(len(mail.outbox), outbox_level + 1) activation_mail1, activation_mail2 = mail.outbox # User side - user click on first email client = Client() activation_url = get_link_from_mail(activation_mail1) response = client.get(activation_url) self.assertEqual(response.status_code, status.HTTP_200_OK) assert utils.make_url(return_url, params={'token': token}) in response.content self.assertEqual(User.objects.count(), user_count + 1) response = client.get(reverse('auth_homepage')) self.assertContains(response, username) last_user = User.objects.order_by('id').last() self.assertEqual(last_user.username, username) self.assertEqual(last_user.email, email) self.assertEqual(last_user.ou.slug, self.ou.slug) self.assertTrue(last_user.check_password(password)) # User click on second email client = Client() activation_url = get_link_from_mail(activation_mail2) response = client.get(activation_url) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, 200) self.assertFormError( response, 'form', 'username', _('This username is already in use. Please supply a different username.'))