~cjwatson/canonical-identity-provider/caveat-descriptions

« back to all changes in this revision

Viewing changes to src/identityprovider/tests/test_views_server.py

  • Committer: Tarmac
  • Author(s): Colin Watson
  • Date: 2016-04-11 12:23:52 UTC
  • mfrom: (1419.1.12 openid-macaroons)
  • Revision ID: tarmac-20160411122352-c8cujdn2k3kj5f4p
[r=nataliabidart,facundo] Add an OpenID extension to issue discharge macaroons.

Show diffs side-by-side

added added

removed removed

Lines of Context:
30
30
    Message,
31
31
)
32
32
from openid.yadis.constants import YADIS_HEADER_NAME
 
33
from pymacaroons import (
 
34
    Macaroon,
 
35
    Verifier,
 
36
)
 
37
from pymacaroons.exceptions import MacaroonUnmetCaveatException
33
38
from pyquery import PyQuery
34
39
 
35
40
import identityprovider.signed as signed
39
44
    AX_URI_EMAIL,
40
45
    AX_URI_FULL_NAME,
41
46
    AX_URI_LANGUAGE,
 
47
    MACAROON_NS,
42
48
)
43
49
from identityprovider.models import (
44
50
    Account,
220
226
        self.account.save()
221
227
        self._test_auto_auth(sreg=['fullname'])
222
228
 
223
 
    def _test_auto_auth(self, ax=None, sreg=None):
 
229
    def test_handle_user_response_auto_auth_discharge_macaroon(self):
 
230
        root_macaroon, macaroon_random_key = self.build_macaroon()
 
231
        # Add padding to force a POST after signing.  We don't know exactly
 
232
        # how long the serialized discharge macaroon will be yet, but it
 
233
        # will probably be at least 1024 bytes.
 
234
        self.account.displayname = 'a' * (OPENID1_URL_LIMIT - 1024)
 
235
        self.account.save()
 
236
        self._test_auto_auth(
 
237
            sreg=['fullname'],
 
238
            root_macaroon=root_macaroon, macaroon_key=macaroon_random_key)
 
239
 
 
240
    def _test_auto_auth(self, ax=None, sreg=None,
 
241
                        root_macaroon=None, macaroon_key=None):
224
242
        # update rp to auto authorize
225
243
        self.rpconfig.auto_authorize = True
226
244
        self.rpconfig.allowed_user_attribs = 'fullname,email,account_verified'
239
257
            ('openid.claimed_id', openid_identity_url),
240
258
            ('openid.identity', openid_identity_url),
241
259
        ]
242
 
        unexpected_fields = []
 
260
        unexpected_fields = [
 
261
            'openid.macaroon.discharge',
 
262
        ]
243
263
 
244
264
        self.client.login(username=self.email, password=DEFAULT_USER_PASSWORD)
245
265
        if ax:
269
289
            unexpected_fields += ['openid.sreg.%s' % k
270
290
                                  for k, v in expected_values.iteritems()
271
291
                                  if k not in sreg or v is None]
 
292
        if root_macaroon and macaroon_key:
 
293
            self.params.update({
 
294
                'openid.ns.macaroon': MACAROON_NS,
 
295
                'openid.macaroon.root': root_macaroon.serialize(),
 
296
            })
 
297
            unexpected_fields.remove('openid.macaroon.discharge')
272
298
        response = self.client.post(self.url, self.params)
273
299
        self.assertEqual('text/html', response['Content-type'].split(';')[0])
274
300
        dom = PyQuery(response.content.decode('utf-8'))
280
306
        self.assertEqual(len(forms), 1)
281
307
        for k, v in expected_fields:
282
308
            self.assertEqual(v, forms[0].fields[k])
 
309
        if root_macaroon and macaroon_key:
 
310
            self.assertIn('openid.macaroon.discharge', forms[0].fields)
 
311
            verifier = Verifier()
 
312
            # XXX cjwatson 2016-04-01: Can we reuse the normal auth checker
 
313
            # somehow?
 
314
            verifier.satisfy_general(lambda caveat: True)
 
315
            discharge_macaroon = Macaroon.deserialize(
 
316
                forms[0].fields['openid.macaroon.discharge'])
 
317
            self.assertRaises(
 
318
                MacaroonUnmetCaveatException, verifier.verify,
 
319
                root_macaroon, macaroon_key, [])
 
320
            self.assertTrue(verifier.verify(
 
321
                root_macaroon, macaroon_key, [discharge_macaroon]))
