Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / tests.py @ 7233d542

History | View | Annotate | Download (21.8 kB)

1 d2633501 Kostas Papadimitriou
# Copyright 2011 GRNET S.A. All rights reserved.
2 d2633501 Kostas Papadimitriou
#
3 d2633501 Kostas Papadimitriou
# Redistribution and use in source and binary forms, with or
4 d2633501 Kostas Papadimitriou
# without modification, are permitted provided that the following
5 d2633501 Kostas Papadimitriou
# conditions are met:
6 d2633501 Kostas Papadimitriou
#
7 d2633501 Kostas Papadimitriou
#   1. Redistributions of source code must retain the above
8 d2633501 Kostas Papadimitriou
#      copyright notice, this list of conditions and the following
9 d2633501 Kostas Papadimitriou
#      disclaimer.
10 d2633501 Kostas Papadimitriou
#
11 d2633501 Kostas Papadimitriou
#   2. Redistributions in binary form must reproduce the above
12 d2633501 Kostas Papadimitriou
#      copyright notice, this list of conditions and the following
13 d2633501 Kostas Papadimitriou
#      disclaimer in the documentation and/or other materials
14 d2633501 Kostas Papadimitriou
#      provided with the distribution.
15 d2633501 Kostas Papadimitriou
#
16 d2633501 Kostas Papadimitriou
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 d2633501 Kostas Papadimitriou
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 d2633501 Kostas Papadimitriou
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 d2633501 Kostas Papadimitriou
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 d2633501 Kostas Papadimitriou
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 d2633501 Kostas Papadimitriou
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 d2633501 Kostas Papadimitriou
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 d2633501 Kostas Papadimitriou
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 d2633501 Kostas Papadimitriou
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 d2633501 Kostas Papadimitriou
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 d2633501 Kostas Papadimitriou
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 d2633501 Kostas Papadimitriou
# POSSIBILITY OF SUCH DAMAGE.
28 d2633501 Kostas Papadimitriou
#
29 d2633501 Kostas Papadimitriou
# The views and conclusions contained in the software and
30 d2633501 Kostas Papadimitriou
# documentation are those of the authors and should not be
31 d2633501 Kostas Papadimitriou
# interpreted as representing official policies, either expressed
32 d2633501 Kostas Papadimitriou
# or implied, of GRNET S.A.
33 d2633501 Kostas Papadimitriou
34 d2633501 Kostas Papadimitriou
import datetime
35 d2633501 Kostas Papadimitriou
36 d2633501 Kostas Papadimitriou
from django.test import TestCase, Client
37 d2633501 Kostas Papadimitriou
from django.conf import settings
38 d2633501 Kostas Papadimitriou
from django.core import mail
39 d2633501 Kostas Papadimitriou
40 d2633501 Kostas Papadimitriou
from astakos.im.target.shibboleth import Tokens as ShibbolethTokens
41 d2633501 Kostas Papadimitriou
from astakos.im.models import *
42 d2633501 Kostas Papadimitriou
from astakos.im import functions
43 d2633501 Kostas Papadimitriou
from astakos.im import settings as astakos_settings
44 d2633501 Kostas Papadimitriou
45 d2633501 Kostas Papadimitriou
from urllib import quote
46 d2633501 Kostas Papadimitriou
47 2e90e3ec Kostas Papadimitriou
from astakos.im import messages
48 2e90e3ec Kostas Papadimitriou
49 d2633501 Kostas Papadimitriou
class ShibbolethClient(Client):
50 d2633501 Kostas Papadimitriou
    """
51 d2633501 Kostas Papadimitriou
    A shibboleth agnostic client.
52 d2633501 Kostas Papadimitriou
    """
53 d2633501 Kostas Papadimitriou
    VALID_TOKENS = filter(lambda x: not x.startswith("_"), dir(ShibbolethTokens))
54 d2633501 Kostas Papadimitriou
55 d2633501 Kostas Papadimitriou
    def __init__(self, *args, **kwargs):
56 d2633501 Kostas Papadimitriou
        self.tokens = kwargs.pop('tokens', {})
57 d2633501 Kostas Papadimitriou
        super(ShibbolethClient, self).__init__(*args, **kwargs)
58 d2633501 Kostas Papadimitriou
59 d2633501 Kostas Papadimitriou
    def set_tokens(self, **kwargs):
60 d2633501 Kostas Papadimitriou
        for key, value in kwargs.iteritems():
61 d2633501 Kostas Papadimitriou
            key = 'SHIB_%s' % key.upper()
62 d2633501 Kostas Papadimitriou
            if not key in self.VALID_TOKENS:
63 d2633501 Kostas Papadimitriou
                raise Exception('Invalid shibboleth token')
64 d2633501 Kostas Papadimitriou
65 d2633501 Kostas Papadimitriou
            self.tokens[key] = value
66 d2633501 Kostas Papadimitriou
67 d2633501 Kostas Papadimitriou
    def unset_tokens(self, *keys):
68 d2633501 Kostas Papadimitriou
        for key in keys:
69 d2633501 Kostas Papadimitriou
            key = 'SHIB_%s' % param.upper()
70 d2633501 Kostas Papadimitriou
            if key in self.tokens:
71 d2633501 Kostas Papadimitriou
                del self.tokens[key]
72 d2633501 Kostas Papadimitriou
73 d2633501 Kostas Papadimitriou
    def reset_tokens(self):
74 d2633501 Kostas Papadimitriou
        self.tokens = {}
75 d2633501 Kostas Papadimitriou
76 d2633501 Kostas Papadimitriou
    def get_http_token(self, key):
