Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / tests.py @ be62cfa8

History | View | Annotate | Download (29.5 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 e5966bd9 Kostas Papadimitriou
from astakos.im import forms
45 d2633501 Kostas Papadimitriou
46 d2633501 Kostas Papadimitriou
from urllib import quote
47 d2633501 Kostas Papadimitriou
48 2e90e3ec Kostas Papadimitriou
from astakos.im import messages
49 2e90e3ec Kostas Papadimitriou
50 34a76cdb Kostas Papadimitriou
51 34a76cdb Kostas Papadimitriou
astakos_settings.EMAILCHANGE_ENABLED = True
52 34a76cdb Kostas Papadimitriou
53 d2633501 Kostas Papadimitriou
class ShibbolethClient(Client):
54 d2633501 Kostas Papadimitriou
    """
55 d2633501 Kostas Papadimitriou
    A shibboleth agnostic client.
56 d2633501 Kostas Papadimitriou
    """
57 d2633501 Kostas Papadimitriou
    VALID_TOKENS = filter(lambda x: not x.startswith("_"), dir(ShibbolethTokens))
58 d2633501 Kostas Papadimitriou
59 d2633501 Kostas Papadimitriou
    def __init__(self, *args, **kwargs):
60 d2633501 Kostas Papadimitriou
        self.tokens = kwargs.pop('tokens', {})
61 d2633501 Kostas Papadimitriou
        super(ShibbolethClient, self).__init__(*args, **kwargs)
62 d2633501 Kostas Papadimitriou
63 d2633501 Kostas Papadimitriou
    def set_tokens(self, **kwargs):
64 d2633501 Kostas Papadimitriou
        for key, value in kwargs.iteritems():
65 d2633501 Kostas Papadimitriou
            key = 'SHIB_%s' % key.upper()
66 d2633501 Kostas Papadimitriou
            if not key in self.VALID_TOKENS:
67 d2633501 Kostas Papadimitriou
                raise Exception('Invalid shibboleth token')
68 d2633501 Kostas Papadimitriou
69 d2633501 Kostas Papadimitriou
            self.tokens[key] = value
70 d2633501 Kostas Papadimitriou
71 d2633501 Kostas Papadimitriou
    def unset_tokens(self, *keys):
72 d2633501 Kostas Papadimitriou
        for key in keys:
73 d2633501 Kostas Papadimitriou
            key = 'SHIB_%s' % param.upper()
74 d2633501 Kostas Papadimitriou
            if key in self.tokens:
75 d2633501 Kostas Papadimitriou
                del self.tokens[key]
76 d2633501 Kostas Papadimitriou
77 d2633501 Kostas Papadimitriou
    def reset_tokens(self):
78 d2633501 Kostas Papadimitriou
        self.tokens = {}
79 d2633501 Kostas Papadimitriou
80 d2633501 Kostas Papadimitriou
    def get_http_token(self, key):
81 d2633501 Kostas Papadimitriou
        http_header = getattr(ShibbolethTokens, key)
82 d2633501 Kostas Papadimitriou
        return http_header
83 d2633501 Kostas Papadimitriou
84 d2633501 Kostas Papadimitriou
    def request(self, **request):
85 d2633501 Kostas Papadimitriou
        """
86 d2633501 Kostas Papadimitriou
        Transform valid shibboleth tokens to http headers
87 d2633501 Kostas Papadimitriou
        """
88 d2633501 Kostas Papadimitriou
        for token, value in self.tokens.iteritems():
89 d2633501 Kostas Papadimitriou
            request[self.get_http_token(token)] = value
90 d2633501 Kostas Papadimitriou
91 d2633501 Kostas Papadimitriou
        for param in request.keys():
92 d2633501 Kostas Papadimitriou
            key = 'SHIB_%s' % param.upper()
93 d2633501 Kostas Papadimitriou
            if key in self.VALID_TOKENS:
94 d2633501 Kostas Papadimitriou
                request[self.get_http_token(key)] = request[param]
95 d2633501 Kostas Papadimitriou
                del request[param]
96 d2633501 Kostas Papadimitriou
97 d2633501 Kostas Papadimitriou
        return super(ShibbolethClient, self).request(**request)
98 d2633501 Kostas Papadimitriou
99 d2633501 Kostas Papadimitriou
100 d2633501 Kostas Papadimitriou
def get_local_user(username, **kwargs):
101 d2633501 Kostas Papadimitriou
        try:
102 d2633501 Kostas Papadimitriou
            return AstakosUser.objects.get(email=username)
103 d2633501 Kostas Papadimitriou
        except:
104 d2633501 Kostas Papadimitriou
            user_params = {
105 d2633501 Kostas Papadimitriou
                'username': username,
106 d2633501 Kostas Papadimitriou
                'email': username,
107 d2633501 Kostas Papadimitriou
                'is_active': True,
108 d2633501 Kostas Papadimitriou
                'activation_sent': datetime.now(),
109 d2633501 Kostas Papadimitriou
                'email_verified': True,
110 d2633501 Kostas Papadimitriou
                'provider': 'local'
111 d2633501 Kostas Papadimitriou
            }
112 d2633501 Kostas Papadimitriou
            user_params.update(kwargs)
113 d2633501 Kostas Papadimitriou
            user = AstakosUser(**user_params)
114 d2633501 Kostas Papadimitriou
            user.set_password(kwargs.get('password', 'password'))
115 d2633501 Kostas Papadimitriou
            user.save()
116 d2633501 Kostas Papadimitriou
            user.add_auth_provider('local', auth_backend='astakos')
117 d2633501 Kostas Papadimitriou
            if kwargs.get('is_active', True):
118 d2633501 Kostas Papadimitriou
                user.is_active = True
119 d2633501 Kostas Papadimitriou
            else:
120 d2633501 Kostas Papadimitriou
                user.is_active = False
121 d2633501 Kostas Papadimitriou
            user.save()
122 d2633501 Kostas Papadimitriou
            return user
123 d2633501 Kostas Papadimitriou
124 d2633501 Kostas Papadimitriou
125 d2633501 Kostas Papadimitriou
def get_mailbox(email):
126 d2633501 Kostas Papadimitriou
    mails = []
127 d2633501 Kostas Papadimitriou
    for sent_email in mail.outbox:
128 d2633501 Kostas Papadimitriou
        for recipient in sent_email.recipients():
129 d2633501 Kostas Papadimitriou
            if email in recipient:
130 d2633501 Kostas Papadimitriou
                mails.append(sent_email)
131 d2633501 Kostas Papadimitriou
    return mails
132 d2633501 Kostas Papadimitriou
133 d2633501 Kostas Papadimitriou
134 d2633501 Kostas Papadimitriou
class ShibbolethTests(TestCase):
135 d2633501 Kostas Papadimitriou
    """
136 d2633501 Kostas Papadimitriou
    Testing shibboleth authentication.
137 d2633501 Kostas Papadimitriou
    """
138 d2633501 Kostas Papadimitriou
139 d2633501 Kostas Papadimitriou
    fixtures = ['groups']
140 d2633501 Kostas Papadimitriou
141 d2633501 Kostas Papadimitriou
    def setUp(self):
142 1af308f1 Kostas Papadimitriou
        kind = GroupKind.objects.create(name="default")
143 1af308f1 Kostas Papadimitriou
        AstakosGroup.objects.create(name="default", kind=kind)
144 d2633501 Kostas Papadimitriou
        self.client = ShibbolethClient()
145 d2633501 Kostas Papadimitriou
        settings.ASTAKOS_IM_MODULES = ['local', 'shibboleth']
146 ba50648c Kostas Papadimitriou
        settings.ASTAKOS_MODERATION_ENABLED = True
147 d2633501 Kostas Papadimitriou
148 d2633501 Kostas Papadimitriou
    def test_create_account(self):
149 ba50648c Kostas Papadimitriou
150 d2633501 Kostas Papadimitriou
        client = ShibbolethClient()
151 d2633501 Kostas Papadimitriou
152 d2633501 Kostas Papadimitriou
        # shibboleth views validation
153 d2633501 Kostas Papadimitriou
        # eepn required
154 d2633501 Kostas Papadimitriou
        r = client.get('/im/login/shibboleth?', follow=True)
155 2e90e3ec Kostas Papadimitriou
        self.assertContains(r, messages.SHIBBOLETH_MISSING_EPPN)
156 d2633501 Kostas Papadimitriou
        client.set_tokens(eppn="kpapeppn")
157 ba50648c Kostas Papadimitriou
158 31fdafa8 Kostas Papadimitriou
        astakos_settings.SHIBBOLETH_REQUIRE_NAME_INFO = True
159 d2633501 Kostas Papadimitriou
        # shibboleth user info required
160 d2633501 Kostas Papadimitriou
        r = client.get('/im/login/shibboleth?', follow=True)
161 2e90e3ec Kostas Papadimitriou
        self.assertContains(r, messages.SHIBBOLETH_MISSING_NAME)
162 31fdafa8 Kostas Papadimitriou
        astakos_settings.SHIBBOLETH_REQUIRE_NAME_INFO = False
163 d2633501 Kostas Papadimitriou
164 d2633501 Kostas Papadimitriou
        # shibboleth logged us in
165 e5966bd9 Kostas Papadimitriou
        client.set_tokens(mail="kpap@grnet.gr", eppn="kpapeppn",
166 e5966bd9 Kostas Papadimitriou
                          cn="Kostas Papadimitriou",
167 ba50648c Kostas Papadimitriou
                          ep_affiliation="Test Affiliation")
168 d2633501 Kostas Papadimitriou
        r = client.get('/im/login/shibboleth?')
169 ba50648c Kostas Papadimitriou
        self.assertEqual(r.status_code, 200)
170 d2633501 Kostas Papadimitriou
171 d2633501 Kostas Papadimitriou
        # astakos asks if we want to add shibboleth
172 d2633501 Kostas Papadimitriou
        self.assertContains(r, "Already have an account?")
173 d2633501 Kostas Papadimitriou
174 d2633501 Kostas Papadimitriou
        # a new pending user created
175 d2633501 Kostas Papadimitriou
        pending_user = PendingThirdPartyUser.objects.get(
176 d2633501 Kostas Papadimitriou
            third_party_identifier="kpapeppn")
177 d2633501 Kostas Papadimitriou
        self.assertEqual(PendingThirdPartyUser.objects.count(), 1)
178 ba50648c Kostas Papadimitriou
        # keep the token for future use
179 d2633501 Kostas Papadimitriou
        token = pending_user.token
180 d2633501 Kostas Papadimitriou
        # from now on no shibboleth headers are sent to the server
181 d2633501 Kostas Papadimitriou
        client.reset_tokens()
182 d2633501 Kostas Papadimitriou
183 ba50648c Kostas Papadimitriou
        # this is the old way, it should fail, to avoid pending user take over
184 d2633501 Kostas Papadimitriou
        r = client.get('/im/shibboleth/signup/%s' % pending_user.username)
185 d2633501 Kostas Papadimitriou
        self.assertEqual(r.status_code, 404)
186 d2633501 Kostas Papadimitriou
187 ba50648c Kostas Papadimitriou
        # this is the signup unique url associated with the pending user created
188 ba50648c Kostas Papadimitriou
        r = client.get('/im/signup/?third_party_token=%s' % token)
189 d2633501 Kostas Papadimitriou
        form = r.context['form']
190 ba50648c Kostas Papadimitriou
        post_data = {'third_party_identifier': pending_user.third_party_identifier,
191 d2633501 Kostas Papadimitriou
                     'first_name': 'Kostas',
192 d2633501 Kostas Papadimitriou
                     'third_party_token': token,
193 d2633501 Kostas Papadimitriou
                     'last_name': 'Mitroglou',
194 d2633501 Kostas Papadimitriou
                     'provider': 'shibboleth'
195 d2633501 Kostas Papadimitriou
                    }
196 ba50648c Kostas Papadimitriou
197 ba50648c Kostas Papadimitriou
        # invlid email
198 ba50648c Kostas Papadimitriou
        post_data['email'] = 'kpap'
199 d2633501 Kostas Papadimitriou
        r = client.post('/im/signup', post_data)
200 8ab484ea Kostas Papadimitriou
        self.assertContains(r, token)
201 ba50648c Kostas Papadimitriou
202 ba50648c Kostas Papadimitriou
        # existing email
203 ba50648c Kostas Papadimitriou
        existing_user = get_local_user('test@test.com')
204 ba50648c Kostas Papadimitriou
        post_data['email'] = 'test@test.com'
205 ba50648c Kostas Papadimitriou
        r = client.post('/im/signup', post_data)
206 ba50648c Kostas Papadimitriou
        self.assertContains(r, messages.EMAIL_USED)
207 ba50648c Kostas Papadimitriou
        existing_user.delete()
208 ba50648c Kostas Papadimitriou
209 ba50648c Kostas Papadimitriou
        # and finally a valid signup
210 8ab484ea Kostas Papadimitriou
        post_data['email'] = 'kpap@grnet.gr'
211 e5966bd9 Kostas Papadimitriou
        r = client.post('/im/signup', post_data, follow=True)
212 e5966bd9 Kostas Papadimitriou
        self.assertContains(r, messages.NOTIFICATION_SENT)
213 ba50648c Kostas Papadimitriou
214 ba50648c Kostas Papadimitriou
        # everything is ok in our db
215 d2633501 Kostas Papadimitriou
        self.assertEqual(AstakosUser.objects.count(), 1)
216 d2633501 Kostas Papadimitriou
        self.assertEqual(AstakosUserAuthProvider.objects.count(), 1)
217 ba50648c Kostas Papadimitriou
        self.assertEqual(PendingThirdPartyUser.objects.count(), 0)
218 d2633501 Kostas Papadimitriou
219 ba50648c Kostas Papadimitriou
        # provider info stored
220 ba50648c Kostas Papadimitriou
        provider = AstakosUserAuthProvider.objects.get(module="shibboleth")
221 ba50648c Kostas Papadimitriou
        self.assertEqual(provider.affiliation, 'Test Affiliation')
222 ba50648c Kostas Papadimitriou
        self.assertEqual(provider.info, {u'email': u'kpap@grnet.gr',
223 e5966bd9 Kostas Papadimitriou
                                         u'eppn': u'kpapeppn',
224 e5966bd9 Kostas Papadimitriou
                                         u'name': u'Kostas Papadimitriou'})
225 d2633501 Kostas Papadimitriou
226 ba50648c Kostas Papadimitriou
        # lets login (not activated yet)
227 e5966bd9 Kostas Papadimitriou
        client.set_tokens(mail="kpap@grnet.gr", eppn="kpapeppn",
228 e5966bd9 Kostas Papadimitriou
                          cn="Kostas Papadimitriou", )
229 d2633501 Kostas Papadimitriou
        r = client.get("/im/login/shibboleth?", follow=True)
230 31fdafa8 Kostas Papadimitriou
        self.assertContains(r, messages.ACCOUNT_PENDING_MODERATION)
231 d2633501 Kostas Papadimitriou
        r = client.get("/im/profile", follow=True)
232 d2633501 Kostas Papadimitriou
        self.assertRedirects(r, 'http://testserver/im/?next=%2Fim%2Fprofile')
233 d2633501 Kostas Papadimitriou
234 ba50648c Kostas Papadimitriou
        # admin activates our user
235 ba50648c Kostas Papadimitriou
        u = AstakosUser.objects.get(username="kpap@grnet.gr")
236 d2633501 Kostas Papadimitriou
        functions.activate(u)
237 d2633501 Kostas Papadimitriou
        self.assertEqual(u.is_active, True)
238 d2633501 Kostas Papadimitriou
239 ba50648c Kostas Papadimitriou
        # we see our profile
240 ba50648c Kostas Papadimitriou
        r = client.get("/im/login/shibboleth?", follow=True)
241 d2633501 Kostas Papadimitriou
        self.assertRedirects(r, '/im/profile')
242 ba50648c Kostas Papadimitriou
        self.assertEqual(r.status_code, 200)
243 d2633501 Kostas Papadimitriou
244 d2633501 Kostas Papadimitriou
    def test_existing(self):
245 ba50648c Kostas Papadimitriou
        """
246 ba50648c Kostas Papadimitriou
        Test adding of third party login to an existing account
247 ba50648c Kostas Papadimitriou
        """
248 ba50648c Kostas Papadimitriou
249 ba50648c Kostas Papadimitriou
        # this is our existing user
250 d2633501 Kostas Papadimitriou
        existing_user = get_local_user('kpap@grnet.gr')
251 d2633501 Kostas Papadimitriou
252 d2633501 Kostas Papadimitriou
        client = ShibbolethClient()
253 d2633501 Kostas Papadimitriou
        # shibboleth logged us in, notice that we use different email
254 e5966bd9 Kostas Papadimitriou
        client.set_tokens(mail="kpap@shibboleth.gr", eppn="kpapeppn",
255 e5966bd9 Kostas Papadimitriou
                          cn="Kostas Papadimitriou", )
256 d2633501 Kostas Papadimitriou
        r = client.get("/im/login/shibboleth?")
257 ba50648c Kostas Papadimitriou
258 d2633501 Kostas Papadimitriou
        # astakos asks if we want to switch a local account to shibboleth
259 d2633501 Kostas Papadimitriou
        self.assertContains(r, "Already have an account?")
260 d2633501 Kostas Papadimitriou
261 d2633501 Kostas Papadimitriou
        # a new pending user created
262 d2633501 Kostas Papadimitriou
        pending_user = PendingThirdPartyUser.objects.get()
263 d2633501 Kostas Papadimitriou
        self.assertEqual(PendingThirdPartyUser.objects.count(), 1)
264 d2633501 Kostas Papadimitriou
        pending_key = pending_user.token
265 d2633501 Kostas Papadimitriou
        client.reset_tokens()
266 d2633501 Kostas Papadimitriou
267 d2633501 Kostas Papadimitriou
        # we choose to add shibboleth to an our existing account
268 d2633501 Kostas Papadimitriou
        # we get redirected to login page with the pending token set
269 d2633501 Kostas Papadimitriou
        r = client.get('/im/login?key=%s' % pending_key)
270 d2633501 Kostas Papadimitriou
        post_data = {'password': 'password',
271 d2633501 Kostas Papadimitriou
                     'username': 'kpap@grnet.gr',
272 d2633501 Kostas Papadimitriou
                     'key': pending_key}
273 d2633501 Kostas Papadimitriou
        r = client.post('/im/local', post_data, follow=True)
274 ba50648c Kostas Papadimitriou
        self.assertRedirects(r, "/im/profile")
275 e5966bd9 Kostas Papadimitriou
        self.assertContains(r, messages.AUTH_PROVIDER_ADDED)
276 d2633501 Kostas Papadimitriou
277 ba50648c Kostas Papadimitriou
        self.assertTrue(existing_user.has_auth_provider('shibboleth'))
278 ba50648c Kostas Papadimitriou
        self.assertTrue(existing_user.has_auth_provider('local',
279 ba50648c Kostas Papadimitriou
                                                        auth_backend='astakos'))
280 d2633501 Kostas Papadimitriou
        client.logout()
281 d2633501 Kostas Papadimitriou
282 ba50648c Kostas Papadimitriou
        # check that we cannot assign same third party provide twice
283 d2633501 Kostas Papadimitriou
        r = client.get('/im/login?key=%s' % pending_key)
284 d2633501 Kostas Papadimitriou
        post_data = {'password': 'password',
285 d2633501 Kostas Papadimitriou
                     'username': 'kpap@grnet.gr',
286 d2633501 Kostas Papadimitriou
                     'key': pending_key}
287 d2633501 Kostas Papadimitriou
        r = self.client.post('/im/local', post_data, follow=True)
288 31fdafa8 Kostas Papadimitriou
        self.assertContains(r, messages.AUTH_PROVIDER_ADD_FAILED)
289 d2633501 Kostas Papadimitriou
        self.client.logout()
290 d2633501 Kostas Papadimitriou
        client.logout()
291 d2633501 Kostas Papadimitriou
292 d2633501 Kostas Papadimitriou
        # look Ma, i can login with both my shibboleth and local account
293 e5966bd9 Kostas Papadimitriou
        client.set_tokens(mail="kpap@shibboleth.gr", eppn="kpapeppn",
294 e5966bd9 Kostas Papadimitriou
                          cn="Kostas Papadimitriou")
295 d2633501 Kostas Papadimitriou
        r = client.get("/im/login/shibboleth?", follow=True)
296 d2633501 Kostas Papadimitriou
        self.assertTrue(r.context['request'].user.is_authenticated())
297 d2633501 Kostas Papadimitriou
        self.assertTrue(r.context['request'].user.email == "kpap@grnet.gr")
298 ba50648c Kostas Papadimitriou
        self.assertRedirects(r, '/im/profile')
299 ba50648c Kostas Papadimitriou
        self.assertEqual(r.status_code, 200)
300 d2633501 Kostas Papadimitriou
        client.logout()
301 d2633501 Kostas Papadimitriou
        client.reset_tokens()
302 ba50648c Kostas Papadimitriou
303 ba50648c Kostas Papadimitriou
        # logged out
304 d2633501 Kostas Papadimitriou
        r = client.get("/im/profile", follow=True)
305 d2633501 Kostas Papadimitriou
        self.assertFalse(r.context['request'].user.is_authenticated())
306 d2633501 Kostas Papadimitriou
307 ba50648c Kostas Papadimitriou
        # login with local account also works
308 d2633501 Kostas Papadimitriou
        post_data = {'password': 'password',
309 d2633501 Kostas Papadimitriou
                     'username': 'kpap@grnet.gr'}
310 d2633501 Kostas Papadimitriou
        r = self.client.post('/im/local', post_data, follow=True)
311 d2633501 Kostas Papadimitriou
        self.assertTrue(r.context['request'].user.is_authenticated())
312 ba50648c Kostas Papadimitriou
        self.assertTrue(r.context['request'].user.email == "kpap@grnet.gr")
313 ba50648c Kostas Papadimitriou
        self.assertRedirects(r, '/im/profile')
314 ba50648c Kostas Papadimitriou
        self.assertEqual(r.status_code, 200)
315 d2633501 Kostas Papadimitriou
316 ba50648c Kostas Papadimitriou
        # cannot add the same eppn
317 e5966bd9 Kostas Papadimitriou
        client.set_tokens(mail="secondary@shibboleth.gr", eppn="kpapeppn",
318 e5966bd9 Kostas Papadimitriou
                          cn="Kostas Papadimitriou", )
319 d2633501 Kostas Papadimitriou
        r = client.get("/im/login/shibboleth?", follow=True)
320 ba50648c Kostas Papadimitriou
        self.assertRedirects(r, '/im/profile')
321 ba50648c Kostas Papadimitriou
        self.assertTrue(r.status_code, 200)
322 ba50648c Kostas Papadimitriou
        self.assertEquals(existing_user.auth_providers.count(), 2)
323 d2633501 Kostas Papadimitriou
324 ba50648c Kostas Papadimitriou
        # but can add additional eppn
325 ba50648c Kostas Papadimitriou
        client.set_tokens(mail="secondary@shibboleth.gr", eppn="kpapeppn2",
326 e5966bd9 Kostas Papadimitriou
                          cn="Kostas Papadimitriou", ep_affiliation="affil2")
327 ba50648c Kostas Papadimitriou
        r = client.get("/im/login/shibboleth?", follow=True)
328 ba50648c Kostas Papadimitriou
        new_provider = existing_user.auth_providers.get(identifier="kpapeppn2")
329 ba50648c Kostas Papadimitriou
        self.assertRedirects(r, '/im/profile')
330 ba50648c Kostas Papadimitriou
        self.assertTrue(r.status_code, 200)
331 ba50648c Kostas Papadimitriou
        self.assertEquals(existing_user.auth_providers.count(), 3)
332 ba50648c Kostas Papadimitriou
        self.assertEqual(new_provider.affiliation, 'affil2')
333 d2633501 Kostas Papadimitriou
        client.logout()
334 ba50648c Kostas Papadimitriou
        client.reset_tokens()
335 ba50648c Kostas Papadimitriou
336 ba50648c Kostas Papadimitriou
        # cannot login with another eppn
337 e5966bd9 Kostas Papadimitriou
        client.set_tokens(mail="kpap@grnet.gr", eppn="kpapeppninvalid",
338 e5966bd9 Kostas Papadimitriou
                          cn="Kostas Papadimitriou")
339 d2633501 Kostas Papadimitriou
        r = client.get("/im/login/shibboleth?", follow=True)
340 d2633501 Kostas Papadimitriou
        self.assertFalse(r.context['request'].user.is_authenticated())
341 d2633501 Kostas Papadimitriou
342 f432088a Kostas Papadimitriou
        # lets remove local password
343 f432088a Kostas Papadimitriou
        user = AstakosUser.objects.get(username="kpap@grnet.gr",
344 f432088a Kostas Papadimitriou
                                       email="kpap@grnet.gr")
345 ba50648c Kostas Papadimitriou
        remove_local_url = user.get_provider_remove_url('local')
346 ba50648c Kostas Papadimitriou
        remove_shibbo_url = user.get_provider_remove_url('shibboleth',
347 ba50648c Kostas Papadimitriou
                                                         identifier='kpapeppn')
348 ba50648c Kostas Papadimitriou
        remove_shibbo2_url = user.get_provider_remove_url('shibboleth',
349 ba50648c Kostas Papadimitriou
                                                         identifier='kpapeppn2')
350 e5966bd9 Kostas Papadimitriou
        client.set_tokens(mail="kpap@shibboleth.gr", eppn="kpapeppn",
351 e5966bd9 Kostas Papadimitriou
                          cn="Kostas Papadimtriou")
352 f432088a Kostas Papadimitriou
        r = client.get("/im/login/shibboleth?", follow=True)
353 f432088a Kostas Papadimitriou
        client.reset_tokens()
354 ba50648c Kostas Papadimitriou
355 ba50648c Kostas Papadimitriou
        # TODO: this view should use POST
356 ba50648c Kostas Papadimitriou
        r = client.get(remove_local_url)
357 ba50648c Kostas Papadimitriou
        # 2 providers left
358 ba50648c Kostas Papadimitriou
        self.assertEqual(user.auth_providers.count(), 2)
359 ba50648c Kostas Papadimitriou
        r = client.get(remove_shibbo2_url)
360 ba50648c Kostas Papadimitriou
        # 1 provider left
361 f432088a Kostas Papadimitriou
        self.assertEqual(user.auth_providers.count(), 1)
362 ba50648c Kostas Papadimitriou
        # cannot remove last provider
363 ba50648c Kostas Papadimitriou
        r = client.get(remove_shibbo_url)
364 f432088a Kostas Papadimitriou
        self.assertEqual(r.status_code, 403)
365 f432088a Kostas Papadimitriou
        self.client.logout()
366 ba50648c Kostas Papadimitriou
367 ba50648c Kostas Papadimitriou
        # cannot login using local credentials (notice we use another client)
368 f432088a Kostas Papadimitriou
        post_data = {'password': 'password',
369 f432088a Kostas Papadimitriou
                     'username': 'kpap@grnet.gr'}
370 f432088a Kostas Papadimitriou
        r = self.client.post('/im/local', post_data, follow=True)
371 f432088a Kostas Papadimitriou
        self.assertFalse(r.context['request'].user.is_authenticated())
372 f432088a Kostas Papadimitriou
373 ba50648c Kostas Papadimitriou
        # we can reenable the local provider by setting a password
374 f432088a Kostas Papadimitriou
        r = client.get("/im/password_change", follow=True)
375 f432088a Kostas Papadimitriou
        r = client.post("/im/password_change", {'new_password1':'111',
376 f432088a Kostas Papadimitriou
                                                'new_password2': '111'},
377 f432088a Kostas Papadimitriou
                        follow=True)
378 f432088a Kostas Papadimitriou
        user = r.context['request'].user
379 f432088a Kostas Papadimitriou
        self.assertTrue(user.has_auth_provider('local'))
380 f432088a Kostas Papadimitriou
        self.assertTrue(user.has_auth_provider('shibboleth'))
381 f432088a Kostas Papadimitriou
        self.assertTrue(user.check_password('111'))
382 f432088a Kostas Papadimitriou
        self.assertTrue(user.has_usable_password())
383 f432088a Kostas Papadimitriou
        self.client.logout()
384 ba50648c Kostas Papadimitriou
385 ba50648c Kostas Papadimitriou
        # now we can login
386 f432088a Kostas Papadimitriou
        post_data = {'password': '111',
387 f432088a Kostas Papadimitriou
                     'username': 'kpap@grnet.gr'}
388 f432088a Kostas Papadimitriou
        r = self.client.post('/im/local', post_data, follow=True)
389 f432088a Kostas Papadimitriou
        self.assertTrue(r.context['request'].user.is_authenticated())
390 f432088a Kostas Papadimitriou
391 ba50648c Kostas Papadimitriou
        client.reset_tokens()
392 f432088a Kostas Papadimitriou
393 ba50648c Kostas Papadimitriou
        # we cannot take over another shibboleth identifier
394 f432088a Kostas Papadimitriou
        user2 = get_local_user('another@grnet.gr')
395 f432088a Kostas Papadimitriou
        user2.add_auth_provider('shibboleth', identifier='existingeppn')
396 ba50648c Kostas Papadimitriou
        # login
397 e5966bd9 Kostas Papadimitriou
        client.set_tokens(mail="kpap@shibboleth.gr", eppn="kpapeppn",
398 e5966bd9 Kostas Papadimitriou
                          cn="Kostas Papadimitriou")
399 f432088a Kostas Papadimitriou
        r = client.get("/im/login/shibboleth?", follow=True)
400 ba50648c Kostas Papadimitriou
        # try to assign existing shibboleth identifier of another user
401 e5966bd9 Kostas Papadimitriou
        client.set_tokens(mail="kpap_second@shibboleth.gr", eppn="existingeppn",
402 e5966bd9 Kostas Papadimitriou
                          cn="Kostas Papadimitriou")
403 f432088a Kostas Papadimitriou
        r = client.get("/im/login/shibboleth?", follow=True)
404 e5966bd9 Kostas Papadimitriou
        self.assertContains(r, messages.AUTH_PROVIDER_ADD_FAILED)
405 e5966bd9 Kostas Papadimitriou
        self.assertContains(r, messages.AUTH_PROVIDER_ADD_EXISTS)
406 2e90e3ec Kostas Papadimitriou
407 d2633501 Kostas Papadimitriou
408 d2633501 Kostas Papadimitriou
class LocalUserTests(TestCase):
409 d2633501 Kostas Papadimitriou
410 d2633501 Kostas Papadimitriou
    fixtures = ['groups']
411 d2633501 Kostas Papadimitriou
412 2e90e3ec Kostas Papadimitriou
    def setUp(self):
413 1af308f1 Kostas Papadimitriou
        kind = GroupKind.objects.create(name="default")
414 1af308f1 Kostas Papadimitriou
        AstakosGroup.objects.create(name="default", kind=kind)
415 2e90e3ec Kostas Papadimitriou
        from django.conf import settings
416 2e90e3ec Kostas Papadimitriou
        settings.ADMINS = (('admin', 'support@cloud.grnet.gr'),)
417 2e90e3ec Kostas Papadimitriou
        settings.SERVER_EMAIL = 'no-reply@grnet.gr'
418 2e90e3ec Kostas Papadimitriou
419 7233d542 Kostas Papadimitriou
    def test_no_moderation(self):
420 ba50648c Kostas Papadimitriou
        # disable moderation
421 7233d542 Kostas Papadimitriou
        astakos_settings.MODERATION_ENABLED = False
422 ba50648c Kostas Papadimitriou
423 ba50648c Kostas Papadimitriou
        # create a new user
424 7233d542 Kostas Papadimitriou
        r = self.client.get("/im/signup")
425 7233d542 Kostas Papadimitriou
        self.assertEqual(r.status_code, 200)
426 7233d542 Kostas Papadimitriou
        data = {'email':'kpap@grnet.gr', 'password1':'password',
427 7233d542 Kostas Papadimitriou
                'password2':'password', 'first_name': 'Kostas',
428 7233d542 Kostas Papadimitriou
                'last_name': 'Mitroglou', 'provider': 'local'}
429 7233d542 Kostas Papadimitriou
        r = self.client.post("/im/signup", data)
430 ba50648c Kostas Papadimitriou
431 ba50648c Kostas Papadimitriou
        # user created
432 7233d542 Kostas Papadimitriou
        self.assertEqual(AstakosUser.objects.count(), 1)
433 7233d542 Kostas Papadimitriou
        user = AstakosUser.objects.get(username="kpap@grnet.gr",
434 7233d542 Kostas Papadimitriou
                                       email="kpap@grnet.gr")
435 7233d542 Kostas Papadimitriou
        self.assertEqual(user.username, 'kpap@grnet.gr')
436 7233d542 Kostas Papadimitriou
        self.assertEqual(user.has_auth_provider('local'), True)
437 7233d542 Kostas Papadimitriou
        self.assertFalse(user.is_active)
438 7233d542 Kostas Papadimitriou
439 ba50648c Kostas Papadimitriou
        # user (but not admin) gets notified
440 7233d542 Kostas Papadimitriou
        self.assertEqual(len(get_mailbox('support@cloud.grnet.gr')), 0)
441 7233d542 Kostas Papadimitriou
        self.assertEqual(len(get_mailbox('kpap@grnet.gr')), 1)
442 7233d542 Kostas Papadimitriou
        astakos_settings.MODERATION_ENABLED = True
443 d2633501 Kostas Papadimitriou
444 e5966bd9 Kostas Papadimitriou
    def test_email_case(self):
445 e5966bd9 Kostas Papadimitriou
        data = {
446 e5966bd9 Kostas Papadimitriou
          'email': 'kPap@grnet.gr',
447 e5966bd9 Kostas Papadimitriou
          'password1': '1234',
448 e5966bd9 Kostas Papadimitriou
          'password2': '1234'
449 e5966bd9 Kostas Papadimitriou
        }
450 e5966bd9 Kostas Papadimitriou
451 e5966bd9 Kostas Papadimitriou
        form = forms.LocalUserCreationForm(data)
452 e5966bd9 Kostas Papadimitriou
        self.assertTrue(form.is_valid())
453 e5966bd9 Kostas Papadimitriou
        user = form.save()
454 e5966bd9 Kostas Papadimitriou
        form.store_user(user, {})
455 e5966bd9 Kostas Papadimitriou
456 e5966bd9 Kostas Papadimitriou
        u = AstakosUser.objects.get(pk=1)
457 e5966bd9 Kostas Papadimitriou
        self.assertEqual(u.email, 'kPap@grnet.gr')
458 e5966bd9 Kostas Papadimitriou
        self.assertEqual(u.username, 'kpap@grnet.gr')
459 e5966bd9 Kostas Papadimitriou
        u.is_active = True
460 e5966bd9 Kostas Papadimitriou
        u.email_verified = True
461 e5966bd9 Kostas Papadimitriou
        u.save()
462 e5966bd9 Kostas Papadimitriou
463 e5966bd9 Kostas Papadimitriou
        data = {'username': 'kpap@grnet.gr', 'password': '1234'}
464 e5966bd9 Kostas Papadimitriou
        login = forms.LoginForm(data=data)
465 e5966bd9 Kostas Papadimitriou
        self.assertTrue(login.is_valid())
466 e5966bd9 Kostas Papadimitriou
467 e5966bd9 Kostas Papadimitriou
        data = {'username': 'KpaP@grnet.gr', 'password': '1234'}
468 e5966bd9 Kostas Papadimitriou
        login = forms.LoginForm(data=data)
469 e5966bd9 Kostas Papadimitriou
        self.assertTrue(login.is_valid())
470 e5966bd9 Kostas Papadimitriou
471 e5966bd9 Kostas Papadimitriou
        data = {
472 e5966bd9 Kostas Papadimitriou
          'email': 'kpap@grnet.gr',
473 e5966bd9 Kostas Papadimitriou
          'password1': '1234',
474 e5966bd9 Kostas Papadimitriou
          'password2': '1234'
475 e5966bd9 Kostas Papadimitriou
        }
476 e5966bd9 Kostas Papadimitriou
        form = forms.LocalUserCreationForm(data)
477 e5966bd9 Kostas Papadimitriou
        self.assertFalse(form.is_valid())
478 e5966bd9 Kostas Papadimitriou
479 d2633501 Kostas Papadimitriou
    def test_local_provider(self):
480 ba50648c Kostas Papadimitriou
        # enable moderation
481 7233d542 Kostas Papadimitriou
        astakos_settings.MODERATION_ENABLED = True
482 ba50648c Kostas Papadimitriou
483 ba50648c Kostas Papadimitriou
        # create a user
484 d2633501 Kostas Papadimitriou
        r = self.client.get("/im/signup")
485 d2633501 Kostas Papadimitriou
        self.assertEqual(r.status_code, 200)
486 d2633501 Kostas Papadimitriou
        data = {'email':'kpap@grnet.gr', 'password1':'password',
487 d2633501 Kostas Papadimitriou
                'password2':'password', 'first_name': 'Kostas',
488 d2633501 Kostas Papadimitriou
                'last_name': 'Mitroglou', 'provider': 'local'}
489 d2633501 Kostas Papadimitriou
        r = self.client.post("/im/signup", data)
490 ba50648c Kostas Papadimitriou
491 ba50648c Kostas Papadimitriou
        # user created
492 d2633501 Kostas Papadimitriou
        self.assertEqual(AstakosUser.objects.count(), 1)
493 d2633501 Kostas Papadimitriou
        user = AstakosUser.objects.get(username="kpap@grnet.gr",
494 d2633501 Kostas Papadimitriou
                                       email="kpap@grnet.gr")
495 d2633501 Kostas Papadimitriou
        self.assertEqual(user.username, 'kpap@grnet.gr')
496 d2633501 Kostas Papadimitriou
        self.assertEqual(user.has_auth_provider('local'), True)
497 ba50648c Kostas Papadimitriou
        self.assertFalse(user.is_active) # not activated
498 ba50648c Kostas Papadimitriou
        self.assertFalse(user.email_verified) # not verified
499 ba50648c Kostas Papadimitriou
        self.assertFalse(user.activation_sent) # activation automatically sent
500 d2633501 Kostas Papadimitriou
501 ba50648c Kostas Papadimitriou
        # admin gets notified and activates the user from the command line
502 d2633501 Kostas Papadimitriou
        self.assertEqual(len(get_mailbox('support@cloud.grnet.gr')), 1)
503 31fdafa8 Kostas Papadimitriou
        r = self.client.post('/im/local', {'username': 'kpap@grnet.gr',
504 31fdafa8 Kostas Papadimitriou
                                                 'password': 'password'})
505 31fdafa8 Kostas Papadimitriou
        self.assertContains(r, messages.ACCOUNT_PENDING_MODERATION)
506 d2633501 Kostas Papadimitriou
        functions.send_activation(user)
507 d2633501 Kostas Papadimitriou
508 ba50648c Kostas Papadimitriou
        # user activation fields updated and user gets notified via email
509 d2633501 Kostas Papadimitriou
        user = AstakosUser.objects.get(pk=user.pk)
510 d2633501 Kostas Papadimitriou
        self.assertTrue(user.activation_sent)
511 d2633501 Kostas Papadimitriou
        self.assertFalse(user.email_verified)
512 d2633501 Kostas Papadimitriou
        self.assertEqual(len(get_mailbox('kpap@grnet.gr')), 1)
513 d2633501 Kostas Papadimitriou
514 d2633501 Kostas Papadimitriou
        # user forgot she got registered and tries to submit registration
515 d2633501 Kostas Papadimitriou
        # form. Notice the upper case in email
516 d2633501 Kostas Papadimitriou
        data = {'email':'KPAP@grnet.gr', 'password1':'password',
517 d2633501 Kostas Papadimitriou
                'password2':'password', 'first_name': 'Kostas',
518 d2633501 Kostas Papadimitriou
                'last_name': 'Mitroglou', 'provider': 'local'}
519 d2633501 Kostas Papadimitriou
        r = self.client.post("/im/signup", data)
520 2e90e3ec Kostas Papadimitriou
        self.assertContains(r, messages.EMAIL_USED)
521 d2633501 Kostas Papadimitriou
522 d2633501 Kostas Papadimitriou
        # hmmm, email exists; lets get the password
523 d2633501 Kostas Papadimitriou
        r = self.client.get('/im/local/password_reset')
524 d2633501 Kostas Papadimitriou
        self.assertEqual(r.status_code, 200)
525 d2633501 Kostas Papadimitriou
        r = self.client.post('/im/local/password_reset', {'email':
526 d2633501 Kostas Papadimitriou
                                                          'kpap@grnet.gr'})
527 d2633501 Kostas Papadimitriou
        # she can't because account is not active yet
528 d2633501 Kostas Papadimitriou
        self.assertContains(r, "doesn't have an associated user account")
529 d2633501 Kostas Papadimitriou
530 d2633501 Kostas Papadimitriou
        # moderation is enabled so no automatic activation can be send
531 d2633501 Kostas Papadimitriou
        r = self.client.get('/im/send/activation/%d' % user.pk)
532 d2633501 Kostas Papadimitriou
        self.assertEqual(r.status_code, 403)
533 d2633501 Kostas Papadimitriou
        self.assertEqual(len(get_mailbox('kpap@grnet.gr')), 1)
534 ba50648c Kostas Papadimitriou
535 d2633501 Kostas Papadimitriou
        # also she cannot login
536 d2633501 Kostas Papadimitriou
        r = self.client.post('/im/local', {'username': 'kpap@grnet.gr',
537 d2633501 Kostas Papadimitriou
                                                 'password': 'password'})
538 31fdafa8 Kostas Papadimitriou
        self.assertContains(r, messages.ACCOUNT_PENDING_ACTIVATION_HELP)
539 31fdafa8 Kostas Papadimitriou
        self.assertContains(r, messages.ACCOUNT_PENDING_ACTIVATION)
540 d2633501 Kostas Papadimitriou
        self.assertNotContains(r, 'Resend activation')
541 d2633501 Kostas Papadimitriou
        self.assertFalse(r.context['request'].user.is_authenticated())
542 d2633501 Kostas Papadimitriou
        self.assertFalse('_pithos2_a' in self.client.cookies)
543 d2633501 Kostas Papadimitriou
544 ba50648c Kostas Papadimitriou
        # same with disabled moderation
545 d2633501 Kostas Papadimitriou
        astakos_settings.MODERATION_ENABLED = False
546 d2633501 Kostas Papadimitriou
        r = self.client.post('/im/local/password_reset', {'email':
547 d2633501 Kostas Papadimitriou
                                                          'kpap@grnet.gr'})
548 d2633501 Kostas Papadimitriou
        self.assertContains(r, "doesn't have an associated user account")
549 d2633501 Kostas Papadimitriou
        r = self.client.post('/im/local', {'username': 'kpap@grnet.gr',
550 d2633501 Kostas Papadimitriou
                                                 'password': 'password'})
551 31fdafa8 Kostas Papadimitriou
        self.assertContains(r, messages.ACCOUNT_PENDING_ACTIVATION)
552 d2633501 Kostas Papadimitriou
        self.assertContains(r, 'Resend activation')
553 d2633501 Kostas Papadimitriou
        self.assertFalse(r.context['request'].user.is_authenticated())
554 d2633501 Kostas Papadimitriou
        self.assertFalse('_pithos2_a' in self.client.cookies)
555 ba50648c Kostas Papadimitriou
556 d2633501 Kostas Papadimitriou
        # user sees the message and resends activation
557 d2633501 Kostas Papadimitriou
        r = self.client.get('/im/send/activation/%d' % user.pk)
558 d2633501 Kostas Papadimitriou
        self.assertEqual(len(get_mailbox('kpap@grnet.gr')), 2)
559 d2633501 Kostas Papadimitriou
560 d2633501 Kostas Papadimitriou
        # switch back moderation setting
561 d2633501 Kostas Papadimitriou
        astakos_settings.MODERATION_ENABLED = True
562 d2633501 Kostas Papadimitriou
        r = self.client.get(user.get_activation_url(), follow=True)
563 d2633501 Kostas Papadimitriou
        self.assertRedirects(r, "/im/profile")
564 d2633501 Kostas Papadimitriou
        self.assertContains(r, "kpap@grnet.gr")
565 d2633501 Kostas Papadimitriou
        self.assertEqual(len(get_mailbox('kpap@grnet.gr')), 3)
566 d2633501 Kostas Papadimitriou
567 d2633501 Kostas Papadimitriou
        user = AstakosUser.objects.get(pk=user.pk)
568 d2633501 Kostas Papadimitriou
        # user activated and logged in, token cookie set
569 d2633501 Kostas Papadimitriou
        self.assertTrue(r.context['request'].user.is_authenticated())
570 d2633501 Kostas Papadimitriou
        self.assertTrue('_pithos2_a' in self.client.cookies)
571 d2633501 Kostas Papadimitriou
        cookies = self.client.cookies
572 d2633501 Kostas Papadimitriou
        self.assertTrue(quote(user.auth_token) in cookies.get('_pithos2_a').value)
573 d2633501 Kostas Papadimitriou
        r = self.client.get('/im/logout', follow=True)
574 d2633501 Kostas Papadimitriou
        r = self.client.get('/im/')
575 d2633501 Kostas Papadimitriou
        # user logged out, token cookie removed
576 d2633501 Kostas Papadimitriou
        self.assertFalse(r.context['request'].user.is_authenticated())
577 d2633501 Kostas Papadimitriou
        self.assertFalse(self.client.cookies.get('_pithos2_a').value)
578 d2633501 Kostas Papadimitriou
        # https://docs.djangoproject.com/en/dev/topics/testing/#persistent-state
579 d2633501 Kostas Papadimitriou
        del self.client.cookies['_pithos2_a']
580 d2633501 Kostas Papadimitriou
581 d2633501 Kostas Papadimitriou
        # user can login
582 d2633501 Kostas Papadimitriou
        r = self.client.post('/im/local', {'username': 'kpap@grnet.gr',
583 d2633501 Kostas Papadimitriou
                                           'password': 'password'},
584 d2633501 Kostas Papadimitriou
                                          follow=True)
585 d2633501 Kostas Papadimitriou
        self.assertTrue(r.context['request'].user.is_authenticated())
586 d2633501 Kostas Papadimitriou
        self.assertTrue('_pithos2_a' in self.client.cookies)
587 d2633501 Kostas Papadimitriou
        cookies = self.client.cookies
588 d2633501 Kostas Papadimitriou
        self.assertTrue(quote(user.auth_token) in cookies.get('_pithos2_a').value)
589 d2633501 Kostas Papadimitriou
        self.client.get('/im/logout', follow=True)
590 d2633501 Kostas Papadimitriou
591 d2633501 Kostas Papadimitriou
        # user forgot password
592 d2633501 Kostas Papadimitriou
        old_pass = user.password
593 d2633501 Kostas Papadimitriou
        r = self.client.get('/im/local/password_reset')
594 d2633501 Kostas Papadimitriou
        self.assertEqual(r.status_code, 200)
595 d2633501 Kostas Papadimitriou
        r = self.client.post('/im/local/password_reset', {'email':
596 d2633501 Kostas Papadimitriou
                                                          'kpap@grnet.gr'})
597 d2633501 Kostas Papadimitriou
        self.assertEqual(r.status_code, 302)
598 d2633501 Kostas Papadimitriou
        # email sent
599 d2633501 Kostas Papadimitriou
        self.assertEqual(len(get_mailbox('kpap@grnet.gr')), 4)
600 d2633501 Kostas Papadimitriou
601 d2633501 Kostas Papadimitriou
        # user visits change password link
602 d2633501 Kostas Papadimitriou
        r = self.client.get(user.get_password_reset_url())
603 d2633501 Kostas Papadimitriou
        r = self.client.post(user.get_password_reset_url(),
604 d2633501 Kostas Papadimitriou
                            {'new_password1':'newpass',
605 d2633501 Kostas Papadimitriou
                             'new_password2':'newpass'})
606 d2633501 Kostas Papadimitriou
607 d2633501 Kostas Papadimitriou
        user = AstakosUser.objects.get(pk=user.pk)
608 d2633501 Kostas Papadimitriou
        self.assertNotEqual(old_pass, user.password)
609 d2633501 Kostas Papadimitriou
610 d2633501 Kostas Papadimitriou
        # old pass is not usable
611 d2633501 Kostas Papadimitriou
        r = self.client.post('/im/local', {'username': 'kpap@grnet.gr',
612 d2633501 Kostas Papadimitriou
                                           'password': 'password'})
613 31fdafa8 Kostas Papadimitriou
        self.assertContains(r, 'Please enter a correct username and password')
614 d2633501 Kostas Papadimitriou
        r = self.client.post('/im/local', {'username': 'kpap@grnet.gr',
615 d2633501 Kostas Papadimitriou
                                           'password': 'newpass'},
616 d2633501 Kostas Papadimitriou
                                           follow=True)
617 d2633501 Kostas Papadimitriou
        self.assertTrue(r.context['request'].user.is_authenticated())
618 d2633501 Kostas Papadimitriou
        self.client.logout()
619 d2633501 Kostas Papadimitriou
620 d2633501 Kostas Papadimitriou
        # tests of special local backends
621 d2633501 Kostas Papadimitriou
        user = AstakosUser.objects.get(pk=user.pk)
622 d2633501 Kostas Papadimitriou
        user.auth_providers.filter(module='local').update(auth_backend='ldap')
623 d2633501 Kostas Papadimitriou
        user.save()
624 d2633501 Kostas Papadimitriou
625 d2633501 Kostas Papadimitriou
        # non astakos local backends do not support password reset
626 d2633501 Kostas Papadimitriou
        r = self.client.get('/im/local/password_reset')
627 d2633501 Kostas Papadimitriou
        self.assertEqual(r.status_code, 200)
628 d2633501 Kostas Papadimitriou
        r = self.client.post('/im/local/password_reset', {'email':
629 d2633501 Kostas Papadimitriou
                                                          'kpap@grnet.gr'})
630 d2633501 Kostas Papadimitriou
        # she can't because account is not active yet
631 d2633501 Kostas Papadimitriou
        self.assertContains(r, "Password change for this account is not"
632 d2633501 Kostas Papadimitriou
                                " supported")
633 d2633501 Kostas Papadimitriou
634 34a76cdb Kostas Papadimitriou
class UserActionsTests(TestCase):
635 34a76cdb Kostas Papadimitriou
636 34a76cdb Kostas Papadimitriou
    def setUp(self):
637 34a76cdb Kostas Papadimitriou
        kind = GroupKind.objects.create(name="default")
638 34a76cdb Kostas Papadimitriou
        AstakosGroup.objects.create(name="default", kind=kind)
639 34a76cdb Kostas Papadimitriou
640 34a76cdb Kostas Papadimitriou
    def test_email_change(self):
641 34a76cdb Kostas Papadimitriou
        # to test existing email validation
642 34a76cdb Kostas Papadimitriou
        existing_user = get_local_user('existing@grnet.gr')
643 34a76cdb Kostas Papadimitriou
644 34a76cdb Kostas Papadimitriou
        # local user
645 34a76cdb Kostas Papadimitriou
        user = get_local_user('kpap@grnet.gr')
646 34a76cdb Kostas Papadimitriou
647 34a76cdb Kostas Papadimitriou
        # login as kpap
648 34a76cdb Kostas Papadimitriou
        self.client.login(username='kpap@grnet.gr', password='password')
649 34a76cdb Kostas Papadimitriou
        r = self.client.get('/im/profile', follow=True)
650 34a76cdb Kostas Papadimitriou
        user = r.context['request'].user
651 34a76cdb Kostas Papadimitriou
        self.assertTrue(user.is_authenticated())
652 34a76cdb Kostas Papadimitriou
653 34a76cdb Kostas Papadimitriou
        # change email is enabled
654 34a76cdb Kostas Papadimitriou
        r = self.client.get('/im/email_change')
655 34a76cdb Kostas Papadimitriou
        self.assertEqual(r.status_code, 200)
656 34a76cdb Kostas Papadimitriou
        self.assertFalse(user.email_change_is_pending())
657 34a76cdb Kostas Papadimitriou
658 34a76cdb Kostas Papadimitriou
        # request email change to an existing email fails
659 34a76cdb Kostas Papadimitriou
        data = {'new_email_address': 'existing@grnet.gr'}
660 34a76cdb Kostas Papadimitriou
        r = self.client.post('/im/email_change', data)
661 34a76cdb Kostas Papadimitriou
        self.assertContains(r, messages.EMAIL_USED)
662 34a76cdb Kostas Papadimitriou
663 34a76cdb Kostas Papadimitriou
        # proper email change
664 34a76cdb Kostas Papadimitriou
        data = {'new_email_address': 'kpap@gmail.com'}
665 34a76cdb Kostas Papadimitriou
        r = self.client.post('/im/email_change', data, follow=True)
666 34a76cdb Kostas Papadimitriou
        self.assertRedirects(r, '/im/profile')
667 34a76cdb Kostas Papadimitriou
        self.assertContains(r, messages.EMAIL_CHANGE_REGISTERED)
668 34a76cdb Kostas Papadimitriou
        change1 = EmailChange.objects.get()
669 34a76cdb Kostas Papadimitriou
670 34a76cdb Kostas Papadimitriou
        # user sees a warning
671 34a76cdb Kostas Papadimitriou
        r = self.client.get('/im/email_change')
672 34a76cdb Kostas Papadimitriou
        self.assertEqual(r.status_code, 200)
673 34a76cdb Kostas Papadimitriou
        self.assertContains(r, messages.PENDING_EMAIL_CHANGE_REQUEST)
674 34a76cdb Kostas Papadimitriou
        self.assertTrue(user.email_change_is_pending())
675 34a76cdb Kostas Papadimitriou
676 34a76cdb Kostas Papadimitriou
        # link was sent
677 34a76cdb Kostas Papadimitriou
        self.assertEqual(len(get_mailbox('kpap@grnet.gr')), 0)
678 34a76cdb Kostas Papadimitriou
        self.assertEqual(len(get_mailbox('kpap@gmail.com')), 1)
679 34a76cdb Kostas Papadimitriou
680 34a76cdb Kostas Papadimitriou
        # proper email change
681 34a76cdb Kostas Papadimitriou
        data = {'new_email_address': 'kpap@yahoo.com'}
682 34a76cdb Kostas Papadimitriou
        r = self.client.post('/im/email_change', data, follow=True)
683 34a76cdb Kostas Papadimitriou
        self.assertRedirects(r, '/im/profile')
684 34a76cdb Kostas Papadimitriou
        self.assertContains(r, messages.EMAIL_CHANGE_REGISTERED)
685 34a76cdb Kostas Papadimitriou
        self.assertEqual(len(get_mailbox('kpap@grnet.gr')), 0)
686 34a76cdb Kostas Papadimitriou
        self.assertEqual(len(get_mailbox('kpap@yahoo.com')), 1)
687 34a76cdb Kostas Papadimitriou
        change2 = EmailChange.objects.get()
688 34a76cdb Kostas Papadimitriou
689 34a76cdb Kostas Papadimitriou
        r = self.client.get(change1.get_url())
690 34a76cdb Kostas Papadimitriou
        self.assertEquals(r.status_code, 302)
691 34a76cdb Kostas Papadimitriou
        self.client.logout()
692 34a76cdb Kostas Papadimitriou
693 34a76cdb Kostas Papadimitriou
        r = self.client.post('/im/local?next=' + change2.get_url(),
694 34a76cdb Kostas Papadimitriou
                             {'username': 'kpap@grnet.gr',
695 34a76cdb Kostas Papadimitriou
                              'password': 'password',
696 34a76cdb Kostas Papadimitriou
                              'next': change2.get_url()},
697 34a76cdb Kostas Papadimitriou
                             follow=True)
698 34a76cdb Kostas Papadimitriou
        self.assertRedirects(r, '/im/profile')
699 34a76cdb Kostas Papadimitriou
        user = r.context['request'].user
700 34a76cdb Kostas Papadimitriou
        self.assertEquals(user.email, 'kpap@yahoo.com')
701 34a76cdb Kostas Papadimitriou
        self.assertEquals(user.username, 'kpap@yahoo.com')
702 34a76cdb Kostas Papadimitriou
703 34a76cdb Kostas Papadimitriou
704 34a76cdb Kostas Papadimitriou
        self.client.logout()
705 34a76cdb Kostas Papadimitriou
        r = self.client.post('/im/local?next=' + change2.get_url(),
706 34a76cdb Kostas Papadimitriou
                             {'username': 'kpap@grnet.gr',
707 34a76cdb Kostas Papadimitriou
                              'password': 'password',
708 34a76cdb Kostas Papadimitriou
                              'next': change2.get_url()},
709 34a76cdb Kostas Papadimitriou
                             follow=True)
710 34a76cdb Kostas Papadimitriou
        self.assertContains(r, "Please enter a correct username and password")
711 34a76cdb Kostas Papadimitriou
        self.assertEqual(user.emailchanges.count(), 0)