예제 #1
0
 def setUp(self):
     User = get_user_model()
     Role = get_role_model()
     self.user = User.objects.create_user(self.LOGIN,
                                          password=self.PASSWORD,
                                          email=self.EMAIL,
                                          first_name=self.FIRST_NAME,
                                          last_name=self.LAST_NAME)
     self.service = Service.objects.create(
         name=self.NAME,
         slug=self.SLUG,
         urls=self.URL,
         identifier_attribute='django_user_username',
         ou=get_default_ou(),
         logout_url=self.URL + 'logout/')
     self.service_attribute1 = Attribute.objects.create(
         service=self.service,
         slug='email',
         attribute_name='django_user_email')
     self.service2 = Service.objects.create(
         name=self.NAME2,
         slug=self.SLUG2,
         urls=self.URL2,
         ou=get_default_ou(),
         identifier_attribute='django_user_email')
     self.service2_attribute1 = Attribute.objects.create(
         service=self.service2,
         slug='username',
         attribute_name='django_user_username')
     self.authorized_service = Role.objects.create(name='rogue',
                                                   ou=get_default_ou())
     self.factory = RequestFactory()
예제 #2
0
 def setUp(self):
     User = get_user_model()
     self.user = User.objects.create_user(
         self.LOGIN, password=self.PASSWORD, email=self.EMAIL, first_name=self.FIRST_NAME, last_name=self.LAST_NAME
     )
     self.service = Service.objects.create(
         name=self.NAME,
         slug=self.SLUG,
         urls=self.URL,
         identifier_attribute="django_user_username",
         ou=get_default_ou(),
         logout_url=self.URL + "logout/",
     )
     self.service_attribute1 = Attribute.objects.create(
         service=self.service, slug="email", attribute_name="django_user_email"
     )
     self.service2 = Service.objects.create(
         name=self.NAME2,
         slug=self.SLUG2,
         urls=self.URL2,
         ou=get_default_ou(),
         identifier_attribute="django_user_email",
     )
     self.service2_attribute1 = Attribute.objects.create(
         service=self.service2, slug="username", attribute_name="django_user_username"
     )
     self.factory = RequestFactory()
예제 #3
0
def test_api_users_list_by_authorized_service(app, superuser):
    from authentic2.models import Service

    app.authorization = ('Basic', (superuser.username, superuser.username))
    User = get_user_model()
    Role = get_role_model()

    user1 = User.objects.create(username='******')
    user2 = User.objects.create(username='******')
    user3 = User.objects.create(username='******')

    role1 = Role.objects.create(name='role1')
    role2 = Role.objects.create(name='role2')
    role1.add_child(role2)
    user1.roles = [role1]
    user2.roles = [role2]

    service1 = Service.objects.create(ou=get_default_ou(), name='service1', slug='service1')
    service1.add_authorized_role(role1)

    service2 = Service.objects.create(ou=get_default_ou(), name='service2', slug='service2')

    resp = app.get('/api/users/')
    assert len(resp.json['results']) == 4

    resp = app.get('/api/users/?service-ou=default&service-slug=service1')
    assert len(resp.json['results']) == 2
    assert set(user['username'] for user in resp.json['results']) == set(['user1', 'user2'])

    resp = app.get('/api/users/?service-ou=default&service-slug=service2')
    assert len(resp.json['results']) == 4
예제 #4
0
def test_keep_password_in_session(slapd, settings, client):
    settings.LDAP_AUTH_SETTINGS = [{
        'url': [slapd.ldap_url],
        'basedn': u'o=ôrga',
        'use_tls': False,
        'keep_password_in_session': True,
    }]
    result = client.post('/login/', {
        'login-password-submit': '1',
        'username': USERNAME,
        'password': PASS.decode('utf-8')
    },
                         follow=True)
    assert result.status_code == 200
    assert 'Étienne Michu' in str(result)
    User = get_user_model()
    assert User.objects.count() == 1
    user = User.objects.get()
    assert user.username == u'%s@ldap' % USERNAME
    assert user.first_name == u'Étienne'
    assert user.last_name == 'Michu'
    assert user.ou == get_default_ou()
    assert not user.check_password(PASS)
    assert client.session['ldap-data']['password']
    assert DN in result.context['request'].user.ldap_data['password']
    assert crypto.aes_base64_decrypt(
        settings.SECRET_KEY,
        result.context['request'].user.ldap_data['password'][DN]) == PASS
