Projet

Général

Profil

0001-agent-authentic2-add-new-command-import-wcs-roles.patch

Benjamin Dauvergne, 07 mai 2015 20:21

Télécharger (6,65 ko)

Voir les différences:

Subject: [PATCH] agent/authentic2: add new command import-wcs-roles

The command make a signed get to the "roles" web-service of W.C.S. and try to
created services roles for all found roles.

   ./authentic2-ctl import-wcs-roles --slug <slug-of-the-wcs-saml-service> --key key --orig orig [--delete]

Use the --delete option if you want roles which have disapeared to be removed.
 .../management/commands/import-wcs-roles.py        | 24 +++++++
 .../agent/authentic2/management/role_extractors.py | 75 ++++++++++++++++++++++
 hobo/agent/authentic2/management/signature.py      | 37 +++++++++++
 3 files changed, 136 insertions(+)
 create mode 100644 hobo/agent/authentic2/management/commands/import-wcs-roles.py
 create mode 100644 hobo/agent/authentic2/management/role_extractors.py
 create mode 100644 hobo/agent/authentic2/management/signature.py
hobo/agent/authentic2/management/commands/import-wcs-roles.py
1
from optparse import make_option
2
from django.core.management.base import BaseCommand
3

  
4
from .. import role_extractors
5

  
6
class Command(BaseCommand):
7
    option_list = BaseCommand.option_list + (
8
        make_option('--slug', action='store', dest='slug'),
9
        make_option('--key', action='store', dest='key'),
10
        make_option('--orig', action='store', dest='orig'),
11
        make_option('--delete', action='store_true', dest='delete'),
12
    )
13
    help = "Import W.C.S. roles"
14

  
15
    requires_system_checks = False
16

  
17
    def handle(self, *args, **options):
18
        role_extractor = role_extractors.WcsRoleExtractor(
19
            slug=options['slug'],
20
            key=options['key'],
21
            orig=options['orig'],
22
            delete=options.get('delete', False),
23
        )
24
        role_extractor.extract()
hobo/agent/authentic2/management/role_extractors.py
1
import logging
2
import requests
3
import urllib
4

  
5
from django.utils.text import slugify
6

  
7
from authentic2.saml.models import LibertyProvider
8
from authentic2.model import ServiceRole, ServiceRoleAttribute
9

  
10
from . import signature
11

  
12
class RoleExtractor(object):
13
    def __init__(self, slug, delete=False):
14
        self.slug = slug
15
        self.logger = logging.getLogger('%s.%s' % (__name__, self.__class__.__name__))
16
        self.delete = delete
17

  
18
    def extract_impl(self):
19
        pass
20

  
21
    def extract(self):
22
        seen_ids = set()
23
        for role_tpl in self.extract_impl():
24
            defaults = {
25
                'name': role_tpl['name'],
26
                'slug': role_tpl.get('slug', slugify(role_tpl['name'])),
27
            }
28
            seen_ids.add(role_tpl['seen_ids'])
29
            role, created = ServiceRole.objects.get_or_create(
30
                    service=self.service,
31
                    external_id=role_tpl['external_id'],
32
                    defaults=defaults)
33
            ServiceRoleAttribute.objects.get_or_create(
34
                    service_role=role,
35
                    name=role_tpl['attribute_name'],
36
                    kind='string',
37
                    value=role_tpl['external_id'])
38
            if created:
39
                self.logger.info('imported new role %r(%r) from service '
40
                        '%s', role_tpl['external_id'], role_tpl['name'],
41
                        self.slug)
42
            self.logger
43
            if not created:
44
                # Update name if it has changed
45
                if role.name != role_tpl['name']:
46
                    role.name = role_tpl['name']
47
                    role.save()
48
        if self.delete:
49
            qs = ServiceRole.objects.filter(service=self.service) \
50
                .exclude(external_id__in=list(seen_ids))
51
            for role in qs:
52
                self.logger.info('deleted dead role %r(%r) from service '
53
                        '%s', role.external_id, role.slug, self.slug)
54
            qs.delete()
55

  
56
class WcsRoleExtractor(RoleExtractor):
57
    def __init__(self, *args, **kwargs):
58
        self.key = kwargs.pop('key')
59
        self.orig = kwargs.pop('orig')
60
        self.attribute_name = kwargs.pop('attribute_name', 'role_id')
61
        super(WcsRoleExtractor, self).__init__(*args, **kwargs)
62
        self.service = LibertyProvider.objects.get(slug=self.slug)
63
        assert 'saml/metadata' in self.liberty_provider.entity_id
64
        self.wcs_url = self.liberty_provider.entity_id.split('saml/metadata')[0]
65

  
66
    def extract_impl(self):
67
        url = self.wcs_url + 'roles?%s' % urllib.urlencode({'orig': self.orig})
68
        signed_url = signature.sign_url(url, self.key)
69
        response = requests.get(signed_url)
70
        for role in response.json()['data']:
71
            yield {
72
                'attribute_name': self.attribute_name,
73
                'external_id': str(role['id']),
74
                'name': role['text'],
75
            }
hobo/agent/authentic2/management/signature.py
1
import base64
2
import hmac
3
import hashlib
4
import datetime
5
import urllib
6
import urllib2
7
import urlparse
8
import random
9

  
10
def sign_url(url, key, algo='sha256', timestamp=None, nonce=None):
11
    parsed = urlparse.urlparse(url)
12
    new_query = sign_query(parsed.query, key, algo, timestamp, nonce)
13
    return urlparse.urlunparse(parsed[:4] + (new_query,) + parsed[5:])
14

  
15
def sign_query(query, key, algo='sha256', timestamp=None, nonce=None):
16
    if timestamp is None:
17
        timestamp = datetime.datetime.utcnow()
18
    timestamp = timestamp.strftime('%Y-%m-%dT%H:%M:%SZ')
19
    if nonce is None:
20
        nonce = hex(random.getrandbits(128))[2:-1]
21
    new_query = query
22
    if new_query:
23
        new_query += '&'
24
    new_query += urllib.urlencode((
25
        ('algo', algo),
26
        ('timestamp', timestamp),
27
        ('nonce', nonce)))
28
    signature = base64.b64encode(sign_string(new_query, key, algo=algo))
29
    new_query += '&signature=' + urllib.quote(signature)
30
    return new_query
31

  
32
def sign_string(s, key, algo='sha256', timedelta=30):
33
    digestmod = getattr(hashlib, algo)
34
    hash = hmac.HMAC(key, digestmod=digestmod, msg=s)
35
    return hash.digest()
36

  
37

  
0
-