283
322
        for k in unexpected_fields:
284
323
            self.assertNotIn(k, forms[0].fields)
285
324
        for k in ('openid.assoc_handle', 'openid.sig'):
1022
1061
                                            label='Team membership',
1023
1062
                                            value=team_name)
1024
1063
 
 
1064
    def test_decide_issues_discharge_macaroon_with_auto_authorize(self):
 
1065
        # make sure rpconfig is set to auto authorize
 
1066
        OpenIDRPConfig.objects.create(
 
1067
            trust_root='http://localhost/', auto_authorize=True)
 
1068
        root_macaroon, macaroon_random_key = self.build_macaroon()
 
1069
        param_overrides = {
 
1070
            'openid.ns.macaroon': MACAROON_NS,
 
1071
            'openid.macaroon.root': root_macaroon.serialize(),
 
1072
        }
 
1073
        self._prepare_openid_token(param_overrides=param_overrides)
 
1074
        response = self.client.post(self.url)
 
1075
        query = self.get_query(response)
 
1076
        self.assertEqual(302, response.status_code)
 
1077
        self.assertEqual('id_res', query['openid.mode'])
 
1078
        self.assertIn('openid.macaroon.discharge', query)
 
1079
        verifier = Verifier()
 
1080
        # XXX cjwatson 2016-04-01: Can we reuse the normal auth checker
 
1081
        # somehow?
 
1082
        verifier.satisfy_general(lambda caveat: True)
 
1083
        discharge_macaroon = Macaroon.deserialize(
 
1084
            query['openid.macaroon.discharge'])
 
1085
        self.assertRaises(
 
1086
            MacaroonUnmetCaveatException, verifier.verify,
 
1087
            root_macaroon, macaroon_random_key, [])
 
1088
        self.assertTrue(verifier.verify(
 
1089
            root_macaroon, macaroon_random_key, [discharge_macaroon]))
 
1090
 
 
1091
    def test_state_of_checkboxes_and_data_formats_macaroon(self):
 
1092
        root_macaroon, _ = self.build_macaroon()
 
1093
        param_overrides = {
 
1094
            'openid.ns.macaroon': MACAROON_NS,
 
1095
            'openid.macaroon.root': root_macaroon.serialize(),
 
1096
        }
 
1097
        self._prepare_openid_token(param_overrides=param_overrides)
 
1098
        response = self.client.post(self.url)
 
1099
        dom = PyQuery(response.content)
 
1100
        # This field is checked regardless of whether a site is trusted.
 
1101
        self._test_optional_trusted_field(
 
1102
            dom, field='macaroon',
 
1103
            value='Service authorization for The store ;)')
 
1104
 
1025
1105
 
1026
1106
class DecideUserUnverifiedTestCase(DecideBaseTestCase):
1027
1107
 
1765
1845
class ApprovedDataTestCase(SSOBaseTestCase):
1766
1846
 
1767
1847
    def _get_openid_request(
1768
 
            self, with_sreg=True, with_ax=True, with_teams=True):
 
1848
            self, with_sreg=True, with_ax=True, with_teams=True,
 
1849
            with_macaroon=True):
1769
1850
        params = {
1770
1851
            'openid.mode': 'checkid_setup',
1771
1852
            'openid.trust_root': 'http://localhost/',
1782
1863
            params['openid.ax.required'] = 'email,fullname'
1783
1864
        if with_teams:
1784
1865
            params['openid.lp.query_membership'] = 'ubuntu-team'
 
1866
        if with_macaroon:
 
1867
            root_macaroon, _ = self.build_macaroon()
 
1868
            params['openid.ns.macaroon'] = MACAROON_NS
 
1869
            params['openid.macaroon.root'] = root_macaroon.serialize()
1785
1870
        provider_url = get_provider_url(request)
1786
1871
        openid_server = server._get_openid_server(provider_url)
1787
1872
        return openid_server.decodeRequest(params)
1806
1891
        result = server._get_approved_data(
1807
1892
            self._get_request_with_post_args(post_args),
1808
1893
            self._get_openid_request(with_sreg=True, with_ax=False,
1809
 
                                     with_teams=False))
 
1894
                                     with_teams=False, with_macaroon=False))
1810
1895
        self.assertEqual(sorted(result['user_attribs']['requested']),
1811
1896
                         ['email', 'fullname'])
1812
1897
        self.assertEqual(result['user_attribs']['approved'], ['email'])