예제 #5
0
def test_simple(slapd, settings, client):
    settings.LDAP_AUTH_SETTINGS = [{
        'url': [slapd.ldap_url],
        'basedn': u'o=ôrga',
        'use_tls': False,
    }]
    result = client.post('/login/', {
        'login-password-submit': '1',
        'username': USERNAME,
        'password': PASS
    },
                         follow=True)
    assert result.status_code == 200
    assert 'Étienne Michu' in str(result)
    User = get_user_model()
    assert User.objects.count() == 1
    user = User.objects.get()
    assert user.username == u'%s@ldap' % USERNAME
    assert user.first_name == u'Étienne'
    assert user.last_name == 'Michu'
    assert user.is_active is True
    assert user.is_superuser is False
    assert user.is_staff is False
    assert user.groups.count() == 0
    assert user.ou == get_default_ou()
    assert not user.check_password(PASS)
    assert 'password' not in client.session['ldap-data']
예제 #6
0
def test_expired_manager(db, simple_user):
    expired = now() - datetime.timedelta(seconds=1)
    not_expired = now() + datetime.timedelta(days=1)
    client = OIDCClient.objects.create(
        name='client',
        slug='client',
        ou=get_default_ou(),
        redirect_uris='https://example.com/')
    OIDCAuthorization.objects.create(
        client=client,
        user=simple_user,
        scopes='openid',
        expired=expired)
    OIDCAuthorization.objects.create(
        client=client,
        user=simple_user,
        scopes='openid',
        expired=not_expired)
    assert OIDCAuthorization.objects.count() == 2
    OIDCAuthorization.objects.cleanup()
    assert OIDCAuthorization.objects.count() == 1

    OIDCCode.objects.create(
        client=client,
        user=simple_user,
        scopes='openid',
        redirect_uri='https://example.com/',
        session_key='xxx',
        auth_time=now(),
        expired=expired)
    OIDCCode.objects.create(
        client=client,
        user=simple_user,
        scopes='openid',
        redirect_uri='https://example.com/',
        session_key='xxx',
        auth_time=now(),
        expired=not_expired)
    assert OIDCCode.objects.count() == 2
    OIDCCode.objects.cleanup()
    assert OIDCCode.objects.count() == 1

    OIDCAccessToken.objects.create(
        client=client,
        user=simple_user,
        scopes='openid',
        session_key='xxx',
        expired=expired)
    OIDCAccessToken.objects.create(
        client=client,
        user=simple_user,
        scopes='openid',
        session_key='xxx',
        expired=not_expired)
    assert OIDCAccessToken.objects.count() == 2
    OIDCAccessToken.objects.cleanup()
    assert OIDCAccessToken.objects.count() == 1
예제 #7
0
def test_role_members_show_all_ou(app, superuser, settings):
    Role = get_role_model()
    r = Role.objects.create(name='role', slug='role', ou=get_default_ou())
    url = reverse('a2-manager-role-members', kwargs={'pk': r.pk})
    response = login(app, superuser, url)
    assert not response.context['form'].fields['user'].queryset.query.where
    settings.A2_MANAGER_ROLE_MEMBERS_FROM_OU = True
    response = app.get(url)
    assert response.context['form'].fields['user'].queryset.query.where
예제 #8
0
def oidc_provider(request, db, oidc_provider_jwkset):
    idtoken_algo = request.param
    from authentic2_auth_oidc.utils import get_provider, get_provider_by_issuer
    get_provider.cache.clear()
    get_provider_by_issuer.cache.clear()
    if idtoken_algo == OIDCProvider.ALGO_RSA:
        jwkset = json.loads(oidc_provider_jwkset.export())
    else:
        jwkset = None
    provider = OIDCProvider.objects.create(
        id=1,
        ou=get_default_ou(),
        name='OIDIDP',
        issuer='https://idp.example.com/',
        authorization_endpoint='https://idp.example.com/authorize',
        token_endpoint='https://idp.example.com/token',
        end_session_endpoint='https://idp.example.com/logout',
        userinfo_endpoint='https://idp.example.com/user_info',
        token_revocation_endpoint='https://idp.example.com/revoke',
        max_auth_age=10,
        strategy=OIDCProvider.STRATEGY_CREATE,
        jwkset_json=jwkset,
        idtoken_algo=idtoken_algo,
    )
    provider.full_clean()
    OIDCClaimMapping.objects.create(
        provider=provider,
        claim='sub',
        attribute='username',
        idtoken_claim=True)
    OIDCClaimMapping.objects.create(
        provider=provider,
        claim='email',
        attribute='email')
    OIDCClaimMapping.objects.create(
        provider=provider,
        claim='email',
        required=True,
        attribute='email')
    OIDCClaimMapping.objects.create(
        provider=provider,
        claim='given_name',
        required=True,
        verified=OIDCClaimMapping.ALWAYS_VERIFIED,
        attribute='first_name')
    OIDCClaimMapping.objects.create(
        provider=provider,
        claim='family_name',
        required=True,
        verified=OIDCClaimMapping.VERIFIED_CLAIM,
        attribute='last_name')
    OIDCClaimMapping.objects.create(
        provider=provider,
        claim='ou',
        attribute='ou__slug')
    return provider