77 d2633501 Kostas Papadimitriou
        http_header = getattr(ShibbolethTokens, key)
78 d2633501 Kostas Papadimitriou
        return http_header
79 d2633501 Kostas Papadimitriou
80 d2633501 Kostas Papadimitriou
    def request(self, **request):
81 d2633501 Kostas Papadimitriou
        """
82 d2633501 Kostas Papadimitriou
        Transform valid shibboleth tokens to http headers
83 d2633501 Kostas Papadimitriou
        """
84 d2633501 Kostas Papadimitriou
        for token, value in self.tokens.iteritems():
85 d2633501 Kostas Papadimitriou
            request[self.get_http_token(token)] = value
86 d2633501 Kostas Papadimitriou
87 d2633501 Kostas Papadimitriou
        for param in request.keys():
88 d2633501 Kostas Papadimitriou
            key = 'SHIB_%s' % param.upper()
89 d2633501 Kostas Papadimitriou
            if key in self.VALID_TOKENS:
90 d2633501 Kostas Papadimitriou
                request[self.get_http_token(key)] = request[param]
91 d2633501 Kostas Papadimitriou
                del request[param]
92 d2633501 Kostas Papadimitriou
93 d2633501 Kostas Papadimitriou
        return super(ShibbolethClient, self).request(**request)
94 d2633501 Kostas Papadimitriou
95 d2633501 Kostas Papadimitriou
96 d2633501 Kostas Papadimitriou
def get_local_user(username, **kwargs):
97 d2633501 Kostas Papadimitriou
        try:
98 d2633501 Kostas Papadimitriou
            return AstakosUser.objects.get(email=username)
99 d2633501 Kostas Papadimitriou
        except:
100 d2633501 Kostas Papadimitriou
            user_params = {
101 d2633501 Kostas Papadimitriou
                'username': username,
102 d2633501 Kostas Papadimitriou
                'email': username,
103 d2633501 Kostas Papadimitriou
                'is_active': True,
104 d2633501 Kostas Papadimitriou
                'activation_sent': datetime.now(),
105 d2633501 Kostas Papadimitriou
                'email_verified': True,
106 d2633501 Kostas Papadimitriou
                'provider': 'local'
107 d2633501 Kostas Papadimitriou
            }
108 d2633501 Kostas Papadimitriou
            user_params.update(kwargs)
109 d2633501 Kostas Papadimitriou
            user = AstakosUser(**user_params)
110 d2633501 Kostas Papadimitriou
            user.set_password(kwargs.get('password', 'password'))
111 d2633501 Kostas Papadimitriou
            user.save()
112 d2633501 Kostas Papadimitriou
            user.add_auth_provider('local', auth_backend='astakos')
113 d2633501 Kostas Papadimitriou
            if kwargs.get('is_active', True):
114 d2633501 Kostas Papadimitriou
                user.is_active = True
115 d2633501 Kostas Papadimitriou
            else:
116 d2633501 Kostas Papadimitriou
                user.is_active = False
117 d2633501 Kostas Papadimitriou
            user.save()
118 d2633501 Kostas Papadimitriou
            return user
119 d2633501 Kostas Papadimitriou
120 d2633501 Kostas Papadimitriou
121 d2633501 Kostas Papadimitriou
def get_mailbox(email):
122 d2633501 Kostas Papadimitriou
    mails = []
123 d2633501 Kostas Papadimitriou
    for sent_email in mail.outbox:
124 d2633501 Kostas Papadimitriou
        for recipient in sent_email.recipients():
125 d2633501 Kostas Papadimitriou
            if email in recipient:
126 d2633501 Kostas Papadimitriou
                mails.append(sent_email)
127 d2633501 Kostas Papadimitriou
    return mails
128 d2633501 Kostas Papadimitriou
129 d2633501 Kostas Papadimitriou
130 d2633501 Kostas Papadimitriou
class ShibbolethTests(TestCase):
131 d2633501 Kostas Papadimitriou
    """
132 d2633501 Kostas Papadimitriou
    Testing shibboleth authentication.
133 d2633501 Kostas Papadimitriou
    """
134 d2633501 Kostas Papadimitriou
135 d2633501 Kostas Papadimitriou
    fixtures = ['groups']
136 d2633501 Kostas Papadimitriou
137 d2633501 Kostas Papadimitriou
    def setUp(self):
138 d2633501 Kostas Papadimitriou
        self.client = ShibbolethClient()
139 d2633501 Kostas Papadimitriou
        settings.ASTAKOS_IM_MODULES = ['local', 'shibboleth']
140 d2633501 Kostas Papadimitriou
141 d2633501 Kostas Papadimitriou
    def test_create_account(self):
142 d2633501 Kostas Papadimitriou
        client = ShibbolethClient()
143 d2633501 Kostas Papadimitriou
144 d2633501 Kostas Papadimitriou
        # shibboleth views validation
145 d2633501 Kostas Papadimitriou
        # eepn required
146 d2633501 Kostas Papadimitriou
        r = client.get('/im/login/shibboleth?', follow=True)
147 2e90e3ec Kostas Papadimitriou
        self.assertContains(r, messages.SHIBBOLETH_MISSING_EPPN)
148 d2633501 Kostas Papadimitriou
        client.set_tokens(eppn="kpapeppn")
149 d2633501 Kostas Papadimitriou
        # shibboleth user info required
150 d2633501 Kostas Papadimitriou
        r = client.get('/im/login/shibboleth?', follow=True)
