Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / tests.py @ 6a6ceeb0

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