예제 #9
0
def admin(db):
    user = create_user(username='******',
                       first_name='global',
                       last_name='admin',
                       email='*****@*****.**',
                       is_active=True,
                       ou=get_default_ou())
    Role = get_role_model()
    user.roles.add(Role.objects.get(slug='_a2-manager'))
    return user
예제 #10
0
def test_register_no_email_validation(app, admin, django_user_model):
    User = django_user_model
    password = '******'
    username = '******'
    email = '*****@*****.**'
    first_name = 'John'
    last_name = 'Doe'
    return_url = 'http://sp.example.com/validate/'

    # invalid payload
    payload = {
        'last_name': last_name,
        'return_url': return_url,
    }
    headers = basic_authorization_header(admin)
    assert len(mail.outbox) == 0
    response = app.post_json(reverse('a2-api-register'), params=payload, headers=headers,
                             status=400)
    assert 'errors' in response.json
    assert response.json['result'] == 0
    assert response.json['errors'] == {
        '__all__': ['You must set at least a username, an email or a first name and a last name'],
    }

    # valid payload
    payload = {
        'username': username,
        'email': email,
        'first_name': first_name,
        'last_name': last_name,
        'password': password,
        'no_email_validation': True,
        'return_url': return_url,
    }
    assert len(mail.outbox) == 0
    response = app.post_json(reverse('a2-api-register'), params=payload, headers=headers)
    assert len(mail.outbox) == 0
    assert response.status_code == 201
    assert response.json['result'] == 1
    assert response.json['user']['username'] == username
    assert response.json['user']['email'] == email
    assert response.json['user']['first_name'] == first_name
    assert response.json['user']['last_name'] == last_name
    assert check_password(password, response.json['user']['password'])
    assert response.json['token']
    assert response.json['validation_url'].startswith('http://testserver/accounts/activate/')
    assert User.objects.count() == 2
    user = User.objects.latest('id')
    assert user.ou == get_default_ou()
    assert user.username == username
    assert user.email == email
    assert user.first_name == first_name
    assert user.last_name == last_name
    assert user.check_password(password)
예제 #11
0
def test_api_post_role_no_ou(app, superuser):
    app.authorization = ('Basic', (superuser.username, superuser.username))
    Role = get_role_model()

    role_data = {
        'slug': 'tea-manager',
        'name': 'Tea Manager',
        }
    resp = app.post_json('/api/roles/', params=role_data)
    uuid = resp.json['uuid']
    role = Role.objects.get(uuid=uuid)
    assert role.ou == get_default_ou()
예제 #12
0
    def populate_user_ou(self, user, dn, conn, block, attributes):
        '''Assign LDAP user to an ou, the default one if ou_slug setting is
           None'''

        ou_slug = block['ou_slug']
        OU = get_ou_model()
        if ou_slug:
            ou_slug = unicode(ou_slug)
            try:
                user.ou = OU.objects.get(slug=ou_slug)
            except OU.DoesNotExist:
                raise ImproperlyConfigured('ou_slug value is wrong for ldap %r',
                                          block['url'])
        else:
            user.ou = get_default_ou()
예제 #13
0
def oidc_client(request, superuser, app):
    url = reverse('admin:authentic2_idp_oidc_oidcclient_add')
    assert OIDCClient.objects.count() == 0
    response = utils.login(app, superuser, path=url)
    response.form.set('name', 'oidcclient')
    response.form.set('slug', 'oidcclient')
    response.form.set('ou', get_default_ou().pk)
    response.form.set('unauthorized_url', 'https://example.com/southpark/')
    response.form.set('redirect_uris', 'https://example.com/callback')
    for key, value in request.param.iteritems():
        response.form.set(key, value)
    response = response.form.submit().follow()
    assert OIDCClient.objects.count() == 1
    client = OIDCClient.objects.get()
    utils.logout(app)
    return client
예제 #14
0
def test_simple(settings, client):
    settings.LDAP_AUTH_SETTINGS = [{
        'url': [slapd.ldapi_url],
        'basedn': 'o=orga',
        'use_tls': False,
    }]
    result = client.post('/login/', {'login-password-submit': '1',
                                     'username': '******',
                                     'password': '******'}, follow=True)
    assert result.status_code == 200
    assert 'id="user"' in str(result)
    assert 'Étienne Michu' in str(result)
    User = get_user_model()
    assert User.objects.count() == 1
    user = User.objects.get()
    assert user.username == 'etienne.michu@ldap'
    assert user.first_name == u'Étienne'
    assert user.last_name == 'Michu'
    assert user.ou == get_default_ou()
    assert not user.check_password('pass')