151 2e90e3ec Kostas Papadimitriou
        self.assertContains(r, messages.SHIBBOLETH_MISSING_NAME)
152 d2633501 Kostas Papadimitriou
153 d2633501 Kostas Papadimitriou
        # shibboleth logged us in
154 d2633501 Kostas Papadimitriou
        client.set_tokens(mail="kpap@grnet.gr", eppn="kpapeppn", cn="1", )
155 d2633501 Kostas Papadimitriou
        r = client.get('/im/login/shibboleth?')
156 d2633501 Kostas Papadimitriou
157 d2633501 Kostas Papadimitriou
        # astakos asks if we want to add shibboleth
158 d2633501 Kostas Papadimitriou
        self.assertContains(r, "Already have an account?")
159 d2633501 Kostas Papadimitriou
160 d2633501 Kostas Papadimitriou
        # a new pending user created
161 d2633501 Kostas Papadimitriou
        pending_user = PendingThirdPartyUser.objects.get(
162 d2633501 Kostas Papadimitriou
            third_party_identifier="kpapeppn")
163 d2633501 Kostas Papadimitriou
        self.assertEqual(PendingThirdPartyUser.objects.count(), 1)
164 d2633501 Kostas Papadimitriou
        token = pending_user.token
165 d2633501 Kostas Papadimitriou
        # from now on no shibboleth headers are sent to the server
166 d2633501 Kostas Papadimitriou
        client.reset_tokens()
167 d2633501 Kostas Papadimitriou
168 d2633501 Kostas Papadimitriou
        # we choose to signup as a new user
169 d2633501 Kostas Papadimitriou
        r = client.get('/im/shibboleth/signup/%s' % pending_user.username)
170 d2633501 Kostas Papadimitriou
        self.assertEqual(r.status_code, 404)
171 d2633501 Kostas Papadimitriou
172 d2633501 Kostas Papadimitriou
        r = client.get('/im/shibboleth/signup/%s' % token)
173 d2633501 Kostas Papadimitriou
        form = r.context['form']
174 8ab484ea Kostas Papadimitriou
        post_data = {'email': 'kpap',
175 d2633501 Kostas Papadimitriou
                     'third_party_identifier': pending_user.third_party_identifier,
176 d2633501 Kostas Papadimitriou
                     'first_name': 'Kostas',
177 d2633501 Kostas Papadimitriou
                     'third_party_token': token,
178 d2633501 Kostas Papadimitriou
                     'last_name': 'Mitroglou',
179 d2633501 Kostas Papadimitriou
                     'additional_email': 'kpap@grnet.gr',
180 d2633501 Kostas Papadimitriou
                     'provider': 'shibboleth'
181 d2633501 Kostas Papadimitriou
                    }
182 d2633501 Kostas Papadimitriou
        r = client.post('/im/signup', post_data)
183 8ab484ea Kostas Papadimitriou
        self.assertContains(r, token)
184 8ab484ea Kostas Papadimitriou
        post_data['email'] = 'kpap@grnet.gr'
185 8ab484ea Kostas Papadimitriou
        r = client.post('/im/signup', post_data)
186 d2633501 Kostas Papadimitriou
        self.assertEqual(r.status_code, 200)
187 d2633501 Kostas Papadimitriou
        self.assertEqual(AstakosUser.objects.count(), 1)
188 d2633501 Kostas Papadimitriou
        self.assertEqual(PendingThirdPartyUser.objects.count(), 0)
189 d2633501 Kostas Papadimitriou
        self.assertEqual(AstakosUserAuthProvider.objects.count(), 1)
190 d2633501 Kostas Papadimitriou
191 d2633501 Kostas Papadimitriou
192 d2633501 Kostas Papadimitriou
        client.set_tokens(mail="kpap@grnet.gr", eppn="kpapeppn", cn="1", )
193 d2633501 Kostas Papadimitriou
        r = client.get("/im/login/shibboleth?", follow=True)
194 d2633501 Kostas Papadimitriou
        self.assertContains(r, "Your request is pending activation")
195 d2633501 Kostas Papadimitriou
        r = client.get("/im/profile", follow=True)
196 d2633501 Kostas Papadimitriou
        self.assertRedirects(r, 'http://testserver/im/?next=%2Fim%2Fprofile')
197 d2633501 Kostas Papadimitriou
198 d2633501 Kostas Papadimitriou
        u = AstakosUser.objects.get()
199 d2633501 Kostas Papadimitriou
        functions.activate(u)
200 d2633501 Kostas Papadimitriou
        self.assertEqual(u.is_active, True)
201 d2633501 Kostas Papadimitriou
202 d2633501 Kostas Papadimitriou
        r = client.get("/im/login/shibboleth?")
203 d2633501 Kostas Papadimitriou
        self.assertRedirects(r, '/im/profile')
204 d2633501 Kostas Papadimitriou
205 d2633501 Kostas Papadimitriou
    def test_existing(self):
206 d2633501 Kostas Papadimitriou
        existing_user = get_local_user('kpap@grnet.gr')
207 d2633501 Kostas Papadimitriou
208 d2633501 Kostas Papadimitriou
        client = ShibbolethClient()
209 d2633501 Kostas Papadimitriou
        # shibboleth logged us in, notice that we use different email
210 d2633501 Kostas Papadimitriou
        client.set_tokens(mail="kpap@shibboleth.gr", eppn="kpapeppn", cn="1", )
211 d2633501 Kostas Papadimitriou
        r = client.get("/im/login/shibboleth?")
212 d2633501 Kostas Papadimitriou
        # astakos asks if we want to switch a local account to shibboleth
