Projet

Général

Profil

0001-agent-authentic2-retry-service-s-metadata-retrieval-.patch

Benjamin Dauvergne, 09 août 2019 20:45

Télécharger (14,5 ko)

Voir les différences:

Subject: [PATCH] agent/authentic2: retry service's metadata retrieval (#35351)

 .../management/commands/hobo_deploy.py        | 213 +++++++++++-------
 .../common/management/commands/hobo_deploy.py |   1 +
 tests_authentic/test_hobo_deploy.py           |  18 +-
 3 files changed, 141 insertions(+), 91 deletions(-)
hobo/agent/authentic2/management/commands/hobo_deploy.py
1 1
import requests
2 2
import logging
3 3
import os
4
import time
5
import xml.etree.ElementTree as ET
4 6

  
5 7
from authentic2 import app_settings
6 8
from authentic2.compat_lasso import lasso
......
27 29
class Command(hobo_deploy.Command):
28 30
    help = 'Deploy multitenant authentic service from hobo'
29 31

  
32
    backoff_factor = 5
33

  
30 34
    def __init__(self, *args, **kwargs):
31 35
        self.logger = logging.getLogger(__name__)
32 36
        super(Command, self).__init__(*args, **kwargs)
......
117 121

  
118 122
            # create or update Service Providers
119 123
            services = hobo_environment['services']
120
            for service in services:
121
                if not service.get('saml-sp-metadata-url'):
122
                    continue
123
                sp_url = service['saml-sp-metadata-url']
124
                try:
125
                    metadata_response = requests.get(
126
                        sp_url, verify=app_settings.A2_VERIFY_SSL, timeout=5)
127
                    metadata_response.raise_for_status()
128
                except requests.exceptions.RequestException as e:
129
                    self.stderr.write(self.style.WARNING(
130
                        'Error registering %s: %r\n' % (sp_url, e)))
131
                    continue
132
                metadata_text = metadata_response.text
133

  
134
                provider, service_created = \
135
                    LibertyProvider.objects.get_or_create(
136
                        entity_id=sp_url,
137
                        protocol_conformance=lasso.PROTOCOL_SAML_2_0)
138
                provider.name = service['title']
139
                provider.slug = service['slug']
140
                provider.federation_source = 'hobo'
141
                provider.metadata = metadata_text
142
                provider.metadata_url = service['saml-sp-metadata-url']
143
                if service.get('variables', {}).get('ou-slug'):
144
                    ou, created = get_ou_model().objects.get_or_create(
145
                            slug=service['variables']['ou-slug'])
146
                    ou.name = service['variables']['ou-label']
147
                    ou.save()
148
                else:
149
                    # if there are more than one w.c.s. service we will create an
150
                    # ou of the same name
151
                    ou = get_default_ou()
152
                    create_ou = False
153
                    if service_created and service['service-id'] == 'wcs':
154
                        for s in services:
155
                            if s['service-id'] != 'wcs':
156
                                continue
157
                            if s['slug'] == service['slug']:
158
                                continue
159
                            if LibertyProvider.objects.filter(
160
                                    slug=s['slug']).exists():
161
                                create_ou = True
162
                                break
163
                    if create_ou:
124
            retries = 0
125
            loaded = 0
126
            max_retries = 1 if self.redeploy else 5
127
            while retries < max_retries:
128
                for service in services:
129
                    if service.get('$done'):
130
                        continue
131
                    if not service.get('saml-sp-metadata-url'):
132
                        service['$done'] = True
133
                        loaded += 1
134
                        continue
135
                    sp_url = service['saml-sp-metadata-url']
136
                    metadata_text = None
137
                    try:
138
                        metadata_response = requests.get(
139
                            sp_url, verify=app_settings.A2_VERIFY_SSL, timeout=5)
140
                        metadata_response.raise_for_status()
141
                        # verify metadata is correct
142
                        if self.check_saml_metadata(metadata_response.text):
143
                            metadata_text = metadata_response.text
144
                        else:
145
                            service['$last-error'] = 'metadata is incorrect'