예제 #15
0
    def get_form_kwargs(self, **kwargs):
        '''Initialize mail from token'''
        kwargs = super(RegistrationCompletionView,
                       self).get_form_kwargs(**kwargs)
        if 'ou' in self.token:
            OU = get_ou_model()
            ou = get_object_or_404(OU, id=self.token['ou'])
        else:
            ou = get_default_ou()

        attributes = {'email': self.email, 'ou': ou}
        for key in self.token:
            if key in app_settings.A2_PRE_REGISTRATION_FIELDS:
                attributes[key] = self.token[key]
        logger.debug(u'attributes %s', attributes)

        prefilling_list = utils.accumulate_from_backends(
            self.request, 'registration_form_prefill')
        logger.debug(u'prefilling_list %s', prefilling_list)
        # Build a single meaningful prefilling with sets of values
        prefilling = {}
        for p in prefilling_list:
            for name, values in p.items():
                if name in self.fields:
                    prefilling.setdefault(name, set()).update(values)
        logger.debug(u'prefilling %s', prefilling)

        for name, values in prefilling.items():
            attributes[name] = ' '.join(values)
        logger.debug(u'attributes with prefilling %s', attributes)

        if self.token.get('user_id'):
            kwargs['instance'] = User.objects.get(id=self.token.get('user_id'))
        else:
            init_kwargs = {}
            for key in ('email', 'first_name', 'last_name', 'ou'):
                if key in attributes:
                    init_kwargs[key] = attributes[key]
            kwargs['instance'] = get_user_model()(**init_kwargs)

        return kwargs
예제 #16
0
 def dispatch(self, request, *args, **kwargs):
     if not getattr(settings, 'REGISTRATION_OPEN', True):
         raise Http404('Registration is not open.')
     self.token = {}
     self.ou = get_default_ou()
     # load pre-filled values
     if request.GET.get('token'):
         try:
             self.token = signing.loads(
                 request.GET.get('token'),
                 max_age=settings.ACCOUNT_ACTIVATION_DAYS * 3600 * 24)
         except (TypeError, ValueError, signing.BadSignature) as e:
             logger.warning(u'registration_view: invalid token: %s', e)
             return HttpResponseBadRequest('invalid token',
                                           content_type='text/plain')
         if 'ou' in self.token:
             self.ou = OrganizationalUnit.objects.get(pk=self.token['ou'])
     self.next_url = self.token.pop(REDIRECT_FIELD_NAME,
                                    utils.select_next_url(request, None))
     return super(BaseRegistrationView,
                  self).dispatch(request, *args, **kwargs)
예제 #17
0
def test_manager_user_change_email(app, superuser_or_admin, simple_user,
                                   mailoutbox):
    ou = get_default_ou()
    ou.validate_emails = True
    ou.save()

    NEW_EMAIL = '*****@*****.**'

    assert NEW_EMAIL != simple_user.email

    response = login(
        app, superuser_or_admin,
        reverse('a2-manager-user-by-uuid-detail',
                kwargs={'slug': unicode(simple_user.uuid)}))
    assert 'Change user email' in response.content
    # cannot click it's a submit button :/
    response = app.get(
        reverse('a2-manager-user-by-uuid-change-email',
                kwargs={'slug': unicode(simple_user.uuid)}))
    assert response.form['new_email'].value == simple_user.email
    response.form.set('new_email', NEW_EMAIL)
    assert len(mailoutbox) == 0
    response = response.form.submit().follow()
    assert 'A mail was sent to [email protected] to verify it.' in response.content
    assert 'Change user email' in response.content
    # cannot click it's a submit button :/
    assert len(mailoutbox) == 1
    assert simple_user.email in mailoutbox[0].body
    assert NEW_EMAIL in mailoutbox[0].body

    # logout
    app.session.flush()

    link = get_link_from_mail(mailoutbox[0])
    response = app.get(link).maybe_follow()
    assert (
        'your request for changing your email for [email protected] is successful'
        in response.content)
    simple_user.refresh_from_db()
    assert simple_user.email == NEW_EMAIL
예제 #18
0
 def dispatch(self, request, *args, **kwargs):
     self.token = request.token
     self.authentication_method = self.token.get('authentication_method',
                                                 'email')
     self.email = request.token['email']
     if 'ou' in self.token:
         self.ou = OrganizationalUnit.objects.get(pk=self.token['ou'])
     else:
         self.ou = get_default_ou()
     self.users = User.objects.filter(email__iexact=self.email) \
         .order_by('date_joined')
     if self.ou:
         self.users = self.users.filter(ou=self.ou)
     self.email_is_unique = app_settings.A2_EMAIL_IS_UNIQUE \
         or app_settings.A2_REGISTRATION_EMAIL_IS_UNIQUE
     if self.ou:
         self.email_is_unique |= self.ou.email_is_unique
     self.init_fields_labels_and_help_texts()
     # if registration is done during an SSO add the service to the registration event
     self.service = self.token.get(constants.SERVICE_FIELD_NAME)
     return super(RegistrationCompletionView, self) \
         .dispatch(request, *args, **kwargs)
