Completed
Push — master ( 7e5f4d...79309a )
by Andrea
01:19
created

Federation.get_idp()   A

Complexity

Conditions 3

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 0 Features 0
Metric Value
c 5
b 0
f 0
dl 0
loc 7
rs 9.4285
cc 3
1
#################################################################
2
# MET v2 Metadate Explorer Tool
3
#
4
# This Software is Open Source. See License: https://github.com/TERENA/met/blob/master/LICENSE.md
5
# Copyright (c) 2012, TERENA All rights reserved.
6
#
7
# This Software is based on MET v1 developed for TERENA by Yaco Sistemas, http://www.yaco.es/
8
# MET v2 was developed for TERENA by Tamim Ziai, DAASI International GmbH, http://www.daasi.de
9
# Current version of MET has been revised for performance improvements by Andrea Biancini,
10
# Consortium GARR, http://www.garr.it
11
#########################################################################################
12
13
import pytz
14
import simplejson as json
15
16
from datetime import datetime, time, timedelta
17
18
from django.contrib import messages
19
from django.core.urlresolvers import reverse
20
from django.db import models
21
from django.db.models import Max
22
from django.db.models.signals import pre_save
23
from django.utils.safestring import mark_safe
24
from django.utils.translation import ugettext_lazy as _
25
from django.utils import timezone
26
from django.dispatch import receiver
27
from django.template.defaultfilters import slugify
28
29
from met.metadataparser.xmlparser import MetadataParser
30
31
from base import Base
32
from entity import Entity
33
from entity_type import EntityType
34
from entity_stat import EntityStat, stats
35
from entity_federations import Entity_Federations
36
37
FEDERATION_TYPES = (
38
    (None, ''),
39
    ('hub-and-spoke', 'Hub and Spoke'),
40
    ('mesh', 'Full Mesh'),
41
)
42
43
def update_obj(mobj, obj, attrs=None):
44
    for_attrs = attrs or getattr(mobj, 'all_attrs', [])
45
    for attrb in attrs or for_attrs:
46
        if (getattr(mobj, attrb, None) and
47
            getattr(obj, attrb, None) and
48
            getattr(mobj, attrb) != getattr(obj, attrb)):
49
            setattr(obj, attrb, getattr(mobj, attrb))
50
51
class Federation(Base):
52
    name = models.CharField(blank=False, null=False, max_length=200,
53
                            unique=True, verbose_name=_(u'Name'))
54
55
    type = models.CharField(blank=True, null=True, max_length=100,
56
                            unique=False, verbose_name=_(u'Type'), choices=FEDERATION_TYPES)
57
58
    url = models.URLField(verbose_name='Federation url',
59
                          blank=True, null=True)
60
    
61
    fee_schedule_url = models.URLField(verbose_name='Fee schedule url',
62
                                       max_length=150, blank=True, null=True)
63
64
    logo = models.ImageField(upload_to='federation_logo', blank=True,
65
                             null=True, verbose_name=_(u'Federation logo'))
66
67
    is_interfederation = models.BooleanField(default=False, db_index=True,
68
                                         verbose_name=_(u'Is interfederation'))
69
70
    slug = models.SlugField(max_length=200, unique=True)
71
72
    country = models.CharField(blank=True, null=True, max_length=100,
73
                               unique=False, verbose_name=_(u'Country'))
74
75
    metadata_update = models.DateField(blank=True, null=True,
76
                                       unique=False, verbose_name=_(u'Metadata update date'))
77
78
    certstats = models.CharField(blank=True, null=True, max_length=200,
79
                                 unique=False, verbose_name=_(u'Certificate Stats'))
80
81
    @property
82
    def certificates(self):
83
        return json.loads(self.certstats)
84
85
    @property
86
    def _metadata(self):
87
        if not hasattr(self, '_metadata_cache'):
88
            self._metadata_cache = self.load_file()
89
        return self._metadata_cache
90
    def __unicode__(self):
91
        return self.name
92
93
    def get_entity_metadata(self, entityid):
94
        return self._metadata.get_entity(entityid)
95
96
    def get_entity(self, entityid):
97
        return self.entity_set.get(entityid=entityid)
98
99
    def process_metadata(self):
100
        metadata = self.load_file()
101
102
        if self.file_id and metadata.file_id and metadata.file_id == self.file_id:
103
            return
104
        else:
105
            self.file_id = metadata.file_id
106
107
        if not metadata:
108
            return
109
        if not metadata.is_federation:
110
            raise XmlDescriptionError("XML Haven't federation form")
111
112
        update_obj(metadata.get_federation(), self)
113
        self.certstats = MetadataParser.get_certstats(metadata.rootelem)
114
115
    def _remove_deleted_entities(self, entities_from_xml, request):
116
        entities_to_remove = []
117
        for entity in self.entity_set.all():
