Projet

Général

Profil

0001-authentic-agent-remove-obsolete-import-wcs-roles-com.patch

Frédéric Péters, 12 août 2019 16:31

Télécharger (9,89 ko)

Voir les différences:

Subject: [PATCH] authentic agent: remove obsolete import-wcs-roles command
 (#35374)

 README                                        |   6 -
 .../management/commands/import-wcs-roles.py   | 202 ------------------
 2 files changed, 208 deletions(-)
 delete mode 100644 hobo/agent/authentic2/management/commands/import-wcs-roles.py
README
156 156
same rights as the authentic2 process (redefine the command to use sudo if
157 157
necessary).
158 158

  
159
The agent also provide a commands to import roles from w.c.s named
160
import-wcs-roles. It computes the web-service credentials from the hobo.json
161
and use the email of the oldest superuser. Cron job can be created for calling
162
this command when regular synchronization of roles with your w.c.s.  instances
163
is needed. The sole option named "--delete" indicate if you want to delete
164
stale roles, default is to not delete them.  
165 159

  
166 160
Tests
167 161
-----
hobo/agent/authentic2/management/commands/import-wcs-roles.py
1
# hobo - portal to configure and deploy applications
2
# Copyright (C) 2015  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
import os
18
import logging
19
import requests
20
import urllib
21
import urlparse
22
import hashlib
23
import json
24

  
25
from optparse import make_option
26

  
27
from django.utils.text import slugify
28
from django.core.management.base import BaseCommand
29
from django.utils.translation import ugettext as _
30
from django.conf import settings
31

  
32
from authentic2.saml.models import LibertyProvider
33
from authentic2.a2_rbac.models import Role, RoleAttribute
34
from authentic2 import app_settings
35

  
36

  
37
from hobo import signature
38
from hobo.multitenant.middleware import TenantMiddleware
39

  
40
from tenant_schemas.utils import tenant_context
41

  
42

  
43
class WcsRoleImporter(object):
44
    def __init__(self, liberty_provider, key, orig,
45
                 attribute_name='role-slug', delete=False):
46
        self.service = liberty_provider
47
        self.slug = liberty_provider.slug
48
        self.key = key
49
        self.orig = orig
50
        self.attribute_name = attribute_name
51
        self.delete = delete
52
        assert 'saml/metadata' in self.service.entity_id
53
        self.wcs_url = self.service.entity_id.split('saml/metadata')[0]
54
        self.logger = logging.getLogger('%s.%s' % (__name__,
55
                                                   self.__class__.__name__))
56
        self.seen_ids = set()
57

  
58
    def import_roles(self):
59
        for role_tpl in self.get_roles():
60
            self.seen_ids.add(role_tpl.external_id)
61
            self.create_role(role_tpl)
62
        if self.delete:
63
            self.delete_dead_roles()
64
        su_role, created = Role.objects.get_or_create(
65
            service=self.service, slug='_a2-hobo-superuser',
66
            defaults={'name': _('Superuser')})
67
        su_role.attributes.get_or_create(name='is_superuser', kind='string',
68
                                         value='true')
69

  
70
    def create_role(self, role_tpl):
71
        defaults = {
72
            'name': role_tpl.name,
73
            # w.c.s. will always provide a slug but for other services we do
74
            # not know
75
            'slug': role_tpl.slug or slugify(role_tpl.name),
76
        }
77
        # search role by external id, create if not found
78
        role, created = Role.objects.get_or_create(
79
            ou=self.service.ou,
80
            external_id=role_tpl.external_id,
81
            defaults=defaults)
82
        RoleAttribute.objects.filter(role=role).delete()
83
        role_attribute, ra_created = RoleAttribute.objects.get_or_create(
84
            role=role,
85
            name=self.attribute_name,
86
            kind='string',
87
            defaults={
88
                'value': role_tpl.external_id
89
            })
90
        if created:
91
            self.logger.info('imported new role %r(%r) from service %s',
92
                             role.external_id, role.name, self.slug)
93
        # update role attribute value if it has changed
94
        if not ra_created:
95
            if role_attribute.value != role_tpl.external_id:
96
                role_attribute.value = role_tpl.external_id
97
                role_attribute.save()
98
        # update role name if has changed
99
        if not created:
100
            # Update name and slug if they have changed
101
            if role.name != role_tpl.name:
102
                role.name = role_tpl.name
103
                role.save()
104
        # update emails, emails_to_members and details in RA
105
        if role_tpl.emails:
106
            ra, created = RoleAttribute.objects.get_or_create(
107
                role=role, name='emails', kind='json',
108
                defaults={'value': json.dumps(role_tpl.emails)})
109
            if ra.value != json.dumps(role_tpl.emails):
110
                ra.value = json.dumps(role_tpl.emails)
111
                ra.save()
112
        ra, created = RoleAttribute.objects.get_or_create(
113
            role=role, name='emails_to_members', kind='json',
114
            defaults={'value': json.dumps(role_tpl.emails_to_members)})
115
        if ra.value != json.dumps(role_tpl.emails_to_members):
116
            ra.value = json.dumps(role_tpl.emails_to_members)
117
            ra.save()
118
        if role_tpl.details:
119
            value = json.dumps(role_tpl.details)
120
            ra, created = RoleAttribute.objects.get_or_create(
121
                role=role, name='details', kind='json',
122
                defaults={'value': value})
123
            if ra.value != value:
124
                ra.value = value
125
                ra.save()
126

  
127
    def delete_dead_roles(self):
128
        '''Deletes service roles whose id is not in self.seen_ids'''
129
        qs = Role.objects.filter(service=self.service) \
130
            .exclude(external_id__in=list(self.seen_ids))
131
        for role in qs:
132
            self.logger.info('deleted dead role %r(%r) from service %s',
133
                             role.external_id, role.slug, self.slug)
134
        qs.delete()
135

  
136
    def get_roles(self):
137
        '''Get w.c.s. from its roles web-service by sending a signed GET
138
           request.
139
        '''
140
        url = self.wcs_url + 'api/roles?%s' % urllib.urlencode(
141
            {'format': 'json', 'orig': self.orig})
142
        signed_url = signature.sign_url(url, self.key)
143
        response = requests.get(signed_url, verify=app_settings.A2_VERIFY_SSL)
144
        if response.status_code == 200:
145
            for role in response.json()['data']:
146
                new_role = Role(name=role['text'], external_id=str(role['slug']),
147
                                slug=str(role['slug']))
148
                new_role.description = role.get('details', u'')
149
                new_role.emails = role.get('emails', [])
150
                new_role.emails_to_members = role.get('emails_to_members', False)
151
                new_role.details = role.get('details', '')
152
                yield new_role
153
        else:
154
            self.logger.warn('failed to get roles for %s (response: %s)',
155
                             self.wcs_url, response.status_code)
156

  
157

  
158
class Command(BaseCommand):
159
    help = "Import W.C.S. roles"
160

  
161
    requires_system_checks = False
162

  
163
    def add_arguments(self, parser):
164
        parser.add_argument('--delete', action="store_true", dest='delete')
165

  
166
    def handle(self, *args, **options):
167
        # traverse list of tenants
168
        for tenant in TenantMiddleware.get_tenants():
169
            with tenant_context(tenant):
170
                if getattr(settings, 'HOBO_ROLE_EXPORT', True):
171
                    continue
172
                self.handle_tenant(tenant, **options)
173

  
174
    def handle_tenant(self, tenant, **options):
175
        # extract informations on deployed w.c.s. instances from hobo.json
176
        hobo_json_path = os.path.join(tenant.get_directory(), 'hobo.json')
177
        if not os.path.exists(hobo_json_path):
178
            print 'skipping %s, no hobo.json found' % tenant
179
            return
180
        hobo_environment = json.load(open(hobo_json_path))
181
        # compute our credentials from our hobo configuration
182
        me = [x for x in hobo_environment['services'] if x.get('this')]
183
        if not me:
184
            print 'skipping %s, self services is not marked' % tenant
185
            return
186
        me = me[0]
187
        orig = urlparse.urlsplit(me['base_url']).netloc.split(':')[0]
188
        for service_id in settings.KNOWN_SERVICES:
189
            if service_id != 'wcs':
190
                continue
191
            for service in settings.KNOWN_SERVICES[service_id].values():
192
                if not service.get('secret'):
193
                    continue
194
                liberty_provider = LibertyProvider.objects.get(
195
                    entity_id=service['url'] + 'saml/metadata')
196
                importer = WcsRoleImporter(
197
                    liberty_provider=liberty_provider,
198
                    key=service['secret'],
199
                    orig=orig,
200
                    delete=options.get('delete', False),
201
                )
202
                importer.import_roles()
203
-