예제 #19
0
def test_manager_user_change_email_no_change(app, superuser_or_admin,
                                             simple_user, mailoutbox):
    ou = get_default_ou()
    ou.validate_emails = True
    ou.save()

    NEW_EMAIL = '*****@*****.**'

    assert NEW_EMAIL != simple_user.email

    response = login(
        app, superuser_or_admin,
        reverse('a2-manager-user-by-uuid-detail',
                kwargs={'slug': unicode(simple_user.uuid)}))
    assert 'Change user email' in response.content
    # cannot click it's a submit button :/
    response = app.get(
        reverse('a2-manager-user-by-uuid-change-email',
                kwargs={'slug': unicode(simple_user.uuid)}))
    assert response.form['new_email'].value == simple_user.email
    assert len(mailoutbox) == 0
    response = response.form.submit().follow()
    assert 'A mail was sent to [email protected] to verify it.' not in response.content
예제 #20
0
def simple_oidc_client(db):
    return OIDCClient.objects.create(
        name='client',
        slug='client',
        ou=get_default_ou(),
        redirect_uris='https://example.com/')
예제 #21
0
 def save(self, *args, **kwargs):
     if 'ou' not in self.fields:
         self.instance.ou = get_default_ou()
     return super(HideOUFieldMixin, self).save(*args, **kwargs)
예제 #22
0
def test_api_user(client):
    # create an user, an ou role, a service and a service role
    ou = get_default_ou()

    User = get_user_model()
    user = User.objects.create(ou=ou, username='******', first_name=u'Jôhn',
                               last_name=u'Doe', email='*****@*****.**')
    user.set_password('password')
    user.save()

    Role = get_role_model()
    role1 = Role.objects.create(name='Role1', ou=ou)
    role1.members.add(user)

    service = Service.objects.create(name='Service1', slug='service1', ou=ou)
    role2 = Role.objects.create(name='Role2', service=service)
    role2.members.add(user)

    Role.objects.create(name='Role3', ou=ou)
    Role.objects.create(name='Role4', service=service)

    # test failure when unlogged
    response = client.get('/api/user/', HTTP_ORIGIN='http://testserver')
    assert response.content == '{}'

    # login
    client.login(username='******', password='******')
    response = client.get('/api/user/', HTTP_ORIGIN='http://testserver')
    data = json.loads(response.content)
    assert isinstance(data, dict)
    assert set(data.keys()) == set(['uuid', 'username', 'first_name',
                                    'ou__slug', 'ou__uuid', 'ou__name',
                                    'last_name', 'email', 'roles', 'services',
                                    'is_superuser', 'ou'])
    assert data['uuid'] == user.uuid
    assert data['username'] == user.username
    assert data['first_name'] == user.first_name
    assert data['last_name'] == user.last_name
    assert data['email'] == user.email
    assert data['is_superuser'] == user.is_superuser
    assert data['ou'] == ou.name
    assert data['ou__name'] == ou.name
    assert data['ou__slug'] == ou.slug
    assert data['ou__uuid'] == ou.uuid
    assert isinstance(data['roles'], list)
    assert len(data['roles']) == 2
    for role in data['roles']:
        assert set(role.keys()) == set(['uuid', 'name', 'slug', 'is_admin',
                                        'is_service', 'ou__uuid', 'ou__name',
                                        'ou__slug'])
        assert (role['uuid'] == role1.uuid and
                role['name'] == role1.name and
                role['slug'] == role1.slug and
                role['is_admin'] is False and
                role['is_service'] is False and
                role['ou__uuid'] == ou.uuid and
                role['ou__name'] == ou.name and
                role['ou__slug'] == ou.slug) or \
               (role['uuid'] == role2.uuid and
                role['name'] == role2.name and
                role['slug'] == role2.slug and
                role['is_admin'] is False and
                role['is_service'] is True and
                role['ou__uuid'] == ou.uuid and
                role['ou__name'] == ou.name and
                role['ou__slug'] == ou.slug)

    assert isinstance(data['services'], list)
    assert len(data['services']) == 1
    s = data['services'][0]
    assert set(s.keys()) == set(['name', 'slug', 'ou', 'ou__name', 'ou__slug',
                                 'ou__uuid', 'roles'])
    assert s['name'] == service.name
    assert s['slug'] == service.slug
    assert s['ou'] == ou.name
    assert s['ou__name'] == ou.name
    assert s['ou__slug'] == ou.slug
    assert s['ou__uuid'] == ou.uuid
    assert isinstance(s['roles'], list)
    assert len(s['roles']) == 2
    for role in s['roles']:
        assert set(role.keys()) == set(['uuid', 'name', 'slug', 'is_admin',
                                        'is_service', 'ou__uuid', 'ou__name',
                                        'ou__slug'])
        assert (role['uuid'] == role1.uuid and
                role['name'] == role1.name and
                role['slug'] == role1.slug and
                role['is_admin'] is False and
                role['is_service'] is False and
                role['ou__uuid'] == ou.uuid and
                role['ou__name'] == ou.name and
                role['ou__slug'] == ou.slug) or \
               (role['uuid'] == role2.uuid and
                role['name'] == role2.name and
                role['slug'] == role2.slug and
                role['is_admin'] is False and
                role['is_service'] is True and
                role['ou__uuid'] == ou.uuid and
                role['ou__name'] == ou.name and
                role['ou__slug'] == ou.slug)