146
                            continue
147
                    except requests.exceptions.RequestException as e:
148
                        service['$last-error'] = str(e)
149
                        continue
150
                    metadata_text = metadata_response.text
151

  
152
                    provider, service_created = \
153
                        LibertyProvider.objects.get_or_create(
154
                            entity_id=sp_url,
155
                            protocol_conformance=lasso.PROTOCOL_SAML_2_0)
156
                    provider.name = service['title']
157
                    provider.slug = service['slug']
158
                    provider.federation_source = 'hobo'
159
                    provider.metadata = metadata_text
160
                    provider.metadata_url = service['saml-sp-metadata-url']
161
                    if service.get('variables', {}).get('ou-slug'):
164 162
                        ou, created = get_ou_model().objects.get_or_create(
165
                            name=service['title'])
166
                if service_created or not provider.ou:
167
                    provider.ou = ou
168
                provider.save()
169
                if service_created:
170
                    service_provider = LibertyServiceProvider(
171
                        enabled=True, liberty_provider=provider,
172
                        sp_options_policy=policy,
173
                        users_can_manage_federations=False)
174
                    service_provider.save()
175

  
176
                # add a superuser role for the service
177
                Role = get_role_model()
178
                name = _('Superuser of %s') % service['title']
179
                su_role, created = Role.objects.get_or_create(
180
                    service=provider, slug='_a2-hobo-superuser',
181
                    defaults={'name': name})
182
                if su_role.name != name:
183
                    su_role.name = name
184
                    su_role.save()
185
                su_role.attributes.get_or_create(name='is_superuser',
186
                                                 kind='string',
187
                                                 value='true')
188
                # pass the new attribute to the service
189
                SAMLAttribute.objects.get_or_create(
190
                    name='is_superuser',
191
                    name_format='basic',
192
                    attribute_name='is_superuser',
193
                    object_id=provider.pk,
194
                    content_type=provider_type)
195
                SAMLAttribute.objects.get_or_create(
196
                    name='role-slug',
197
                    name_format='basic',
198
                    attribute_name='a2_service_ou_role_uuids',
199
                    object_id=provider.pk,
200
                    content_type=provider_type)
201
                # load skeleton if service is new
202
                if service.get('template_name'):
203
                    # if there are more of the same servie, we will create an
204
                    # ou
205
                    self.load_skeleton(provider, service['service-id'],
206
                                       service['template_name'])
163
                                slug=service['variables']['ou-slug'])
164
                        ou.name = service['variables']['ou-label']
165
                        ou.save()
166
                    else:
167
                        # if there are more than one w.c.s. service we will create an
168
                        # ou of the same name
169
                        ou = get_default_ou()
170
                        create_ou = False
171
                        if service_created and service['service-id'] == 'wcs':
172
                            for s in services:
173
                                if s['service-id'] != 'wcs':
174
                                    continue
175
                                if s['slug'] == service['slug']:
176
                                    continue
177
                                if LibertyProvider.objects.filter(
178
                                        slug=s['slug']).exists():
179
                                    create_ou = True
180
                                    break
181
                        if create_ou:
182
                            ou, created = get_ou_model().objects.get_or_create(
183
                                name=service['title'])
184
                    if service_created or not provider.ou:
185
                        provider.ou = ou
186
                    provider.save()
187
                    if service_created:
188
                        service_provider = LibertyServiceProvider(
189
                            enabled=True, liberty_provider=provider,
190
                            sp_options_policy=policy,
191
                            users_can_manage_federations=False)
192
                        service_provider.save()
193

  
194
                    # add a superuser role for the service
195
                    Role = get_role_model()
196
                    name = _('Superuser of %s') % service['title']
197
                    su_role, created = Role.objects.get_or_create(
198
                        service=provider, slug='_a2-hobo-superuser',
199
                        defaults={'name': name})
200
                    if su_role.name != name:
201
                        su_role.name = name
202
                        su_role.save()
203
                    su_role.attributes.get_or_create(name='is_superuser',
204
                                                     kind='string',
205
                                                     value='true')