213 d2633501 Kostas Papadimitriou
        self.assertContains(r, "Already have an account?")
214 d2633501 Kostas Papadimitriou
215 d2633501 Kostas Papadimitriou
        # a new pending user created
216 d2633501 Kostas Papadimitriou
        pending_user = PendingThirdPartyUser.objects.get()
217 d2633501 Kostas Papadimitriou
        self.assertEqual(PendingThirdPartyUser.objects.count(), 1)
218 d2633501 Kostas Papadimitriou
        pending_key = pending_user.token
219 d2633501 Kostas Papadimitriou
        client.reset_tokens()
220 d2633501 Kostas Papadimitriou
221 d2633501 Kostas Papadimitriou
        # we choose to add shibboleth to an our existing account
222 d2633501 Kostas Papadimitriou
        # we get redirected to login page with the pending token set
223 d2633501 Kostas Papadimitriou
        r = client.get('/im/login?key=%s' % pending_key)
224 d2633501 Kostas Papadimitriou
        post_data = {'password': 'password',
225 d2633501 Kostas Papadimitriou
                     'username': 'kpap@grnet.gr',
226 d2633501 Kostas Papadimitriou
                     'key': pending_key}
227 d2633501 Kostas Papadimitriou
        r = client.post('/im/local', post_data, follow=True)
228 d2633501 Kostas Papadimitriou
        self.assertContains(r, "Your new login method has been added")
229 d2633501 Kostas Papadimitriou
230 d2633501 Kostas Papadimitriou
        user = AstakosUser.objects.get(username="kpap@grnet.gr",
231 d2633501 Kostas Papadimitriou
                                       email="kpap@grnet.gr")
232 d2633501 Kostas Papadimitriou
        self.assertTrue(user.has_auth_provider('shibboleth'))
233 d2633501 Kostas Papadimitriou
        self.assertTrue(user.has_auth_provider('local', auth_backend='astakos'))
234 d2633501 Kostas Papadimitriou
        client.logout()
235 d2633501 Kostas Papadimitriou
236 d2633501 Kostas Papadimitriou
        # again ???? show her a message
237 d2633501 Kostas Papadimitriou
        r = client.get('/im/login?key=%s' % pending_key)
238 d2633501 Kostas Papadimitriou
        post_data = {'password': 'password',
239 d2633501 Kostas Papadimitriou
                     'username': 'kpap@grnet.gr',
240 d2633501 Kostas Papadimitriou
                     'key': pending_key}
241 d2633501 Kostas Papadimitriou
        r = self.client.post('/im/local', post_data, follow=True)
242 d2633501 Kostas Papadimitriou
        self.assertContains(r, "Account method assignment failed")
243 d2633501 Kostas Papadimitriou
        self.client.logout()
244 d2633501 Kostas Papadimitriou
        client.logout()
245 d2633501 Kostas Papadimitriou
246 d2633501 Kostas Papadimitriou
        # look Ma, i can login with both my shibboleth and local account
247 d2633501 Kostas Papadimitriou
        client.set_tokens(mail="kpap@shibboleth.gr", eppn="kpapeppn", cn="1")
248 d2633501 Kostas Papadimitriou
        r = client.get("/im/login/shibboleth?", follow=True)
249 d2633501 Kostas Papadimitriou
        self.assertTrue(r.context['request'].user.is_authenticated())
250 d2633501 Kostas Papadimitriou
        self.assertTrue(r.context['request'].user.email == "kpap@grnet.gr")
251 d2633501 Kostas Papadimitriou
        r = client.get("/im/profile")
252 d2633501 Kostas Papadimitriou
        self.assertEquals(r.status_code,200)
253 d2633501 Kostas Papadimitriou
        client.logout()
254 d2633501 Kostas Papadimitriou
        client.reset_tokens()
255 d2633501 Kostas Papadimitriou
        r = client.get("/im/profile", follow=True)
256 d2633501 Kostas Papadimitriou
        self.assertFalse(r.context['request'].user.is_authenticated())
257 d2633501 Kostas Papadimitriou
258 d2633501 Kostas Papadimitriou
        post_data = {'password': 'password',
259 d2633501 Kostas Papadimitriou
                     'username': 'kpap@grnet.gr'}
260 d2633501 Kostas Papadimitriou
        r = self.client.post('/im/local', post_data, follow=True)
261 d2633501 Kostas Papadimitriou
        self.assertTrue(r.context['request'].user.is_authenticated())
262 d2633501 Kostas Papadimitriou
        r = self.client.get("/im/profile")
263 d2633501 Kostas Papadimitriou
        self.assertEquals(r.status_code,200)
264 d2633501 Kostas Papadimitriou
265 d2633501 Kostas Papadimitriou
        r = client.post('/im/local', post_data, follow=True)
266 d2633501 Kostas Papadimitriou
        client.set_tokens(mail="secondary@shibboleth.gr", eppn="kpapeppn", cn="1", )
267 d2633501 Kostas Papadimitriou
        r = client.get("/im/login/shibboleth?", follow=True)
268 d2633501 Kostas Papadimitriou
        client.reset_tokens()
269 d2633501 Kostas Papadimitriou
270 d2633501 Kostas Papadimitriou
        client.logout()
271 d2633501 Kostas Papadimitriou
        client.set_tokens(mail="kpap@grnet.gr", eppn="kpapeppninvalid", cn="1")
272 d2633501 Kostas Papadimitriou
        r = client.get("/im/login/shibboleth?", follow=True)