예제 #23
0
 def save(self, *args, **kwargs):
     if 'ou' not in self.fields:
         self.instance.ou = get_default_ou()
     return super(HideOUFieldMixin, self).save(*args, **kwargs)
예제 #24
0
def simple_role(db):
    return Role.objects.create(name='simple role',
                               slug='simple-role',
                               ou=get_default_ou())
예제 #25
0
    def test_user_listing_admin(user):
        response = login(app, user, '/manage/')

        # test user listing ou search
        response = response.click(href='users')
        assert len(response.form.fields['search-ou']) == 1
        assert len(response.form.fields['search-text']) == 1
        field = response.form['search-ou']
        options = field.options
        assert len(options) == 4
        for key, checked, label in options:
            assert not checked or key == 'all'
        assert 'all' in [o[0] for o in options]
        assert 'none' in [o[0] for o in options]
        # verify table shown
        q = response.pyquery.remove_namespaces()
        assert len(q('table tbody tr')) == 3
        assert set([e.text for e in q('table tbody td.username')
                    ]) == {'admin', 'superuser', 'admin.ou1'}

        # test user's role page
        response = app.get('/manage/users/%d/roles/' % admin.pk)
        assert len(response.form.fields['search-ou']) == 1
        field = response.form['search-ou']
        options = field.options
        assert len(options) == 4
        for key, checked, label in options:
            assert not checked or key == str(get_default_ou().pk)
        q = response.pyquery.remove_namespaces()
        assert len(q('table tbody tr')) == 1
        assert q('table tbody tr').text() == u'simple role'

        response.form.set('search-ou', 'all')
        response = response.form.submit()
        q = response.pyquery.remove_namespaces()
        assert len(q('table tbody tr')) == 1
        assert q('table tbody tr').text() == 'None'

        form = response.forms['search-form']
        form.set('search-internals', True)
        response = form.submit()
        q = response.pyquery.remove_namespaces()
        assert len(q('table tbody tr')) == 4
        # admin enroled only in the Manager role, other roles are inherited
        assert len(q('table tbody tr td.via')) == 4
        assert len(q('table tbody tr td.via:empty')) == 1
        for elt in q('table tbody td.name a'):
            assert 'Manager' in elt.text

        form = response.forms['search-form']
        form.set('search-ou', 'none')
        form.set('search-internals', True)
        response = form.submit()
        q = response.pyquery.remove_namespaces()
        assert len(q('table tbody tr')) == 6
        for elt in q('table tbody td.name a'):
            assert 'Manager' in elt.text

        # test role listing
        response = app.get('/manage/roles/')
        assert len(response.form.fields['search-ou']) == 1
        field = response.form['search-ou']
        options = field.options
        assert len(options) == 4
        for key, checked, label in options:
            if key == 'all':
                assert checked
            else:
                assert not checked
        q = response.pyquery.remove_namespaces()
        assert len(q('table tbody tr')) == 2
        names = [elt.text for elt in q('table tbody td.name a')]
        assert set(names) == {u'simple role', u'role_ou1'}

        response.form.set('search-ou', 'all')
        response.form.set('search-internals', True)
        response = response.form.submit()
        q = response.pyquery.remove_namespaces()
        assert len(q('table tbody tr')) == 12
        for elt in q('table tbody td.name a'):
            assert ('OU1' in elt.text or 'Default' in elt.text
                    or 'Manager' in elt.text or elt.text == u'simple role'
                    or elt.text == u'role_ou1')

        response.form.set('search-ou', 'none')
        response.form.set('search-internals', True)
        response = response.form.submit()
        q = response.pyquery.remove_namespaces()
        assert len(q('table tbody tr')) == 6
        for elt in q('table tbody td.name a'):
            assert 'Manager' in elt.text