118
            #Remove entity relation if does not exist in metadata
119
            if not entity.entityid in entities_from_xml:
120
                entities_to_remove.append(entity)
121
122
        if len(entities_to_remove) > 0:
123
            self.entity_set.remove(*entities_to_remove)
124
125
            if request:
126
                for entity in entities_to_remove:
127
                    if not entity.federations.exists():
128
                        messages.warning(request,
129
                                         mark_safe(_("Orphan entity: <a href='%s'>%s</a>" %
130
                                         (entity.get_absolute_url(), entity.entityid))))
131
132
        return len(entities_to_remove)
133
134
    def _update_entities(self, entities_to_update, entities_to_add):
135
        for e in entities_to_update:
136
            e.save()
137
138
        for e in entities_to_add:
139
            membership = Entity_Federations.objects.get_or_create(federation=self, entity=e)[0]
140
            membership.registration_instant = e.registration_instant.date()
141
            membership.save()
142
143
    def _add_new_entities(self, entities, entities_from_xml, request, federation_slug):
144
        db_entity_types = EntityType.objects.all()
145
        cached_entity_types = { entity_type.xmlname: entity_type for entity_type in db_entity_types }
146
147
        entities_to_add = []
148
        entities_to_update = []
149
150
        for m_id in entities_from_xml:
151
            if request and federation_slug:
152
                request.session['%s_cur_entities' % federation_slug] += 1
153
                request.session.save()
154
155
            created = False
156
            if m_id in entities:
157
                entity = entities[m_id]
158
            else:
159
                entity, created = Entity.objects.get_or_create(entityid=m_id)
160
161
            entityid = entity.entityid
162
            name = entity.name
163
            registration_authority = entity.registration_authority
164
            certstats = entity.certstats
165
            display_protocols = entity._display_protocols
166
 
167
            entity_from_xml = self._metadata.get_entity(m_id, False)
168
            entity.process_metadata(False, entity_from_xml, cached_entity_types)
169
170
            if created or entity.has_changed(entityid, name, registration_authority, certstats, display_protocols):
171
                entities_to_update.append(entity)
172
173
            entities_to_add.append(entity)
174
175
        self._update_entities(entities_to_update, entities_to_add)
176
        return len(entities_to_update) 
177
178
    @staticmethod
179
    def _daterange(start_date, end_date):
180
        for n in range(int ((end_date - start_date).days + 1)):
181
            yield start_date + timedelta(n)
182
183
    def compute_new_stats(self):
184
        entities_from_xml = self._metadata.get_entities()
185
186
        entities = Entity.objects.filter(entityid__in=entities_from_xml)
187
        entities = entities.prefetch_related('types')
188
        memberships = Entity_Federations.objects.filter(federation=self)
189
190
        try:
191
            first_date = EntityStat.objects.filter(federation=self).aggregate(Max('time'))['time__max']
192
            if not first_date:
193
                raise Exception('Not able to find statistical data in the DB.')
194
        except Exception:
195
            first_date = datetime(2010, 1, 1)
196
            first_date = pytz.utc.localize(first_date)
197
      
198
        for curtimestamp in self._daterange(first_date, timezone.now()):
199
            computed = {}
200
            not_computed = []
201
            entity_stats = []
202
            for feature in stats['features'].keys():
203
                fun = getattr(self, 'get_%s' % feature, None)
204
    
205
                if callable(fun):
206
                    stat = EntityStat()
207
                    stat.feature = feature
208
                    stat.time = curtimestamp
209
                    stat.federation = self
210
                    stat.value = fun(entities, stats['features'][feature], curtimestamp)
211
                    entity_stats.append(stat)
212
                    computed[feature] = stat.value
213
                else:
214
                    not_computed.append(feature)
215
216
            from_time = datetime.combine(curtimestamp, time.min) 
217
            if timezone.is_naive(from_time):
218
                from_time = pytz.utc.localize(from_time)
219
            to_time = datetime.combine(curtimestamp, time.max)
220
            if timezone.is_naive(to_time):
221
                to_time = pytz.utc.localize(to_time)
222
223
            EntityStat.objects.filter(federation=self, time__gte=from_time, time__lte=to_time).delete()
224
            EntityStat.objects.bulk_create(entity_stats)
225
226
        return (computed, not_computed)
227
228
    def process_metadata_entities(self, request=None, federation_slug=None):
229
        entities_from_xml = self._metadata.get_entities()
230
        removed = self._remove_deleted_entities(entities_from_xml, request)
231
232
        entities = {}
233
        db_entities = Entity.objects.filter(entityid__in=entities_from_xml)
234
        db_entities = db_entities.prefetch_related('types', 'entity_categories')
235
236
        for entity in db_entities.all():
237
            entities[entity.entityid] = entity
238
239
        if request and federation_slug:
240
            request.session['%s_num_entities' % federation_slug] = len(entities_from_xml)
241
            request.session['%s_cur_entities' % federation_slug] = 0
242
            request.session['%s_process_done' % federation_slug] = False
243
            request.session.save()
244
245
        updated = self._add_new_entities(entities, entities_from_xml, request, federation_slug)
246
247
        if request and federation_slug:
248
            request.session['%s_process_done' % federation_slug] = True
249
            request.session.save()
250
251
        return removed, updated
252
253
    def get_absolute_url(self):
254
        return reverse('federation_view', args=[self.slug])
255
256
    @classmethod
257
    def get_sp(cls, entities, xml_name, ref_date=None):
258
        if ref_date and ref_date < pytz.utc.localize(datetime.now() - timedelta(days = 1)):
259
            selected = entities.filter(types__xmlname=xml_name, entity_federations__registration_instant__lt = ref_date)
260
        else:
261
            selected = entities.filter(types__xmlname=xml_name)
262
        return len(selected)
263
264
    @classmethod
265
    def get_idp(cls, entities, xml_name, ref_date=None):
266
        if ref_date and ref_date < pytz.utc.localize(datetime.now() - timedelta(days = 1)):
267
            selected = entities.filter(types__xmlname=xml_name, entity_federations__registration_instant__lt = ref_date)
268
        else:
269
            selected = entities.filter(types__xmlname=xml_name)
270
        return len(selected)
271
272
    @classmethod
273
    def get_aa(cls, entities, xml_name, ref_date=None):
274
        if ref_date and ref_date < pytz.utc.localize(datetime.now() - timedelta(days = 1)):
275
            selected = entities.filter(types__xmlname=xml_name, entity_federations__registration_instant__lt = ref_date)
276
        else:
277
            selected = entities.filter(types__xmlname=xml_name)
278
        return len(selected)
279
280
    def get_sp_saml1(self, entities, xml_name, ref_date = None):
281
        return self.get_stat_protocol(entities, xml_name, 'SPSSODescriptor', ref_date)
282
283
    def get_sp_saml2(self, entities, xml_name, ref_date = None):
284
        return self.get_stat_protocol(entities, xml_name, 'SPSSODescriptor', ref_date)
285
286
    def get_sp_shib1(self, entities, xml_name, ref_date = None):
287
        return self.get_stat_protocol(entities, xml_name, 'SPSSODescriptor', ref_date)
288
289
    def get_idp_saml1(self, entities, xml_name, ref_date = None):
290
        return self.get_stat_protocol(entities, xml_name, 'IDPSSODescriptor', ref_date)
291
292
    def get_idp_saml2(self, entities, xml_name, ref_date = None):
293
        return self.get_stat_protocol(entities, xml_name, 'IDPSSODescriptor', ref_date)
294
295
    def get_idp_shib1(self, entities, xml_name, ref_date = None):
296
        return self.get_stat_protocol(entities, xml_name, 'IDPSSODescriptor', ref_date)
297
298
    def get_stat_protocol(self, entities, xml_name, service_type, ref_date):
299
        if ref_date and ref_date < pytz.utc.localize(datetime.now() - timedelta(days = 1)):
300
            selected = entities.filter(types__xmlname=service_type, _display_protocols__contains=xml_name, entity_federations__registration_instant__lt = ref_date)
301
        else:
302
            selected = entities.filter(types__xmlname=service_type, _display_protocols__contains=xml_name)
303
        return len(selected)
304
305
    def can_edit(self, user, delete):
306
        if user.is_superuser:
307
            return True
308
309
        permission = 'delete_federation' if delete else 'change_federation'
310
        if user.has_perm('metadataparser.%s' % permission) and user in self.editor_users.all():
311
            return True
312
        return False
313
314
@receiver(pre_save, sender=Federation, dispatch_uid='federation_pre_save')
315
def federation_pre_save(sender, instance, **kwargs):
316
    # Skip pre_save if only file name is saved
317
    if kwargs.has_key('update_fields') and kwargs['update_fields'] == set(['file']):
318
        return
319
320
    #slug = slugify(unicode(instance.name))[:200]
321
    #if instance.file_url and instance.file_url != '':
322
    #    try:
323
    #        instance.fetch_metadata_file(slug)
324
    #    except Exception, e:
325
    #        pass
326
327
    if instance.name:
328
        instance.slug = slugify(unicode(instance))[:200]
329
330
331
@receiver(pre_save, sender=Entity, dispatch_uid='entity_pre_save')
332
def entity_pre_save(sender, instance, **kwargs):
333
    #if refetch and instance.file_url:
334
    #    slug = slugify(unicode(instance.name))[:200]
335
    #    instance.fetch_metadata_file(slug)
336
    #    instance.process_metadata()
337
    pass
338