273 d2633501 Kostas Papadimitriou
        self.assertFalse(r.context['request'].user.is_authenticated())
274 d2633501 Kostas Papadimitriou
275 f432088a Kostas Papadimitriou
        # lets remove local password
276 f432088a Kostas Papadimitriou
        user = AstakosUser.objects.get(username="kpap@grnet.gr",
277 f432088a Kostas Papadimitriou
                                       email="kpap@grnet.gr")
278 f432088a Kostas Papadimitriou
        provider_pk = user.auth_providers.get(module='local').pk
279 f432088a Kostas Papadimitriou
        provider_shib_pk = user.auth_providers.get(module='shibboleth').pk
280 f432088a Kostas Papadimitriou
        client.set_tokens(mail="kpap@shibboleth.gr", eppn="kpapeppn", cn="1")
281 f432088a Kostas Papadimitriou
        r = client.get("/im/login/shibboleth?", follow=True)
282 f432088a Kostas Papadimitriou
        client.reset_tokens()
283 f432088a Kostas Papadimitriou
        r = client.get("/im/remove_auth_provider/%d" % provider_pk)
284 f432088a Kostas Papadimitriou
        self.assertEqual(user.auth_providers.count(), 1)
285 f432088a Kostas Papadimitriou
        r = client.get("/im/remove_auth_provider/%d" % provider_pk)
286 f432088a Kostas Papadimitriou
        self.assertEqual(r.status_code, 404)
287 f432088a Kostas Papadimitriou
        r = client.get("/im/remove_auth_provider/%d" % provider_shib_pk)
288 f432088a Kostas Papadimitriou
        self.assertEqual(r.status_code, 403)
289 f432088a Kostas Papadimitriou
290 f432088a Kostas Papadimitriou
        self.client.logout()
291 f432088a Kostas Papadimitriou
        post_data = {'password': 'password',
292 f432088a Kostas Papadimitriou
                     'username': 'kpap@grnet.gr'}
293 f432088a Kostas Papadimitriou
        r = self.client.post('/im/local', post_data, follow=True)
294 f432088a Kostas Papadimitriou
        self.assertFalse(r.context['request'].user.is_authenticated())
295 f432088a Kostas Papadimitriou
296 f432088a Kostas Papadimitriou
        r = client.get("/im/password_change", follow=True)
297 f432088a Kostas Papadimitriou
        r = client.post("/im/password_change", {'new_password1':'111',
298 f432088a Kostas Papadimitriou
                                                'new_password2': '111'},
299 f432088a Kostas Papadimitriou
                        follow=True)
300 f432088a Kostas Papadimitriou
        user = r.context['request'].user
301 f432088a Kostas Papadimitriou
        self.assertTrue(user.has_auth_provider('local'))
302 f432088a Kostas Papadimitriou
        self.assertTrue(user.has_auth_provider('shibboleth'))
303 f432088a Kostas Papadimitriou
        self.assertTrue(user.check_password('111'))
304 f432088a Kostas Papadimitriou
        self.assertTrue(user.has_usable_password())
305 f432088a Kostas Papadimitriou
        self.client.logout()
306 f432088a Kostas Papadimitriou
        post_data = {'password': '111',
307 f432088a Kostas Papadimitriou
                     'username': 'kpap@grnet.gr'}
308 f432088a Kostas Papadimitriou
        r = self.client.post('/im/local', post_data, follow=True)
309 f432088a Kostas Papadimitriou
        self.assertTrue(r.context['request'].user.is_authenticated())
310 f432088a Kostas Papadimitriou
311 f432088a Kostas Papadimitriou
        client.set_tokens(mail="kpap@shibboleth.gr", eppn="kpapeppn", cn="1")
312 f432088a Kostas Papadimitriou
        r = client.get("/im/login/shibboleth?", follow=True)
313 f432088a Kostas Papadimitriou
        r = client.get("/im/login/shibboleth?", follow=True)
314 f432088a Kostas Papadimitriou
        user = AstakosUser.objects.get(username="kpap@grnet.gr",
315 f432088a Kostas Papadimitriou
                                       email="kpap@grnet.gr")
316 f432088a Kostas Papadimitriou
317 f432088a Kostas Papadimitriou
        user2 = get_local_user('another@grnet.gr')
318 f432088a Kostas Papadimitriou
        user2.add_auth_provider('shibboleth', identifier='existingeppn')
319 f432088a Kostas Papadimitriou
320 f432088a Kostas Papadimitriou
        self.assertEqual(user.auth_providers.count(), 2) # local and 1 shibboleth
321 f432088a Kostas Papadimitriou
        client.set_tokens(mail="kpap_second@shibboleth.gr", eppn="kpapeppn2", cn="1")
322 f432088a Kostas Papadimitriou
        r = client.get("/im/login/shibboleth?", follow=True)
323 f432088a Kostas Papadimitriou
        self.assertEqual(user.auth_providers.count(), 3) # local and 2 shibboleth
324 f432088a Kostas Papadimitriou
325 f432088a Kostas Papadimitriou
        client.set_tokens(mail="kpap_second@shibboleth.gr", eppn="kpapeppn2", cn="1")
326 f432088a Kostas Papadimitriou
        r = client.get("/im/login/shibboleth?", follow=True)
327 f432088a Kostas Papadimitriou
328 f432088a Kostas Papadimitriou
        client.set_tokens(mail="kpap_second@shibboleth.gr", eppn="existingeppn", cn="1")
329 f432088a Kostas Papadimitriou
        r = client.get("/im/login/shibboleth?", follow=True)