1813
1898
        self.assertNotIn('teams', result)
 
1899
        self.assertNotIn('macaroon', result)
1814
1900
 
1815
1901
    def test_approved_data_for_ax_only(self):
1816
1902
        post_args = {'email': 'email', 'ubuntu-team': 'ubuntu-team'}
1817
1903
        result = server._get_approved_data(
1818
1904
            self._get_request_with_post_args(post_args),
1819
1905
            self._get_openid_request(with_sreg=False, with_ax=True,
1820
 
                                     with_teams=False))
 
1906
                                     with_teams=False, with_macaroon=False))
1821
1907
        self.assertEqual(sorted(result['user_attribs']['requested']),
1822
1908
                         ['email', 'fullname'])
1823
1909
        self.assertEqual(result['user_attribs']['approved'], ['email'])
1824
1910
        self.assertNotIn('teams', result)
 
1911
        self.assertNotIn('macaroon', result)
1825
1912
 
1826
1913
    def test_approved_data_for_teams_only(self):
1827
1914
        post_args = {'ubuntu-team': 'ubuntu-team'}
1828
1915
        result = server._get_approved_data(
1829
1916
            self._get_request_with_post_args(post_args),
1830
1917
            self._get_openid_request(with_sreg=False, with_ax=False,
1831
 
                                     with_teams=True))
 
1918
                                     with_teams=True, with_macaroon=False))
1832
1919
        self.assertEqual(result['teams']['requested'], ['ubuntu-team'])
1833
1920
        self.assertEqual(result['teams']['approved'], ['ubuntu-team'])
1834
1921
        self.assertNotIn('user_attribs', result)
 
1922
        self.assertNotIn('macaroon', result)
1835
1923
 
1836
1924
    def test_approved_data_for_sreg_and_teams(self):
1837
1925
        post_args = {'email': 'email', 'ubuntu-team': 'ubuntu-team'}
1838
1926
        result = server._get_approved_data(
1839
1927
            self._get_request_with_post_args(post_args),
1840
1928
            self._get_openid_request(with_sreg=True, with_ax=False,
1841
 
                                     with_teams=True))
 
1929
                                     with_teams=True, with_macaroon=False))
1842
1930
        self.assertEqual(sorted(result['user_attribs']['requested']),
1843
1931
                         ['email', 'fullname'])
1844
1932
        self.assertEqual(result['user_attribs']['approved'], ['email'])
1845
1933
        self.assertEqual(result['teams']['requested'], ['ubuntu-team'])
1846
1934
        self.assertEqual(result['teams']['approved'], ['ubuntu-team'])
 
1935
        self.assertNotIn('macaroon', result)
1847
1936
 
1848
1937
    def test_approved_data_for_ax_and_teams(self):
1849
1938
        post_args = {'email': 'email', 'ubuntu-team': 'ubuntu-team'}
1850
1939
        result = server._get_approved_data(
1851
1940
            self._get_request_with_post_args(post_args),
1852
1941
            self._get_openid_request(with_sreg=False, with_ax=True,
1853
 
                                     with_teams=True))
 
1942
                                     with_teams=True, with_macaroon=False))
1854
1943
        self.assertEqual(sorted(result['user_attribs']['requested']),
1855
1944
                         ['email', 'fullname'])
1856
1945
        self.assertEqual(result['user_attribs']['approved'], ['email'])
1857
1946
        self.assertEqual(result['teams']['requested'], ['ubuntu-team'])
1858
1947
        self.assertEqual(result['teams']['approved'], ['ubuntu-team'])
 
1948
        self.assertNotIn('macaroon', result)
 
1949
 
 
1950
    def test_approved_data_for_macaroon_only(self):
 
1951
        post_args = {'macaroon': 'macaroon'}
 
1952
        result = server._get_approved_data(
 
1953
            self._get_request_with_post_args(post_args),
 
1954
            self._get_openid_request(with_sreg=False, with_ax=False,
 
1955
                                     with_teams=False, with_macaroon=True))
 
1956
        self.assertEqual(result['macaroon']['requested'], ['macaroon'])
 
1957
        self.assertEqual(result['macaroon']['approved'], ['macaroon'])
 
1958
        self.assertNotIn('user_attribs', result)
 
1959
        self.assertNotIn('teams', result)
1859
1960
 
1860
1961
 
1861
1962
class TokenLoginTestCase(SSOBaseTestCase):