206
                    # pass the new attribute to the service
207
                    SAMLAttribute.objects.get_or_create(
208
                        name='is_superuser',
209
                        name_format='basic',
210
                        attribute_name='is_superuser',
211
                        object_id=provider.pk,
212
                        content_type=provider_type)
213
                    SAMLAttribute.objects.get_or_create(
214
                        name='role-slug',
215
                        name_format='basic',
216
                        attribute_name='a2_service_ou_role_uuids',
217
                        object_id=provider.pk,
218
                        content_type=provider_type)
219
                    # load skeleton if service is new
220
                    if service.get('template_name'):
221
                        # if there are more of the same servie, we will create an
222
                        # ou
223
                        self.load_skeleton(provider, service['service-id'],
224
                                           service['template_name'])
225
                    service['$done'] = True
226
                    loaded += 1
227

  
228
                if len(services) == loaded:
229
                    # it's finished no need to continue
230
                    break
231

  
232
                # wait 5, 10, 20, 40, .. seconds
233
                time.sleep(self.backoff_factor * (2 ** retries))
234
                retries += 1
235

  
236
            for service in services:
237
                if not service.get('$done'):
238
                    last_error = service['$last-error']
239
                    sp_url = service['saml-sp-metadata-url']
240
                    self.stderr.write(self.style.WARNING('Error registering %s: %s\n' % (sp_url, last_error)))
207 241

  
208 242
    def load_skeleton(self, provider, service_id, template_name,
209 243
                      create_ou=False):
......
242 276
        if roles:
243 277
            Role.objects.bulk_create(roles)
244 278
            Role.objects.get(uuid=roles[-1].uuid).save()
279

  
280
    def check_saml_metadata(self, saml_metadata):
281
        try:
282
            root = ET.fromstring(saml_metadata)
283
        except ET.ParseError:
284
            return False
285
        return root.tag == '{%s}EntityDescriptor' % lasso.SAML2_METADATA_HREF
hobo/agent/common/management/commands/hobo_deploy.py
44 44

  
45 45
    def handle(self, base_url=None, json_filename=None, ignore_timestamp=None,
46 46
               redeploy=None, *args, **kwargs):
47
        self.redeploy = redeploy
47 48
        if redeploy:
48 49
            for tenant in TenantMiddleware.get_tenants():
49 50
                try:
tests_authentic/test_hobo_deploy.py
6 6
import json
7 7
import mock
8 8

  
9
from requests import RequestException
10

  
9 11
from django.core.management import call_command
10 12
from django.db import connection
11 13
from hobo.multitenant.middleware import TenantMiddleware
......
26 28
    return settings.HOBO_SKELETONS_DIR
27 29

  
28 30

  
29
def test_hobo_deploy(tenant_base, mocker, skeleton_dir):
31
def test_hobo_deploy(monkeypatch, tenant_base, mocker, skeleton_dir):
30 32
    from django.core.management import call_command
31 33
    from django.conf import settings
34
    from hobo.agent.authentic2.management.commands.hobo_deploy import Command as HoboDeployCommand
32 35

  
33 36
    # Create skeleton roles.json
34 37
    os.makedirs(os.path.join(skeleton_dir, 'commune', 'wcs'))
......
117 120
    meta2 = meta1.replace('eservices', 'passerelle')
118 121
    meta3 = meta1.replace('eservices', 'clapiers')
119 122
    metadatas = [meta1, meta2, meta3]
120
    side_effect = []
121
    for meta in metadatas:
123
    monkeypatch.setattr(HoboDeployCommand, 'backoff_factor', 0.0001)
124
    side_effect_iter = iter([meta1, meta2, RequestException(), meta3])
125

  
126
    def side_effect(*args, **kwargs):
127
        v = next(side_effect_iter)
128
        if isinstance(v, Exception):
129
            raise v
122 130
        m = mock.Mock()
123
        m.text = meta
124
        side_effect.append(m)
131
        m.text = v
132
        return m
125 133
    requests_get.side_effect = side_effect
126 134
    env = {
127 135
        'users': [
128
-