330 f432088a Kostas Papadimitriou
        self.assertContains(r, 'Account already exists')
331 2e90e3ec Kostas Papadimitriou
332 d2633501 Kostas Papadimitriou
333 d2633501 Kostas Papadimitriou
class LocalUserTests(TestCase):
334 d2633501 Kostas Papadimitriou
335 d2633501 Kostas Papadimitriou
    fixtures = ['groups']
336 d2633501 Kostas Papadimitriou
337 2e90e3ec Kostas Papadimitriou
    def setUp(self):
338 2e90e3ec Kostas Papadimitriou
        from django.conf import settings
339 2e90e3ec Kostas Papadimitriou
        settings.ADMINS = (('admin', 'support@cloud.grnet.gr'),)
340 2e90e3ec Kostas Papadimitriou
        settings.SERVER_EMAIL = 'no-reply@grnet.gr'
341 2e90e3ec Kostas Papadimitriou
342 7233d542 Kostas Papadimitriou
    def test_no_moderation(self):
343 7233d542 Kostas Papadimitriou
        astakos_settings.MODERATION_ENABLED = False
344 7233d542 Kostas Papadimitriou
        r = self.client.get("/im/signup")
345 7233d542 Kostas Papadimitriou
        self.assertEqual(r.status_code, 200)
346 7233d542 Kostas Papadimitriou
347 7233d542 Kostas Papadimitriou
        data = {'email':'kpap@grnet.gr', 'password1':'password',
348 7233d542 Kostas Papadimitriou
                'password2':'password', 'first_name': 'Kostas',
349 7233d542 Kostas Papadimitriou
                'last_name': 'Mitroglou', 'provider': 'local'}
350 7233d542 Kostas Papadimitriou
        r = self.client.post("/im/signup", data)
351 7233d542 Kostas Papadimitriou
        self.assertEqual(AstakosUser.objects.count(), 1)
352 7233d542 Kostas Papadimitriou
        user = AstakosUser.objects.get(username="kpap@grnet.gr",
353 7233d542 Kostas Papadimitriou
                                       email="kpap@grnet.gr")
354 7233d542 Kostas Papadimitriou
        self.assertEqual(user.username, 'kpap@grnet.gr')
355 7233d542 Kostas Papadimitriou
        self.assertEqual(user.has_auth_provider('local'), True)
356 7233d542 Kostas Papadimitriou
        self.assertFalse(user.is_active)
357 7233d542 Kostas Papadimitriou
358 7233d542 Kostas Papadimitriou
        # user (not admin) gets notified
359 7233d542 Kostas Papadimitriou
        self.assertEqual(len(get_mailbox('support@cloud.grnet.gr')), 0)
360 7233d542 Kostas Papadimitriou
        self.assertEqual(len(get_mailbox('kpap@grnet.gr')), 1)
361 7233d542 Kostas Papadimitriou
        astakos_settings.MODERATION_ENABLED = True
362 d2633501 Kostas Papadimitriou
363 d2633501 Kostas Papadimitriou
    def test_local_provider(self):
364 7233d542 Kostas Papadimitriou
        astakos_settings.MODERATION_ENABLED = True
365 d2633501 Kostas Papadimitriou
        r = self.client.get("/im/signup")
366 d2633501 Kostas Papadimitriou
        self.assertEqual(r.status_code, 200)
367 d2633501 Kostas Papadimitriou
368 d2633501 Kostas Papadimitriou
        data = {'email':'kpap@grnet.gr', 'password1':'password',
369 d2633501 Kostas Papadimitriou
                'password2':'password', 'first_name': 'Kostas',
370 d2633501 Kostas Papadimitriou
                'last_name': 'Mitroglou', 'provider': 'local'}
371 d2633501 Kostas Papadimitriou
        r = self.client.post("/im/signup", data)
372 d2633501 Kostas Papadimitriou
        self.assertEqual(AstakosUser.objects.count(), 1)
373 d2633501 Kostas Papadimitriou
        user = AstakosUser.objects.get(username="kpap@grnet.gr",
374 d2633501 Kostas Papadimitriou
                                       email="kpap@grnet.gr")
375 d2633501 Kostas Papadimitriou
        self.assertEqual(user.username, 'kpap@grnet.gr')
376 d2633501 Kostas Papadimitriou
        self.assertEqual(user.has_auth_provider('local'), True)
377 d2633501 Kostas Papadimitriou
        self.assertFalse(user.is_active)
378 7233d542 Kostas Papadimitriou
        self.assertFalse(user.email_verified)
379 7233d542 Kostas Papadimitriou
        self.assertFalse(user.activation_sent)
380 d2633501 Kostas Papadimitriou
381 d2633501 Kostas Papadimitriou
        # admin gets notified
382 d2633501 Kostas Papadimitriou
        self.assertEqual(len(get_mailbox('support@cloud.grnet.gr')), 1)
383 d2633501 Kostas Papadimitriou
        # and sends user activation email
384 d2633501 Kostas Papadimitriou
        functions.send_activation(user)
385 d2633501 Kostas Papadimitriou
386 d2633501 Kostas Papadimitriou
        # user activation fields updated
387 d2633501 Kostas Papadimitriou
        user = AstakosUser.objects.get(pk=user.pk)
388 d2633501 Kostas Papadimitriou
        self.assertTrue(user.activation_sent)
389 d2633501 Kostas Papadimitriou
        self.assertFalse(user.email_verified)
390 d2633501 Kostas Papadimitriou
        # email sent to user
391 d2633501 Kostas Papadimitriou
        self.assertEqual(len(get_mailbox('kpap@grnet.gr')), 1)
