root / edumanage / models.py @ 3cdee1de
History | View | Annotate | Download (22.1 kB)
1 |
# -*- coding: utf-8 -*- vim:fileencoding=utf-8:
|
---|---|
2 |
# vim: tabstop=4:shiftwidth=4:softtabstop=4:expandtab
|
3 |
|
4 |
'''
|
5 |
TODO main description
|
6 |
'''
|
7 |
|
8 |
from django.db import models |
9 |
from django.utils.translation import ugettext_lazy as _ |
10 |
|
11 |
from django.contrib.contenttypes.models import ContentType |
12 |
from django.contrib.contenttypes import generic |
13 |
from django.db import models |
14 |
from django import forms |
15 |
from django import forms |
16 |
from django.db import models |
17 |
from django.utils.text import capfirst |
18 |
from django.core import exceptions |
19 |
from django.conf import settings |
20 |
from django.contrib.auth.models import User |
21 |
|
22 |
class MultiSelectFormField(forms.MultipleChoiceField): |
23 |
widget = forms.CheckboxSelectMultiple |
24 |
|
25 |
def __init__(self, *args, **kwargs): |
26 |
self.max_choices = 4 |
27 |
super(MultiSelectFormField, self).__init__(*args, **kwargs) |
28 |
|
29 |
def clean(self, value): |
30 |
if not value and self.required: |
31 |
raise forms.ValidationError(self.error_messages['required']) |
32 |
# if value and self.max_choices and len(value) > self.max_choices:
|
33 |
# raise forms.ValidationError('You must select a maximum of %s choice%s.'
|
34 |
# % (apnumber(self.max_choices), pluralize(self.max_choices)))
|
35 |
return value
|
36 |
|
37 |
|
38 |
class MultiSelectField(models.Field): |
39 |
__metaclass__ = models.SubfieldBase |
40 |
|
41 |
def get_internal_type(self): |
42 |
return "CharField" |
43 |
|
44 |
def get_choices_default(self): |
45 |
return self.get_choices(include_blank=False) |
46 |
|
47 |
def _get_FIELD_display(self, field): |
48 |
value = getattr(self, field.attname) |
49 |
choicedict = dict(field.choices)
|
50 |
|
51 |
def formfield(self, **kwargs): |
52 |
# don't call super, as that overrides default widget if it has choices
|
53 |
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), |
54 |
'help_text': self.help_text, 'choices': self.choices} |
55 |
if self.has_default(): |
56 |
defaults['initial'] = self.get_default() |
57 |
defaults.update(kwargs) |
58 |
return MultiSelectFormField(**defaults)
|
59 |
|
60 |
def get_prep_value(self, value): |
61 |
return value
|
62 |
|
63 |
def get_db_prep_value(self, value, connection=None, prepared=False): |
64 |
if isinstance(value, basestring): |
65 |
return value
|
66 |
elif isinstance(value, list): |
67 |
return ",".join(value) |
68 |
|
69 |
def to_python(self, value): |
70 |
if value is not None: |
71 |
return value if isinstance(value, list) else value.split(',') |
72 |
return '' |
73 |
|
74 |
def contribute_to_class(self, cls, name): |
75 |
super(MultiSelectField, self).contribute_to_class(cls, name) |
76 |
if self.choices: |
77 |
func = lambda self, fieldname = name, choicedict = dict(self.choices): ",".join([choicedict.get(value, value) for value in getattr(self, fieldname)]) |
78 |
setattr(cls, 'get_%s_display' % self.name, func) |
79 |
|
80 |
def validate(self, value, model_instance): |
81 |
arr_choices = self.get_choices_selected(self.get_choices_default()) |
82 |
for opt_select in value: |
83 |
if (opt_select not in arr_choices): # the int() here is for comparing with integer choices |
84 |
raise exceptions.ValidationError(self.error_messages['invalid_choice'] % value) |
85 |
return
|
86 |
|
87 |
def get_choices_selected(self, arr_choices=''): |
88 |
if not arr_choices: |
89 |
return False |
90 |
list = [] |
91 |
for choice_selected in arr_choices: |
92 |
list.append(choice_selected[0]) |
93 |
return list |
94 |
|
95 |
def value_to_string(self, obj): |
96 |
value = self._get_val_from_obj(obj)
|
97 |
return self.get_db_prep_value(value) |
98 |
|
99 |
# needed for South compatibility
|
100 |
|
101 |
from south.modelsinspector import add_introspection_rules |
102 |
add_introspection_rules([], ["^edumanage\.models\.MultiSelectField"])
|
103 |
|
104 |
|
105 |
|
106 |
|
107 |
ERTYPES = ( |
108 |
(1, 'IdP only' ), |
109 |
(2, 'SP only'), |
110 |
(3, 'IdP and SP'), |
111 |
) |
112 |
|
113 |
RADPROTOS = ( |
114 |
('radius', 'traditional RADIUS over UDP' ), |
115 |
# ('radius-tcp', 'RADIUS over TCP (RFC6613)'),
|
116 |
# ('radius-tls', 'RADIUS over TLS (RFC6614)'),
|
117 |
# ('radius-dtls', 'RADIUS over datagram TLS (RESERVED)'),
|
118 |
) |
119 |
|
120 |
|
121 |
ADDRTYPES = ( |
122 |
('any', 'Default'), |
123 |
('ipv4', 'IPv4 only'), |
124 |
#('ipv6', 'IPv6 only'), # Commented for the time...not yet in use
|
125 |
) |
126 |
|
127 |
RADTYPES = ( |
128 |
('auth', 'Handles Access-Request packets only'), |
129 |
('acct', 'Handles Accounting-Request packets only'), |
130 |
('auth+acct', 'Handles both Access-Request and Accounting-Request packets'), |
131 |
) |
132 |
|
133 |
class Name_i18n(models.Model): |
134 |
'''
|
135 |
Name in a particular language
|
136 |
'''
|
137 |
|
138 |
name = models.CharField(max_length=80)
|
139 |
lang = models.CharField(max_length=5, choices=settings.URL_NAME_LANGS)
|
140 |
content_type = models.ForeignKey(ContentType, blank=True, null=True) |
141 |
object_id = models.PositiveIntegerField(blank=True, null=True) |
142 |
content_object = generic.GenericForeignKey('content_type', 'object_id') |
143 |
|
144 |
def __unicode__(self): |
145 |
return self.name |
146 |
|
147 |
class Meta: |
148 |
verbose_name = "Name (i18n)"
|
149 |
verbose_name_plural = "Names (i18n)"
|
150 |
|
151 |
|
152 |
class Contact(models.Model): |
153 |
'''
|
154 |
Contact
|
155 |
'''
|
156 |
|
157 |
name = models.CharField(max_length=255, db_column='contact_name') |
158 |
email = models.CharField(max_length=80, db_column='contact_email') |
159 |
phone = models.CharField(max_length=80, db_column='contact_phone') |
160 |
|
161 |
def __unicode__(self): |
162 |
return '%s <%s> (%s)' % (self.name, self.email, self.phone) |
163 |
|
164 |
class Meta: |
165 |
verbose_name = "Contact"
|
166 |
verbose_name_plural = "Contacts"
|
167 |
|
168 |
|
169 |
class InstitutionContactPool(models.Model): |
170 |
contact = models.OneToOneField(Contact) |
171 |
institution = models.ForeignKey("Institution")
|
172 |
|
173 |
def __unicode__(self): |
174 |
return u"%s:%s" %(self.contact, self.institution) |
175 |
|
176 |
class Meta: |
177 |
verbose_name = "Instutution Contact (Pool)"
|
178 |
verbose_name_plural = "Instutution Contacts (Pool)"
|
179 |
|
180 |
class URL_i18n(models.Model): |
181 |
'''
|
182 |
URL of a particular type in a particular language
|
183 |
'''
|
184 |
|
185 |
URLTYPES = ( |
186 |
('info', 'Info' ), |
187 |
('policy', 'Policy'), |
188 |
) |
189 |
url = models.CharField(max_length=180, db_column='URL') |
190 |
lang = models.CharField(max_length=5, choices=settings.URL_NAME_LANGS)
|
191 |
urltype = models.CharField(max_length=10, choices=URLTYPES, db_column='type') |
192 |
content_type = models.ForeignKey(ContentType, blank=True, null=True) |
193 |
object_id = models.PositiveIntegerField(blank=True, null=True) |
194 |
content_object = generic.GenericForeignKey('content_type', 'object_id') |
195 |
|
196 |
class Meta: |
197 |
verbose_name = "Url (i18n)"
|
198 |
verbose_name_plural = "Urls (i18n)"
|
199 |
|
200 |
def __unicode__(self): |
201 |
return self.url |
202 |
|
203 |
class InstRealm(models.Model): |
204 |
'''
|
205 |
Realm of an IdP Institution
|
206 |
'''
|
207 |
# accept if instid.ertype: 1 (idp) or 3 (idpsp)
|
208 |
realm = models.CharField(max_length=160)
|
209 |
instid = models.ForeignKey("Institution",verbose_name="Institution") |
210 |
proxyto = models.ManyToManyField("InstServer", help_text=_("Only IdP and IdP/SP server types are allowed")) |
211 |
|
212 |
class Meta: |
213 |
verbose_name = "Institution Realm"
|
214 |
verbose_name_plural = "Institutions' Realms"
|
215 |
|
216 |
def __unicode__(self): |
217 |
return '%s' % self.realm |
218 |
|
219 |
|
220 |
def get_servers(self): |
221 |
return ",".join(["%s"%x for x in self.proxyto.all()]) |
222 |
|
223 |
|
224 |
class InstServer(models.Model): |
225 |
'''
|
226 |
Server of an Institution
|
227 |
'''
|
228 |
instid = models.ForeignKey("Institution")
|
229 |
ertype = models.PositiveIntegerField(max_length=1, choices=ERTYPES, db_column='type') |
230 |
# ertype:
|
231 |
# 1: accept if instid.ertype: 1 (idp) or 3 (idpsp)
|
232 |
# 2: accept if instid.ertype: 2 (sp) or 3 (idpsp)
|
233 |
# 3: accept if instid.ertype: 3 (idpsp)
|
234 |
|
235 |
# hostname/ipaddr or descriptive label of server
|
236 |
name = models.CharField(max_length=80, help_text=_("Descriptive label"), null=True, blank=True) # ** (acts like a label) |
237 |
# hostname/ipaddr of server, overrides name
|
238 |
addr_type = models.CharField(max_length=16, choices=ADDRTYPES, default='ipv4') |
239 |
host = models.CharField(max_length=80, help_text=_("IP address | FQDN hostname")) # Handling with FQDN parser or ipaddr (google lib) * !!! Add help text to render it in template (mandatory, unique) |
240 |
#TODO: Add description field or label field
|
241 |
# accept if type: 1 (idp) or 3 (idpsp) (for the folowing 4 fields)
|
242 |
rad_pkt_type = models.CharField(max_length=48, choices=RADTYPES, default='auth+acct', null=True, blank=True,) |
243 |
auth_port = models.PositiveIntegerField(max_length=5, null=True, blank=True, default=1812, help_text=_("Default for RADIUS: 1812")) # TODO: Also ignore while exporting XML |
244 |
acct_port = models.PositiveIntegerField(max_length=5, null=True, blank=True, default=1813, help_text=_("Default for RADIUS: 1813")) |
245 |
status_server = models.BooleanField(help_text=_("Do you accept Status-Server requests?"))
|
246 |
|
247 |
secret = models.CharField(max_length=80)
|
248 |
proto = models.CharField(max_length=12, choices=RADPROTOS, default='radius') |
249 |
ts = models.DateTimeField(auto_now=True)
|
250 |
|
251 |
class Meta: |
252 |
verbose_name = "Institution Server"
|
253 |
verbose_name_plural = "Institutions' Servers"
|
254 |
|
255 |
def __unicode__(self): |
256 |
return _('Server: %(servername)s, Type: %(ertype)s') % { |
257 |
# but name is many-to-many from institution
|
258 |
#'inst': self.instid,
|
259 |
'servername': self.get_name(), |
260 |
# the human-readable name would be nice here
|
261 |
'ertype': self.get_ertype_display(), |
262 |
} |
263 |
|
264 |
def get_name(self): |
265 |
if self.name: |
266 |
return self.name |
267 |
return self.host |
268 |
|
269 |
|
270 |
class InstRealmMon(models.Model): |
271 |
'''
|
272 |
Realm of an IdP Institution to be monitored
|
273 |
'''
|
274 |
|
275 |
MONTYPES = ( |
276 |
('localauthn', 'Institution provides account for the NRO to monitor the realm' ), |
277 |
#('loopback', 'Institution proxies the realm back to the NRO'),
|
278 |
) |
279 |
|
280 |
realm = models.ForeignKey(InstRealm) |
281 |
mon_type = models.CharField(max_length=16, choices=MONTYPES)
|
282 |
|
283 |
class Meta: |
284 |
unique_together = ('realm','mon_type') |
285 |
verbose_name = "Institution Monitored Realm"
|
286 |
verbose_name_plural = "Institution Monitored Realms"
|
287 |
|
288 |
def __unicode__(self): |
289 |
return "%s-%s" %(self.realm.realm, self.mon_type) |
290 |
# def __unicode__(self):
|
291 |
# return _('Institution: %(inst)s, Monitored Realm: %(monrealm)s, Monitoring Type: %(montype)s') % {
|
292 |
# # but name is many-to-many from institution
|
293 |
# 'inst': self.instid.name,
|
294 |
# 'monrealm': self.realm,
|
295 |
# 'montype': self.mon_type,
|
296 |
# }
|
297 |
|
298 |
class MonProxybackClient(models.Model): |
299 |
'''
|
300 |
Server of an Institution that will be proxying back requests for a monitored realm
|
301 |
'''
|
302 |
|
303 |
instrealmmonid = models.ForeignKey("InstRealmMon")
|
304 |
# hostname/ipaddr or descriptive label of server
|
305 |
name = models.CharField(max_length=80, help_text=_("Descriptive label"), null=True, blank=True) # ** (acts like a label) |
306 |
# hostname/ipaddr of server, overrides name
|
307 |
host = models.CharField(max_length=80, help_text=_("IP address | FQDN hostname")) # Handling with FQDN parser or ipaddr (google lib) * !!! Add help text to render it in template (mandatory, unique) |
308 |
status_server = models.BooleanField() |
309 |
secret = models.CharField(max_length=80)
|
310 |
proto = models.CharField(max_length=12, choices=RADPROTOS)
|
311 |
ts = models.DateTimeField(auto_now=True)
|
312 |
|
313 |
class Meta: |
314 |
verbose_name = "Instituion Proxyback Client"
|
315 |
verbose_name_plural = "Instituion Proxyback Clients"
|
316 |
|
317 |
def __unicode__(self): |
318 |
return _('Monitored Realm: %(monrealm)s, Proxyback Client: %(servername)s') % { |
319 |
# but name is many-to-many from institution
|
320 |
'monrealm': self.instrealmmonid.realm, |
321 |
'servername': self.name, |
322 |
} |
323 |
|
324 |
class MonLocalAuthnParam(models.Model): |
325 |
'''
|
326 |
Parameters for an old-style monitored realm
|
327 |
'''
|
328 |
|
329 |
EAPTYPES = ( |
330 |
('PEAP', 'EAP-PEAP' ), |
331 |
('TTLS', 'EAP-TTLS'), |
332 |
# ('TLS', 'EAP-TLS'),
|
333 |
) |
334 |
EAP2TYPES = ( |
335 |
('PAP', 'PAP' ), |
336 |
('CHAP', 'CHAP'), |
337 |
('MS-CHAPv2', 'MS-CHAPv2'), |
338 |
) |
339 |
# MONRESPTYPES = (
|
340 |
# ('accept', 'Access-Accept expected' ),
|
341 |
# ('reject', 'Access-Reject expected'),
|
342 |
# ('both', 'RESERVED'),
|
343 |
# )
|
344 |
|
345 |
instrealmmonid = models.OneToOneField("InstRealmMon")
|
346 |
eap_method = models.CharField(max_length=16, choices=EAPTYPES)
|
347 |
phase2 = models.CharField(max_length=16, choices=EAP2TYPES)
|
348 |
# only local-part, no realm
|
349 |
username = models.CharField(max_length=36)
|
350 |
passwp = models.CharField(max_length=80, db_column='pass') |
351 |
# TODO: In next releast change it to TextField and add a key field
|
352 |
#cert = models.CharField(max_length=32)
|
353 |
#exp_response = models.CharField(max_length=6, choices=MONRESPTYPES)
|
354 |
|
355 |
class Meta: |
356 |
verbose_name = "Monitored Realm (local authn)"
|
357 |
verbose_name_plural = "Monitored Realms (local authn)"
|
358 |
|
359 |
def __unicode__(self): |
360 |
return _('Monitored Realm: %(monrealm)s, EAP Method: %(eapmethod)s, Phase 2: %(phase2)s, Username: %(username)s') % { |
361 |
# but name is many-to-many from institution
|
362 |
'monrealm': self.instrealmmonid.realm, |
363 |
'eapmethod': self.eap_method, |
364 |
'phase2': self.phase2, |
365 |
'username': self.username, |
366 |
} |
367 |
|
368 |
class ServiceLoc(models.Model): |
369 |
'''
|
370 |
Service Location of an SP/IdPSP Institution
|
371 |
'''
|
372 |
|
373 |
ENCTYPES = ( |
374 |
('WPA/TKIP', 'WPA-TKIP' ), |
375 |
('WPA/AES', 'WPA-AES'), |
376 |
('WPA2/TKIP', 'WPA2-TKIP'), |
377 |
('WPA2/AES', 'WPA2-AES'), |
378 |
) |
379 |
|
380 |
# accept if institutionid.ertype: 2 (sp) or 3 (idpsp)
|
381 |
institutionid = models.ForeignKey("Institution", verbose_name="Institution") |
382 |
longitude = models.DecimalField(max_digits=12, decimal_places=8) |
383 |
latitude = models.DecimalField(max_digits=12, decimal_places=8) |
384 |
# TODO: multiple names can be specified [...] name in English is required
|
385 |
loc_name = generic.GenericRelation(Name_i18n) |
386 |
address_street = models.CharField(max_length=96)
|
387 |
address_city = models.CharField(max_length=64)
|
388 |
contact = models.ManyToManyField(Contact, blank=True, null=True) |
389 |
SSID = models.CharField(max_length=16)
|
390 |
enc_level = MultiSelectField(max_length=64, choices=ENCTYPES, blank=True, null=True) |
391 |
port_restrict = models.BooleanField() |
392 |
transp_proxy = models.BooleanField() |
393 |
IPv6 = models.BooleanField() |
394 |
NAT = models.BooleanField() |
395 |
AP_no = models.PositiveIntegerField(max_length=3)
|
396 |
wired = models.BooleanField() |
397 |
# only urltype = 'info' should be accepted here
|
398 |
url = generic.GenericRelation(URL_i18n, blank=True, null=True) |
399 |
ts = models.DateTimeField(auto_now=True)
|
400 |
|
401 |
class Meta: |
402 |
verbose_name = "Service Location"
|
403 |
verbose_name_plural = "Service Locations"
|
404 |
|
405 |
def __unicode__(self): |
406 |
return _('Institution: %(inst)s, Service Location: %(locname)s') % { |
407 |
# but name is many-to-many from institution
|
408 |
'inst': self.institutionid, |
409 |
# but locname is many-to-many
|
410 |
'locname': self.get_name(), |
411 |
} |
412 |
|
413 |
|
414 |
def get_name(self, lang=None): |
415 |
name = ', '.join([i.name for i in self.loc_name.all()]) |
416 |
if not lang: |
417 |
return name
|
418 |
else:
|
419 |
try:
|
420 |
name = self.loc_name.get(lang=lang)
|
421 |
return name
|
422 |
except Exception as e: |
423 |
return name
|
424 |
get_name.short_description = 'Location Name'
|
425 |
|
426 |
|
427 |
class Institution(models.Model): |
428 |
'''
|
429 |
Institution
|
430 |
'''
|
431 |
|
432 |
realmid = models.ForeignKey("Realm")
|
433 |
org_name = generic.GenericRelation(Name_i18n) |
434 |
ertype = models.PositiveIntegerField(max_length=1, choices=ERTYPES, db_column='type') |
435 |
|
436 |
def __unicode__(self): |
437 |
return "%s" % ', '.join([i.name for i in self.org_name.all()]) |
438 |
|
439 |
|
440 |
def get_name(self, lang=None): |
441 |
name = ', '.join([i.name for i in self.org_name.all()]) |
442 |
if not lang: |
443 |
return name
|
444 |
else:
|
445 |
try:
|
446 |
name = self.org_name.get(lang=lang)
|
447 |
return name
|
448 |
except Exception as e: |
449 |
return name
|
450 |
|
451 |
def get_active_cat_enrl(self): |
452 |
urls = [] |
453 |
active_cat_enrl = self.catenrollment_set.filter(url='ACTIVE', cat_instance='production') |
454 |
for catenrl in active_cat_enrl: |
455 |
if catenrl.cat_configuration_url:
|
456 |
urls.append(catenrl.cat_configuration_url) |
457 |
return urls
|
458 |
|
459 |
|
460 |
class InstitutionDetails(models.Model): |
461 |
'''
|
462 |
Institution Details
|
463 |
'''
|
464 |
institution = models.OneToOneField(Institution) |
465 |
# TODO: multiple names can be specified [...] name in English is required
|
466 |
address_street = models.CharField(max_length=96)
|
467 |
address_city = models.CharField(max_length=64)
|
468 |
contact = models.ManyToManyField(Contact) |
469 |
url = generic.GenericRelation(URL_i18n) |
470 |
# accept if ertype: 2 (sp) or 3 (idpsp) (Applies to the following field)
|
471 |
oper_name = models.CharField(max_length=24, null=True, blank=True, help_text=_('The primary, registered domain name for your institution, eg. example.com.<br>This is used to derive the Operator-Name attribute according to RFC5580, par.4.1, using the REALM namespace.')) |
472 |
# accept if ertype: 1 (idp) or 3 (idpsp) (Applies to the following field)
|
473 |
number_user = models.PositiveIntegerField(max_length=6, null=True, blank=True, help_text=_("Number of users (individuals) that are eligible to participate in eduroam service")) |
474 |
number_id = models.PositiveIntegerField(max_length=6, null=True, blank=True, help_text=_("Number of issued e-identities (credentials) that may be used for authentication in eduroam service")) |
475 |
ts = models.DateTimeField(auto_now=True)
|
476 |
|
477 |
class Meta: |
478 |
verbose_name = "Institutions' Details"
|
479 |
verbose_name_plural = "Institution Details"
|
480 |
|
481 |
def __unicode__(self): |
482 |
return _('Institution: %(inst)s, Type: %(ertype)s') % { |
483 |
# but name is many-to-many from institution
|
484 |
'inst': ', '.join([i.name for i in self.institution.org_name.all()]), |
485 |
'ertype': self.institution.get_ertype_display(), |
486 |
} |
487 |
def get_inst_name(self): |
488 |
return join([i.name for i in self.institution.org_name.all()]) |
489 |
get_inst_name.short_description = "Institution Name"
|
490 |
|
491 |
|
492 |
|
493 |
class Realm(models.Model): |
494 |
'''
|
495 |
Realm
|
496 |
'''
|
497 |
|
498 |
country = models.CharField(max_length=5, choices=settings.REALM_COUNTRIES)
|
499 |
stype = models.PositiveIntegerField(max_length=1, default=0, editable=False) |
500 |
# TODO: multiple names can be specified [...] name in English is required
|
501 |
org_name = generic.GenericRelation(Name_i18n) |
502 |
address_street = models.CharField(max_length=32)
|
503 |
address_city = models.CharField(max_length=24)
|
504 |
contact = models.ManyToManyField(Contact) |
505 |
url = generic.GenericRelation(URL_i18n) |
506 |
ts = models.DateTimeField(auto_now=True)
|
507 |
|
508 |
class Meta: |
509 |
verbose_name = "Realm"
|
510 |
verbose_name_plural = "Realms"
|
511 |
|
512 |
def __unicode__(self): |
513 |
return _('Country: %(country)s, NRO: %(orgname)s') % { |
514 |
# but name is many-to-many from institution
|
515 |
'orgname': ', '.join([i.name for i in self.org_name.all()]), |
516 |
'country': self.country, |
517 |
} |
518 |
|
519 |
|
520 |
# TODO: this represents a *database view* "realm_data", find a better way to write it
|
521 |
class RealmData(models.Model): |
522 |
'''
|
523 |
Realm statistics
|
524 |
'''
|
525 |
|
526 |
realmid = models.OneToOneField(Realm) |
527 |
# db: select count(institution.id) as number_inst from institution, realm where institution.realmid == realm.realmid
|
528 |
number_inst = models.PositiveIntegerField(max_length=5, editable=False) |
529 |
# db: select sum(institution.number_user) as number_user from institution, realm where institution.realmid == realm.realmid
|
530 |
number_user = models.PositiveIntegerField(max_length=9, editable=False) |
531 |
# db: select sum(institution.number_id) as number_id from institution, realm where institution.realmid == realm.realmid
|
532 |
number_id = models.PositiveIntegerField(max_length=9, editable=False) |
533 |
# db: select count(institution.id) as number_IdP from institution, realm where institution.realmid == realm.realmid and institution.type == 1
|
534 |
number_IdP = models.PositiveIntegerField(max_length=5, editable=False) |
535 |
# db: select count(institution.id) as number_SP from institution, realm where institution.realmid == realm.realmid and institution.type == 2
|
536 |
number_SP = models.PositiveIntegerField(max_length=5, editable=False) |
537 |
# db: select count(institution.id) as number_IdPSP from institution, realm where institution.realmid == realm.realmid and institution.type == 3
|
538 |
number_IdPSP = models.PositiveIntegerField(max_length=5, editable=False) |
539 |
# db: select greatest(max(realm.ts), max(institution.ts)) as ts from institution, realm where institution.realmid == realm.realmid
|
540 |
ts = models.DateTimeField(editable=False)
|
541 |
|
542 |
def __unicode__(self): |
543 |
return _('Country: %(country)s, NRO: %(orgname)s, Institutions: %(inst)s, IdPs: %(idp)s, SPs: %(sp)s, IdPSPs: %(idpsp)s, Users: %(numuser)s, Identities: %(numid)s') % { |
544 |
# but name is many-to-many from institution
|
545 |
'orgname': self.org_name, |
546 |
'country': self.country, |
547 |
'inst': self.number_inst, |
548 |
'idp': self.number_IdP, |
549 |
'sp': self.number_SP, |
550 |
'idpsp': self.number_IdPSP, |
551 |
'numuser': self.number_user, |
552 |
'numid': self.number_id, |
553 |
} |
554 |
|
555 |
|
556 |
class CatEnrollment(models.Model): |
557 |
''' Eduroam CAT enrollment '''
|
558 |
|
559 |
ACTIVE = u"ACTIVE"
|
560 |
|
561 |
cat_inst_id = models.PositiveIntegerField(max_length=10)
|
562 |
inst = models.ForeignKey(Institution) |
563 |
url = models.CharField(max_length=255, blank=True, null=True, help_text="Set to ACTIVE if institution has CAT profiles") |
564 |
cat_instance = models.CharField(max_length=50, choices=settings.CAT_INSTANCES)
|
565 |
applier = models.ForeignKey(User) |
566 |
ts = models.DateTimeField(auto_now=True)
|
567 |
|
568 |
class Meta: |
569 |
unique_together = ['inst', 'cat_instance'] |
570 |
|
571 |
def __unicode__(self): |
572 |
return "%s: %s" % (self.cat_inst_id, ', '.join([i.name for i in self.inst.org_name.all()])) |
573 |
|
574 |
def cat_active(self): |
575 |
return self.url == self.ACTIVE |
576 |
|
577 |
def cat_configuration_url(self): |
578 |
if self.cat_active(): |
579 |
try:
|
580 |
return "%s?idp=%s"%(settings.CAT_AUTH[self.cat_instance]['CAT_PROFILES_URL'],self.cat_inst_id) |
581 |
except:
|
582 |
return False |
583 |
return False |
584 |
|
585 |
cat_active.boolean = True
|
586 |
cat_active.short_description = "CAT profiles"
|
587 |
|
588 |
|
589 |
|