예제 #26
0
def simple_user(db, ou1):
    return create_user(username='******',
                       first_name=u'Jôhn',
                       last_name=u'Dôe',
                       email='*****@*****.**',
                       ou=get_default_ou())
예제 #27
0
def register_issuer(name,
                    issuer=None,
                    openid_configuration=None,
                    verify=True,
                    timeout=None,
                    ou=None):
    from . import models

    if issuer and not openid_configuration:
        openid_configuration_url = get_openid_configuration_url(issuer)
        try:
            response = requests.get(openid_configuration_url,
                                    verify=verify,
                                    timeout=timeout)
            response.raise_for_status()
        except requests.RequestException as e:
            raise ValueError(
                _('Unable to reach the OpenID Connect configuration for %(issuer)s: '
                  '%(error)s') % {
                      'issuer': issuer,
                      'error': e,
                  })

    try:
        openid_configuration = openid_configuration or response.json()
        if not isinstance(openid_configuration, dict):
            raise ValueError(_('MUST be a dictionnary'))
        keys = set(openid_configuration.keys())
        if not keys >= OPENID_CONFIGURATION_REQUIRED:
            raise ValueError(
                _('missing keys %s') % (OPENID_CONFIGURATION_REQUIRED - keys))
        for key in [
                'issuer', 'authorization_endpoint', 'token_endpoint',
                'jwks_uri', 'userinfo_endpoint'
        ]:
            if not check_https(openid_configuration[key]):
                raise ValueError(
                    _('%(key)s is not an https:// URL; %(value)s') % {
                        'key': key,
                        'value': openid_configuration[key],
                    })
    except ValueError as e:
        raise ValueError(
            _('Invalid OpenID Connect configuration for %(issuer)s: '
              '%(error)s') % (issuer, e))
    if 'code' not in openid_configuration['response_types_supported']:
        raise ValueError(
            _('authorization code flow is unsupported: code response type is '
              'unsupported'))
    try:
        response = requests.get(openid_configuration['jwks_uri'],
                                verify=verify,
                                timeout=None)
        response.raise_for_status()
    except requests.RequestException as e:
        raise ValueError(
            _('Unable to reach the OpenID Connect JWKSet for %(issuer)s: '
              '%(url)s %(error)s') % {
                  'issuer': issuer,
                  'url': openid_configuration['jwks_uri'],
                  'error': e,
              })
    try:
        jwkset_json = response.json()
    except ValueError as e:
        raise ValueError(_('Invalid JSKSet document: %s') % e)
    try:
        old_pk = models.OIDCProvider.objects.get(
            issuer=openid_configuration['issuer']).pk
    except models.OIDCProvider.DoesNotExist:
        old_pk = None
    if (set(['RS256', 'RS384', 'RS512']) & set(
            openid_configuration['id_token_signing_alg_values_supported'])):
        idtoken_algo = models.OIDCProvider.ALGO_RSA
    elif (set(['HS256', 'HS384', 'HS512']) & set(
            openid_configuration['id_token_signing_alg_values_supported'])):
        idtoken_algo = models.OIDCProvider.HMAC
    else:
        raise ValueError(
            _('no common algorithm found for signing idtokens: %s') %
            openid_configuration['id_token_signing_alg_values_supported'])
    kwargs = dict(
        ou=ou or get_default_ou(),
        name=name,
        issuer=openid_configuration['issuer'],
        authorization_endpoint=openid_configuration['authorization_endpoint'],
        token_endpoint=openid_configuration['token_endpoint'],
        userinfo_endpoint=openid_configuration['userinfo_endpoint'],
        jwkset_json=jwkset_json,
        idtoken_algo=idtoken_algo,
        strategy=models.OIDCProvider.STRATEGY_CREATE)
    if old_pk:
        models.OIDCProvider.objects.filter(pk=old_pk).update(**kwargs)
        return models.OIDCProvider.objects.get(pk=old_pk)
    else:
        return models.OIDCProvider.objects.create(**kwargs)