392 d2633501 Kostas Papadimitriou
393 d2633501 Kostas Papadimitriou
        # user forgot she got registered and tries to submit registration
394 d2633501 Kostas Papadimitriou
        # form. Notice the upper case in email
395 d2633501 Kostas Papadimitriou
        data = {'email':'KPAP@grnet.gr', 'password1':'password',
396 d2633501 Kostas Papadimitriou
                'password2':'password', 'first_name': 'Kostas',
397 d2633501 Kostas Papadimitriou
                'last_name': 'Mitroglou', 'provider': 'local'}
398 d2633501 Kostas Papadimitriou
        r = self.client.post("/im/signup", data)
399 2e90e3ec Kostas Papadimitriou
        self.assertContains(r, messages.EMAIL_USED)
400 d2633501 Kostas Papadimitriou
401 d2633501 Kostas Papadimitriou
        # hmmm, email exists; lets get the password
402 d2633501 Kostas Papadimitriou
        r = self.client.get('/im/local/password_reset')
403 d2633501 Kostas Papadimitriou
        self.assertEqual(r.status_code, 200)
404 d2633501 Kostas Papadimitriou
        r = self.client.post('/im/local/password_reset', {'email':
405 d2633501 Kostas Papadimitriou
                                                          'kpap@grnet.gr'})
406 d2633501 Kostas Papadimitriou
        # she can't because account is not active yet
407 d2633501 Kostas Papadimitriou
        self.assertContains(r, "doesn't have an associated user account")
408 d2633501 Kostas Papadimitriou
409 d2633501 Kostas Papadimitriou
        # moderation is enabled so no automatic activation can be send
410 d2633501 Kostas Papadimitriou
        r = self.client.get('/im/send/activation/%d' % user.pk)
411 d2633501 Kostas Papadimitriou
        self.assertEqual(r.status_code, 403)
412 d2633501 Kostas Papadimitriou
        self.assertEqual(len(get_mailbox('kpap@grnet.gr')), 1)
413 d2633501 Kostas Papadimitriou
        # also she cannot login
414 d2633501 Kostas Papadimitriou
        r = self.client.post('/im/local', {'username': 'kpap@grnet.gr',
415 d2633501 Kostas Papadimitriou
                                                 'password': 'password'})
416 d2633501 Kostas Papadimitriou
        self.assertContains(r, 'You have not followed the activation link')
417 d2633501 Kostas Papadimitriou
        self.assertNotContains(r, 'Resend activation')
418 d2633501 Kostas Papadimitriou
        self.assertFalse(r.context['request'].user.is_authenticated())
419 d2633501 Kostas Papadimitriou
        self.assertFalse('_pithos2_a' in self.client.cookies)
420 d2633501 Kostas Papadimitriou
421 d2633501 Kostas Papadimitriou
        # lets disable moderation
422 d2633501 Kostas Papadimitriou
        astakos_settings.MODERATION_ENABLED = False
423 d2633501 Kostas Papadimitriou
        r = self.client.post('/im/local/password_reset', {'email':
424 d2633501 Kostas Papadimitriou
                                                          'kpap@grnet.gr'})
425 d2633501 Kostas Papadimitriou
        self.assertContains(r, "doesn't have an associated user account")
426 d2633501 Kostas Papadimitriou
        r = self.client.post('/im/local', {'username': 'kpap@grnet.gr',
427 d2633501 Kostas Papadimitriou
                                                 'password': 'password'})
428 d2633501 Kostas Papadimitriou
        self.assertContains(r, 'You have not followed the activation link')
429 d2633501 Kostas Papadimitriou
        self.assertContains(r, 'Resend activation')
430 d2633501 Kostas Papadimitriou
        self.assertFalse(r.context['request'].user.is_authenticated())
431 d2633501 Kostas Papadimitriou
        self.assertFalse('_pithos2_a' in self.client.cookies)
432 d2633501 Kostas Papadimitriou
        # user sees the message and resends activation
433 d2633501 Kostas Papadimitriou
        r = self.client.get('/im/send/activation/%d' % user.pk)
434 d2633501 Kostas Papadimitriou
        # email sent
435 d2633501 Kostas Papadimitriou
        self.assertEqual(len(get_mailbox('kpap@grnet.gr')), 2)
436 d2633501 Kostas Papadimitriou
437 d2633501 Kostas Papadimitriou
        # switch back moderation setting
438 d2633501 Kostas Papadimitriou
        astakos_settings.MODERATION_ENABLED = True
439 d2633501 Kostas Papadimitriou
        # lets activate the user
440 d2633501 Kostas Papadimitriou
        r = self.client.get(user.get_activation_url(), follow=True)
441 d2633501 Kostas Papadimitriou
        self.assertRedirects(r, "/im/profile")
442 d2633501 Kostas Papadimitriou
        self.assertContains(r, "kpap@grnet.gr")
443 d2633501 Kostas Papadimitriou
        self.assertEqual(len(get_mailbox('kpap@grnet.gr')), 3)
444 d2633501 Kostas Papadimitriou
445 d2633501 Kostas Papadimitriou
        user = AstakosUser.objects.get(pk=user.pk)
446 d2633501 Kostas Papadimitriou
        # user activated and logged in, token cookie set
447 d2633501 Kostas Papadimitriou
        self.assertTrue(r.context['request'].user.is_authenticated())
448 d2633501 Kostas Papadimitriou
        self.assertTrue('_pithos2_a' in self.client.cookies)
