|
1 |
import urlparse
|
|
2 |
import base64
|
|
3 |
import json
|
|
4 |
import urllib
|
|
5 |
|
|
6 |
from quixote import cleanup, get_session_manager
|
|
7 |
|
|
8 |
from utilities import get_app, create_temporary_pub
|
|
9 |
import mock
|
|
10 |
|
|
11 |
PROFILE = {
|
|
12 |
'fields': [
|
|
13 |
{
|
|
14 |
'kind': 'string',
|
|
15 |
'description': '',
|
|
16 |
'required': True,
|
|
17 |
'user_visible': True,
|
|
18 |
'label': u'Prenoms',
|
|
19 |
'disabled': False,
|
|
20 |
'user_editable': True,
|
|
21 |
'asked_on_registration': True,
|
|
22 |
'name': 'prenoms'
|
|
23 |
},
|
|
24 |
{
|
|
25 |
'kind': 'string',
|
|
26 |
'description': '',
|
|
27 |
'required': True,
|
|
28 |
'user_visible': True,
|
|
29 |
'label': 'Nom',
|
|
30 |
'disabled': False,
|
|
31 |
'user_editable': True,
|
|
32 |
'asked_on_registration': True,
|
|
33 |
'name': 'nom'
|
|
34 |
},
|
|
35 |
{
|
|
36 |
'kind': 'string',
|
|
37 |
'description': '',
|
|
38 |
'required': True,
|
|
39 |
'user_visible': True,
|
|
40 |
'label': 'Email',
|
|
41 |
'disabled': False,
|
|
42 |
'user_editable': True,
|
|
43 |
'asked_on_registration': True,
|
|
44 |
'name': 'email'
|
|
45 |
},
|
|
46 |
]
|
|
47 |
}
|
|
48 |
|
|
49 |
|
|
50 |
def base64url_encode(v):
|
|
51 |
return base64.urlsafe_b64encode(v).strip('=')
|
|
52 |
|
|
53 |
|
|
54 |
def setup_module(module):
|
|
55 |
cleanup()
|
|
56 |
global pub
|
|
57 |
pub = create_temporary_pub()
|
|
58 |
|
|
59 |
|
|
60 |
def setup_profile_environement(pub):
|
|
61 |
if not pub.cfg:
|
|
62 |
pub.cfg = {}
|
|
63 |
# create some roles
|
|
64 |
from wcs.ctl.check_hobos import CmdCheckHobos
|
|
65 |
|
|
66 |
# setup an hobo profile
|
|
67 |
CmdCheckHobos().update_profile(PROFILE, pub)
|
|
68 |
pub.cfg['users']['field_name'] = ['_prenoms', '_nom']
|
|
69 |
pub.user_class.wipe()
|
|
70 |
pub.write_cfg()
|
|
71 |
|
|
72 |
FC_CONFIG = {
|
|
73 |
'client_id': '123',
|
|
74 |
'client_secret': 'xyz',
|
|
75 |
'platform': 'dev-particulier',
|
|
76 |
'scopes': 'identite_pivot',
|
|
77 |
'user_field_mappings': [
|
|
78 |
{
|
|
79 |
'field_varname': 'prenoms',
|
|
80 |
'value': '[given_name]',
|
|
81 |
'verified': 'always',
|
|
82 |
},
|
|
83 |
{
|
|
84 |
'field_varname': 'nom',
|
|
85 |
'value': '[family_name]',
|
|
86 |
'verified': 'always',
|
|
87 |
},
|
|
88 |
{
|
|
89 |
'field_varname': 'email',
|
|
90 |
'value': '[email]',
|
|
91 |
'verified': 'always',
|
|
92 |
},
|
|
93 |
]
|
|
94 |
|
|
95 |
}
|
|
96 |
|
|
97 |
|
|
98 |
def setup_fc_environment(pub):
|
|
99 |
if not pub.cfg:
|
|
100 |
pub.cfg = {}
|
|
101 |
pub.cfg['identification'] = {
|
|
102 |
'methods': ['fc'],
|
|
103 |
}
|
|
104 |
pub.cfg['fc'] = FC_CONFIG
|
|
105 |
pub.user_class.wipe()
|
|
106 |
pub.write_cfg()
|
|
107 |
|
|
108 |
|
|
109 |
def test_fc_login_page():
|
|
110 |
setup_profile_environement(pub)
|
|
111 |
setup_fc_environment(pub)
|
|
112 |
app = get_app(pub)
|
|
113 |
resp = app.get('/')
|
|
114 |
resp = app.get('/login/')
|
|
115 |
assert resp.status_int == 302
|
|
116 |
assert resp.location.startswith('https://fcp.integ01.dev-franceconnect.fr/api/v1/authorize')
|
|
117 |
qs = urlparse.parse_qs(resp.location.split('?')[1])
|
|
118 |
nonce = qs['nonce'][0]
|
|
119 |
state = qs['state'][0]
|
|
120 |
|
|
121 |
id_token = {
|
|
122 |
'nonce': nonce,
|
|
123 |
}
|
|
124 |
token_result = {
|
|
125 |
'access_token': 'abcd',
|
|
126 |
'id_token': '.%s.' % base64url_encode(json.dumps(id_token)),
|
|
127 |
}
|
|
128 |
user_info_result = {
|
|
129 |
'sub': 'ymca',
|
|
130 |
'given_name': 'John',
|
|
131 |
'family_name': 'Doe',
|
|
132 |
'email': 'john.doe@example.com',
|
|
133 |
}
|
|
134 |
|
|
135 |
assert pub.user_class.count() == 0
|
|
136 |
with mock.patch('qommon.ident.franceconnect.http_post_request') as http_post_request, \
|
|
137 |
mock.patch('qommon.ident.franceconnect.http_get_page') as http_get_page:
|
|
138 |
http_post_request.return_value = (None, 200, json.dumps(token_result), None)
|
|
139 |
http_get_page.return_value = (None, 200, json.dumps(user_info_result), None)
|
|
140 |
resp = app.get('/ident/fc/callback?%s' % urllib.urlencode({
|
|
141 |
'code': '1234', 'state': state,
|
|
142 |
}))
|
|
143 |
assert pub.user_class.count() == 1
|
|
144 |
user = pub.user_class.select()[0]
|
|
145 |
assert user.form_data == {'_email': 'john.doe@example.com', '_nom': 'Doe', '_prenoms': 'John'}
|
|
146 |
assert set(user.verified_fields) == set(['_nom', '_prenoms', '_email'])
|
|
147 |
assert user.email == 'john.doe@example.com'
|
|
148 |
assert user.name_identifiers == ['ymca']
|
|
149 |
assert user.name == 'John Doe'
|
|
150 |
|
|
151 |
# Verify we are logged in
|
|
152 |
session_id = app.cookies.values()[0].strip('"')
|
|
153 |
session = get_session_manager().session_class.get(session_id)
|
|
154 |
assert session.user == user.id
|
|
155 |
assert session.extra_variables['fc_user_given_name'] == 'John'
|
|
156 |
assert session.extra_variables['fc_user_family_name'] == 'Doe'
|
|
157 |
assert session.extra_variables['fc_user_email'] == 'john.doe@example.com'
|
|
158 |
assert session.extra_variables['fc_user_sub'] == 'ymca'
|
|
159 |
|
|
160 |
# Login existing user
|
|
161 |
resp = app.get('/logout')
|
|
162 |
resp = app.get('/login/')
|
|
163 |
with mock.patch('qommon.ident.franceconnect.http_post_request') as http_post_request, \
|
|
164 |
mock.patch('qommon.ident.franceconnect.http_get_page') as http_get_page:
|
|
165 |
http_post_request.return_value = (None, 200, json.dumps(token_result), None)
|
|
166 |
http_get_page.return_value = (None, 200, json.dumps(user_info_result), None)
|
|
167 |
resp = app.get('/ident/fc/callback?%s' % urllib.urlencode({
|
|
168 |
'code': '1234', 'state': state,
|
|
169 |
}))
|
|
170 |
new_session_id = app.cookies.values()[0].strip('"')
|
|
171 |
assert session_id != new_session_id, 'no new session created'
|
|
172 |
session = get_session_manager().session_class.get(new_session_id)
|
|
173 |
assert pub.user_class.count() == 1, 'existing user has not been used'
|
|
174 |
|
|
175 |
|
|
176 |
def test_fc_settings():
|
|
177 |
setup_profile_environement(pub)
|
|
178 |
app = get_app(pub)
|
|
179 |
resp = app.get('/backoffice/settings/identification/')
|
|
180 |
resp.forms[0]['methods$elementfc'].checked = True
|
|
181 |
resp = resp.forms[0].submit().follow()
|
|
182 |
|
|
183 |
assert 'FranceConnect' in resp.body
|
|
184 |
resp = resp.click('FranceConnect')
|
|
185 |
resp = resp.forms[0].submit('user_field_mappings$add_element')
|
|
186 |
resp = resp.forms[0].submit('user_field_mappings$add_element')
|
|
187 |
resp.forms[0]['client_id'].value = '123'
|
|
188 |
resp.forms[0]['client_secret'].value = 'xyz'
|
|
189 |
resp.forms[0]['platform'].value = 'Development citizens'
|
|
190 |
resp.forms[0]['scopes'].value = 'identite_pivot'
|
|
191 |
|
|
192 |
resp.forms[0]['user_field_mappings$element0$field_varname'] = 'prenoms'
|
|
193 |
resp.forms[0]['user_field_mappings$element0$value'] = '[given_name]'
|
|
194 |
resp.forms[0]['user_field_mappings$element0$verified'] = 'Always'
|
|
195 |
|
|
196 |
resp.forms[0]['user_field_mappings$element1$field_varname'] = 'nom'
|
|
197 |
resp.forms[0]['user_field_mappings$element1$value'] = '[family_name]'
|
|
198 |
resp.forms[0]['user_field_mappings$element1$verified'] = 'Always'
|
|
199 |
|
|
200 |
resp.forms[0]['user_field_mappings$element2$field_varname'] = 'email'
|
|
201 |
resp.forms[0]['user_field_mappings$element2$value'] = '[email]'
|
|
202 |
resp.forms[0]['user_field_mappings$element2$verified'] = 'Always'
|
|
203 |
|
|
204 |
resp = resp.forms[0].submit('submit').follow()
|
|
205 |
assert pub.cfg['fc'] == FC_CONFIG
|