예제 #28
0
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)')
예제 #29
0
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'
예제 #30
0
def test_sso(app, caplog, code, oidc_provider, oidc_provider_jwkset, login_url, login_callback_url, hooks):
    OU = get_ou_model()
    cassis = OU.objects.create(name='Cassis', slug='cassis')
    OU.cached.cache.clear()

    response = app.get('/admin/').maybe_follow()
    assert oidc_provider.name in response.content
    response = response.click(oidc_provider.name)
    location = urlparse.urlparse(response.location)
    endpoint = urlparse.urlparse(oidc_provider.authorization_endpoint)
    assert location.scheme == endpoint.scheme
    assert location.netloc == endpoint.netloc
    assert location.path == endpoint.path
    query = check_simple_qs(urlparse.parse_qs(location.query))
    assert query['state'] in app.session['auth_oidc']
    assert query['response_type'] == 'code'
    assert query['client_id'] == str(oidc_provider.client_id)
    assert query['scope'] == 'openid'
    assert query['redirect_uri'] == 'http://testserver' + reverse('oidc-login-callback')

    User = get_user_model()
    assert User.objects.count() == 0

    with utils.check_log(caplog, 'invalid token endpoint response'):
        with oidc_provider_mock(oidc_provider, oidc_provider_jwkset, code):
            response = app.get(login_callback_url, params={'code': 'yyyy', 'state': query['state']})
    with utils.check_log(caplog, 'invalid id_token %r'):
        with oidc_provider_mock(oidc_provider, oidc_provider_jwkset, code,
                                extra_id_token={'iss': None}):
            response = app.get(login_callback_url, params={'code': code, 'state': query['state']})
    with utils.check_log(caplog, 'invalid id_token %r'):
        with oidc_provider_mock(oidc_provider, oidc_provider_jwkset, code,
                                extra_id_token={'sub': None}):
            response = app.get(login_callback_url, params={'code': code, 'state': query['state']})
    with utils.check_log(caplog, 'authentication is too old'):
        with oidc_provider_mock(oidc_provider, oidc_provider_jwkset, code,
                                extra_id_token={'iat': 1}):
            response = app.get(login_callback_url, params={'code': code, 'state': query['state']})
    with utils.check_log(caplog, 'id_token expired'):
        with oidc_provider_mock(oidc_provider, oidc_provider_jwkset, code,
                                extra_id_token={'exp': 1}):
            response = app.get(login_callback_url, params={'code': code, 'state': query['state']})
    with utils.check_log(caplog, 'invalid id_token audience'):
        with oidc_provider_mock(oidc_provider, oidc_provider_jwkset, code,
                                extra_id_token={'aud': 'zz'}):
            response = app.get(login_callback_url, params={'code': code, 'state': query['state']})
    assert not hooks.auth_oidc_backend_modify_user
    with utils.check_log(caplog, 'created user'):
        with oidc_provider_mock(oidc_provider, oidc_provider_jwkset, code):
            response = app.get(login_callback_url, params={'code': code, 'state': query['state']})
    assert len(hooks.auth_oidc_backend_modify_user) == 1
    assert set(hooks.auth_oidc_backend_modify_user[0]['kwargs']) >= set(['user', 'provider', 'user_info', 'id_token', 'access_token'])
    assert urlparse.urlparse(response['Location']).path == '/admin/'
    assert User.objects.count() == 1
    user = User.objects.get()
    assert user.ou == get_default_ou()
    assert user.username == 'john.doe'
    assert user.first_name == 'John'
    assert user.last_name == 'Doe'
    assert user.email == '*****@*****.**'
    assert user.attributes.first_name == 'John'
    assert user.attributes.last_name == 'Doe'
    assert AttributeValue.objects.filter(content='John', verified=True).count() == 1
    assert AttributeValue.objects.filter(content='Doe', verified=False).count() == 1

    with oidc_provider_mock(oidc_provider, oidc_provider_jwkset, code,
                            extra_user_info={'family_name_verified': True}):
        response = app.get(login_callback_url, params={'code': code, 'state': query['state']})
    assert AttributeValue.objects.filter(content='Doe', verified=False).count() == 0
    assert AttributeValue.objects.filter(content='Doe', verified=True).count() == 1

    with oidc_provider_mock(oidc_provider, oidc_provider_jwkset, code,
                            extra_user_info={'ou': 'cassis'}):
        response = app.get(login_callback_url, params={'code': code, 'state': query['state']})
    assert User.objects.count() == 1
    user = User.objects.get()
    assert user.ou == cassis

    with oidc_provider_mock(oidc_provider, oidc_provider_jwkset, code):
        response = app.get(login_callback_url, params={'code': code, 'state': query['state']})
    assert User.objects.count() == 1
    user = User.objects.get()
    assert user.ou == get_default_ou()
    last_modified = user.modified

    time.sleep(0.1)

    with oidc_provider_mock(oidc_provider, oidc_provider_jwkset, code):
        response = app.get(login_callback_url, params={'code': code, 'state': query['state']})
    assert User.objects.count() == 1
    user = User.objects.get()
    assert user.ou == get_default_ou()
    assert user.modified == last_modified

    response = app.get(reverse('account_management'))
    with utils.check_log(caplog, 'revoked token from OIDC'):
        with oidc_provider_mock(oidc_provider, oidc_provider_jwkset, code):
            response = response.click(href='logout')
    assert 'https://idp.example.com/logout' in response.content