449 d2633501 Kostas Papadimitriou
        cookies = self.client.cookies
450 d2633501 Kostas Papadimitriou
        self.assertTrue(quote(user.auth_token) in cookies.get('_pithos2_a').value)
451 d2633501 Kostas Papadimitriou
        r = self.client.get('/im/logout', follow=True)
452 d2633501 Kostas Papadimitriou
        r = self.client.get('/im/')
453 d2633501 Kostas Papadimitriou
        # user logged out, token cookie removed
454 d2633501 Kostas Papadimitriou
        self.assertFalse(r.context['request'].user.is_authenticated())
455 d2633501 Kostas Papadimitriou
        self.assertFalse(self.client.cookies.get('_pithos2_a').value)
456 d2633501 Kostas Papadimitriou
        # https://docs.djangoproject.com/en/dev/topics/testing/#persistent-state
457 d2633501 Kostas Papadimitriou
        del self.client.cookies['_pithos2_a']
458 d2633501 Kostas Papadimitriou
459 d2633501 Kostas Papadimitriou
        # user can login
460 d2633501 Kostas Papadimitriou
        r = self.client.post('/im/local', {'username': 'kpap@grnet.gr',
461 d2633501 Kostas Papadimitriou
                                           'password': 'password'},
462 d2633501 Kostas Papadimitriou
                                          follow=True)
463 d2633501 Kostas Papadimitriou
        self.assertTrue(r.context['request'].user.is_authenticated())
464 d2633501 Kostas Papadimitriou
        self.assertTrue('_pithos2_a' in self.client.cookies)
465 d2633501 Kostas Papadimitriou
        cookies = self.client.cookies
466 d2633501 Kostas Papadimitriou
        self.assertTrue(quote(user.auth_token) in cookies.get('_pithos2_a').value)
467 d2633501 Kostas Papadimitriou
        self.client.get('/im/logout', follow=True)
468 d2633501 Kostas Papadimitriou
469 d2633501 Kostas Papadimitriou
        # user forgot password
470 d2633501 Kostas Papadimitriou
        old_pass = user.password
471 d2633501 Kostas Papadimitriou
        r = self.client.get('/im/local/password_reset')
472 d2633501 Kostas Papadimitriou
        self.assertEqual(r.status_code, 200)
473 d2633501 Kostas Papadimitriou
        r = self.client.post('/im/local/password_reset', {'email':
474 d2633501 Kostas Papadimitriou
                                                          'kpap@grnet.gr'})
475 d2633501 Kostas Papadimitriou
        self.assertEqual(r.status_code, 302)
476 d2633501 Kostas Papadimitriou
        # email sent
477 d2633501 Kostas Papadimitriou
        self.assertEqual(len(get_mailbox('kpap@grnet.gr')), 4)
478 d2633501 Kostas Papadimitriou
479 d2633501 Kostas Papadimitriou
        # user visits change password link
480 d2633501 Kostas Papadimitriou
        r = self.client.get(user.get_password_reset_url())
481 d2633501 Kostas Papadimitriou
        r = self.client.post(user.get_password_reset_url(),
482 d2633501 Kostas Papadimitriou
                            {'new_password1':'newpass',
483 d2633501 Kostas Papadimitriou
                             'new_password2':'newpass'})
484 d2633501 Kostas Papadimitriou
485 d2633501 Kostas Papadimitriou
        user = AstakosUser.objects.get(pk=user.pk)
486 d2633501 Kostas Papadimitriou
        self.assertNotEqual(old_pass, user.password)
487 d2633501 Kostas Papadimitriou
488 d2633501 Kostas Papadimitriou
        # old pass is not usable
489 d2633501 Kostas Papadimitriou
        r = self.client.post('/im/local', {'username': 'kpap@grnet.gr',
490 d2633501 Kostas Papadimitriou
                                           'password': 'password'})
491 d2633501 Kostas Papadimitriou
        self.assertContains(r, 'Please enter a correct username and password')
492 d2633501 Kostas Papadimitriou
        r = self.client.post('/im/local', {'username': 'kpap@grnet.gr',
493 d2633501 Kostas Papadimitriou
                                           'password': 'newpass'},
494 d2633501 Kostas Papadimitriou
                                           follow=True)
495 d2633501 Kostas Papadimitriou
        self.assertTrue(r.context['request'].user.is_authenticated())
496 d2633501 Kostas Papadimitriou
        self.client.logout()
497 d2633501 Kostas Papadimitriou
498 d2633501 Kostas Papadimitriou
        # tests of special local backends
499 d2633501 Kostas Papadimitriou
        user = AstakosUser.objects.get(pk=user.pk)
500 d2633501 Kostas Papadimitriou
        user.auth_providers.filter(module='local').update(auth_backend='ldap')
501 d2633501 Kostas Papadimitriou
        user.save()
502 d2633501 Kostas Papadimitriou
503 d2633501 Kostas Papadimitriou
        # non astakos local backends do not support password reset
504 d2633501 Kostas Papadimitriou
        r = self.client.get('/im/local/password_reset')
505 d2633501 Kostas Papadimitriou
        self.assertEqual(r.status_code, 200)
506 d2633501 Kostas Papadimitriou
        r = self.client.post('/im/local/password_reset', {'email':
507 d2633501 Kostas Papadimitriou
                                                          'kpap@grnet.gr'})
508 d2633501 Kostas Papadimitriou
        # she can't because account is not active yet
509 d2633501 Kostas Papadimitriou
        self.assertContains(r, "Password change for this account is not"
510 d2633501 Kostas Papadimitriou
                                " supported")