From 18f8e140cd3f03b7913cbb83ec757ac741045a7d Mon Sep 17 00:00:00 2001 From: Josue Kouka Date: Wed, 9 Dec 2015 18:35:24 +0100 Subject: [PATCH] Model/Data migrations of LibertyServiceProvider to LibertyProvider (#8826) --- src/authentic2/idp/saml/backend.py | 13 +++--- src/authentic2/idp/saml/saml2_endpoints.py | 8 ++-- src/authentic2/saml/admin.py | 19 ++++---- src/authentic2/saml/common.py | 17 +++----- src/authentic2/saml/forms.py | 12 +----- .../saml/management/commands/sync-metadata.py | 11 +++-- .../saml/migrations/0017_auto_20151208_1537.py | 45 +++++++++++++++++++ .../saml/migrations/0018_auto_20151208_1542.py | 32 ++++++++++++++ .../saml/migrations/0019_libertyfederation_nsp.py | 20 +++++++++ .../saml/migrations/0020_auto_20151221_1108.py | 27 ++++++++++++ .../saml/migrations/0021_auto_20151221_1149.py | 35 +++++++++++++++ src/authentic2/saml/models.py | 50 +++++++--------------- tests/test_idp_saml2.py | 4 -- 13 files changed, 208 insertions(+), 85 deletions(-) create mode 100644 src/authentic2/saml/migrations/0017_auto_20151208_1537.py create mode 100644 src/authentic2/saml/migrations/0018_auto_20151208_1542.py create mode 100644 src/authentic2/saml/migrations/0019_libertyfederation_nsp.py create mode 100644 src/authentic2/saml/migrations/0020_auto_20151221_1108.py create mode 100644 src/authentic2/saml/migrations/0021_auto_20151221_1149.py diff --git a/src/authentic2/idp/saml/backend.py b/src/authentic2/idp/saml/backend.py index 73324c3..b3965e1 100644 --- a/src/authentic2/idp/saml/backend.py +++ b/src/authentic2/idp/saml/backend.py @@ -19,7 +19,7 @@ class SamlBackend(object): self.logger = logging.getLogger(__name__) def service_list(self, request): - q = models.LibertyServiceProvider.objects.filter(enabled = True) \ + q = models.LibertyProvider.objects.filter(enabled = True) \ .select_related() ls = [] sessions = models.LibertySession.objects.filter( @@ -36,15 +36,14 @@ class SamlBackend(object): sp_options_policy__idp_initiated_sso=True)) queries.append(q.filter(sp_options_policy__enabled=True, sp_options_policy__accept_slo=True, - liberty_provider__entity_id__in=sessions_eids)) + entity_id__in=sessions_eids)) if default_policy and default_policy.idp_initiated_sso: queries.append(q.filter(sp_options_policy__isnull=True)) if default_policy and default_policy.accept_slo: queries.append(q.filter(sp_options_policy__isnull=True, - liberty_provider__entity_id__in=sessions_eids)) + entity_id__in=sessions_eids)) qs = reduce(operator.__or__, queries) - for service_provider in qs: - liberty_provider = service_provider.liberty_provider + for liberty_provider in qs: policy = common.get_sp_options_policy(liberty_provider) if policy: actions = [] @@ -134,8 +133,8 @@ class SamlBackend(object): 'url': url, } qs = models.LibertyProvider.objects - qs = qs.filter(service_provider__users_can_manage_federations=True) - qs = qs.exclude(service_provider__libertyfederation__in=federations) + qs = qs.filter(users_can_manage_federations=True) + qs = qs.exclude(libertyfederation__in=federations) qs = qs.select_related() for liberty_provider in qs: url = reverse('a2-idp-saml2-idp-sso') diff --git a/src/authentic2/idp/saml/saml2_endpoints.py b/src/authentic2/idp/saml/saml2_endpoints.py index a4d00f5..7821368 100644 --- a/src/authentic2/idp/saml/saml2_endpoints.py +++ b/src/authentic2/idp/saml/saml2_endpoints.py @@ -48,7 +48,7 @@ from authentic2.saml.models import (LibertyArtifact, LibertySession, LibertyFederation, nameid2kwargs, saml2_urn_to_nidformat, nidformat_to_saml2_urn, save_key_values, get_and_delete_key_values, - LibertyProvider, LibertyServiceProvider, SAMLAttribute, NAME_ID_FORMATS) + LibertyProvider, SAMLAttribute, NAME_ID_FORMATS) from authentic2.saml.common import redirect_next, asynchronous_bindings, \ soap_bindings, load_provider, get_saml2_request_message, \ error_page, set_saml2_response_responder_status_code, \ @@ -381,8 +381,8 @@ def build_assertion(request, login, nid_format='transient'): kwargs['name_id_qualifier'] = AUTHENTIC_SAME_ID_SENTINEL if kwargs.get('name_id_sp_name_qualifier') == login.remoteProviderId: kwargs['name_id_sp_name_qualifier'] = AUTHENTIC_SAME_ID_SENTINEL - service_provider = LibertyServiceProvider.objects \ - .get(liberty_provider__entity_id=login.remoteProviderId) + service_provider = LibertyProvider.objects \ + .get(entity_id=login.remoteProviderId) federation, new = LibertyFederation.objects.get_or_create( sp=service_provider, user=request.user, **kwargs) @@ -774,7 +774,7 @@ def sso_after_process_request(request, login, consent_obtained=False, try: LibertyFederation.objects.get( user=request.user, - sp__liberty_provider__entity_id=login.remoteProviderId) + sp__entity_id=login.remoteProviderId) logger.debug('consent already ' 'given (existing federation) for %s' % login.remoteProviderId) consent_obtained = True diff --git a/src/authentic2/saml/admin.py b/src/authentic2/saml/admin.py index c095fdf..67b618f 100644 --- a/src/authentic2/saml/admin.py +++ b/src/authentic2/saml/admin.py @@ -13,9 +13,9 @@ try: except ImportError: from django.contrib.contenttypes.generic import GenericTabularInline -from authentic2.saml.models import (LibertyProvider, LibertyServiceProvider, - SPOptionsIdPPolicy, LibertyFederation, - KeyValue, LibertySession, SAMLAttribute) +from authentic2.saml.models import (LibertyProvider, SPOptionsIdPPolicy, + LibertyFederation, KeyValue, + LibertySession, SAMLAttribute) from authentic2.decorators import to_iter from authentic2.attributes_ng.engine import get_attribute_names @@ -24,8 +24,6 @@ from . import admin_views logger = logging.getLogger(__name__) -class LibertyServiceProviderInline(admin.StackedInline): - model = LibertyServiceProvider class TextAndFileWidget(forms.widgets.MultiWidget): def __init__(self, attrs=None): @@ -145,21 +143,24 @@ class LibertyProviderAdmin(admin.ModelAdmin): readonly_fields = ('entity_id','protocol_conformance','entity_id_sha1','federation_source') fieldsets = ( (None, { - 'fields' : ('name', 'slug', 'ou', 'entity_id', 'entity_id_sha1','federation_source') + 'fields' : ('name', 'slug', 'ou', 'entity_id', 'entity_id_sha1','federation_source', 'enabled', ) }), (_('Metadata files'), { 'fields': ('metadata_url', 'metadata', 'public_key', 'ssl_certificate', 'ca_cert_chain') }), + (_('Policy'), { + 'fields': ('enable_following_sp_options_policy', 'sp_options_policy', + 'users_can_manage_federations') + }), ) inlines = [ - LibertyServiceProviderInline, SAMLAttributeInlineAdmin, ] actions = [ update_metadata ] prepopulated_fields = {'slug': ('name',)} list_filter = ( - 'service_provider__sp_options_policy', - 'service_provider__enabled', + 'sp_options_policy', + 'enabled', ) def get_urls(self): diff --git a/src/authentic2/saml/common.py b/src/authentic2/saml/common.py index c5a22aa..a6c54e3 100644 --- a/src/authentic2/saml/common.py +++ b/src/authentic2/saml/common.py @@ -15,7 +15,7 @@ from django.shortcuts import render_to_response from django.core.exceptions import ValidationError from authentic2.saml.models import (LibertyFederation, LibertyProvider, - LibertyServiceProvider, SPOptionsIdPPolicy) + SPOptionsIdPPolicy) from authentic2.saml import models from authentic2.saml import saml2utils @@ -338,8 +338,6 @@ def retrieve_metadata_and_create(request, provider_id, sp_or_idp): return None p.save() logger.debug('%s saved', p) - s = LibertyServiceProvider(liberty_provider=p, enabled=True) - s.save() return p @@ -368,11 +366,8 @@ def load_provider(request, entity_id, server=None, sp_or_idp='sp', return False else: return False - try: - service_provider = liberty_provider.service_provider - except LibertyServiceProvider.DoesNotExist: - return False - if not service_provider.enabled: + + if not liberty_provider.enabled: return False if server: server.addProviderFromBuffer(lasso.PROVIDER_ROLE_SP, @@ -566,10 +561,10 @@ def get_sp_options_policy(provider): policy = get_sp_options_policy_all() if policy: return policy - if provider.service_provider.enable_following_sp_options_policy: - policy = provider.service_provider.sp_options_policy + if provider.enable_following_sp_options_policy: + policy = provider.sp_options_policy if policy and policy.enabled: - return provider.service_provider.sp_options_policy + return provider.sp_options_policy return get_sp_options_policy_default() diff --git a/src/authentic2/saml/forms.py b/src/authentic2/saml/forms.py index 81aa5e5..24f2bd4 100644 --- a/src/authentic2/saml/forms.py +++ b/src/authentic2/saml/forms.py @@ -6,7 +6,7 @@ from django import forms from django.core.exceptions import ValidationError from django.utils.translation import ugettext_lazy as _ -from .models import LibertyProvider, LibertyServiceProvider +from .models import LibertyProvider from authentic2.a2_rbac.utils import get_default_ou @@ -26,7 +26,6 @@ class AddLibertyProviderFromUrlForm(forms.Form): url = cleaned_data.get('url') ou = cleaned_data.get('ou') self.instance = None - self.childs = [] if name and slug and url: try: content = urllib2.urlopen(url).read().decode('utf-8') @@ -40,9 +39,6 @@ class AddLibertyProviderFromUrlForm(forms.Form): slug=slug, metadata=content, metadata_url=url, ou=ou) liberty_provider.full_clean(exclude= ('entity_id', 'protocol_conformance')) - self.childs.append(LibertyServiceProvider( - liberty_provider=liberty_provider, - enabled=True)) except ValidationError, e: raise except Exception, e: @@ -51,9 +47,5 @@ class AddLibertyProviderFromUrlForm(forms.Form): return cleaned_data def save(self): - if not self.instance is None: - self.instance.save() - for child in self.childs: - child.liberty_provider = self.instance - child.save() + self.instance.save() return self.instance diff --git a/src/authentic2/saml/management/commands/sync-metadata.py b/src/authentic2/saml/management/commands/sync-metadata.py index 243599a..6d408dd 100644 --- a/src/authentic2/saml/management/commands/sync-metadata.py +++ b/src/authentic2/saml/management/commands/sync-metadata.py @@ -177,12 +177,11 @@ def load_one_entity(tree, options, sp_policy=None, afp=None): provider.save() options['count'] = options.get('count', 0) + 1 if sp: - service_provider, created = LibertyServiceProvider.objects.get_or_create( - liberty_provider=provider, - defaults={'enabled': not options['create-disabled']}) + provider.enabled = option['create-disabled'] + provider.save() if sp_policy: - service_provider.sp_options_policy = sp_policy - service_provider.save() + provider.sp_options_policy = sp_policy + provider.save() pks = [] if options['load_attribute_consuming_service']: load_acs(tree, provider, pks, verbosity) @@ -211,7 +210,7 @@ def load_one_entity(tree, options, sp_policy=None, afp=None): SAMLAttribute.objects.for_generic_object(provider).exclude(pk__in=pks).delete() class Command(BaseCommand): - '''Load SAMLv2 metadata file into the LibertyProvider, LibertyServiceProvider + '''Load SAMLv2 metadata file into the LibertyProvider, and LibertyIdentityProvider files''' can_import_django_settings = True output_transaction = True diff --git a/src/authentic2/saml/migrations/0017_auto_20151208_1537.py b/src/authentic2/saml/migrations/0017_auto_20151208_1537.py new file mode 100644 index 0000000..04e7dd6 --- /dev/null +++ b/src/authentic2/saml/migrations/0017_auto_20151208_1537.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('saml', '0016_auto_20150915_2041'), + ] + + operations = [ + migrations.AddField( + model_name='libertyprovider', + name='enable_following_sp_options_policy', + field=models.BooleanField(default=False, verbose_name='The following options policy will apply except if a policy for all service provider is defined.'), + preserve_default=True, + ), + migrations.AddField( + model_name='libertyprovider', + name='enabled', + field=models.BooleanField(default=False, db_index=True, verbose_name='Enabled'), + preserve_default=True, + ), + migrations.AddField( + model_name='libertyprovider', + name='sp_options_policy', + field=models.ForeignKey(related_name='sp_options_policy', on_delete=django.db.models.deletion.SET_NULL, verbose_name='service provider options policy', blank=True, to='saml.SPOptionsIdPPolicy', null=True), + preserve_default=True, + ), + migrations.AddField( + model_name='libertyprovider', + name='users_can_manage_federations', + field=models.BooleanField(default=True, db_index=True, verbose_name='users can manage federation'), + preserve_default=True, + ), + migrations.AlterField( + model_name='libertyserviceprovider', + name='sp_options_policy', + field=models.ForeignKey(related_name='old_isp_options_policy', on_delete=django.db.models.deletion.SET_NULL, verbose_name='service provider options policy', blank=True, to='saml.SPOptionsIdPPolicy', null=True), + preserve_default=True, + ), + ] diff --git a/src/authentic2/saml/migrations/0018_auto_20151208_1542.py b/src/authentic2/saml/migrations/0018_auto_20151208_1542.py new file mode 100644 index 0000000..c1e755a --- /dev/null +++ b/src/authentic2/saml/migrations/0018_auto_20151208_1542.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + +def noop(apps, schema_editor): + pass + +def liberty_service_provider_data_to_liberty_provider(apps, schema_editor): + LibertyProvider = apps.get_model('saml','LibertyProvider') + LibertyServiceProvider = apps.get_model('saml','LibertyServiceProvider') + + for lsp in LibertyServiceProvider.objects.all(): + lp = lsp.liberty_provider + + lp.enabled = lsp.enabled + lp.enable_following_sp_options_policy = lsp.enable_following_sp_options_policy + lp.sp_options_policy = lsp.sp_options_policy + lp.users_can_manage_federations = lsp.users_can_manage_federations + lp.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('saml', '0017_auto_20151208_1537'), + ] + + operations = [ + migrations.RunPython(liberty_service_provider_data_to_liberty_provider), + migrations.RunPython(noop) + ] diff --git a/src/authentic2/saml/migrations/0019_libertyfederation_nsp.py b/src/authentic2/saml/migrations/0019_libertyfederation_nsp.py new file mode 100644 index 0000000..b66c7f7 --- /dev/null +++ b/src/authentic2/saml/migrations/0019_libertyfederation_nsp.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('saml', '0018_auto_20151208_1542'), + ] + + operations = [ + migrations.AddField( + model_name='libertyfederation', + name='nsp', + field=models.ForeignKey(blank=True, to='saml.LibertyProvider', null=True), + preserve_default=True, + ), + ] diff --git a/src/authentic2/saml/migrations/0020_auto_20151221_1108.py b/src/authentic2/saml/migrations/0020_auto_20151221_1108.py new file mode 100644 index 0000000..356d7e5 --- /dev/null +++ b/src/authentic2/saml/migrations/0020_auto_20151221_1108.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + +def noop(apps, schema_editor): + pass + +def liberty_federation_sp_to_nsp(apps, schema_editor): + LibertyFederation = apps.get_model('saml','LibertyFederation') + LibertyProvider = apps.get_model('saml','LibertyProvider') + for liberty_federation in LibertyFederation.objects.all(): + liberty_provider = LibertyProvider.objects.get(pk=liberty_federation.sp.pk) + liberty_federation.nsp = liberty_provider + liberty_federation.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('saml', '0019_libertyfederation_nsp'), + ] + + operations = [ + migrations.RunPython(liberty_federation_sp_to_nsp), + migrations.RunPython(noop) + ] diff --git a/src/authentic2/saml/migrations/0021_auto_20151221_1149.py b/src/authentic2/saml/migrations/0021_auto_20151221_1149.py new file mode 100644 index 0000000..8ba5099 --- /dev/null +++ b/src/authentic2/saml/migrations/0021_auto_20151221_1149.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('saml', '0020_auto_20151221_1108'), + ] + + operations = [ + migrations.RemoveField( + model_name='libertyserviceprovider', + name='liberty_provider', + ), + migrations.RemoveField( + model_name='libertyserviceprovider', + name='sp_options_policy', + ), + migrations.RemoveField( + model_name='libertyfederation', + name='nsp', + ), + migrations.AlterField( + model_name='libertyfederation', + name='sp', + field=models.ForeignKey(blank=True, to='saml.LibertyProvider', null=True), + preserve_default=True, + ), + migrations.DeleteModel( + name='LibertyServiceProvider', + ), + ] diff --git a/src/authentic2/saml/models.py b/src/authentic2/saml/models.py index b229583..f12e58e 100644 --- a/src/authentic2/saml/models.py +++ b/src/authentic2/saml/models.py @@ -319,6 +319,21 @@ class LibertyProvider(Service): ssl_certificate = models.TextField(blank=True) ca_cert_chain = models.TextField(blank=True) federation_source = models.CharField(max_length=64, blank=True, null=True) + enabled = models.BooleanField(verbose_name = _('Enabled'), + default=False, db_index=True) + enable_following_sp_options_policy = models.BooleanField(verbose_name = \ + _('The following options policy will apply except if a policy for all service provider is defined.'), + default=False) + sp_options_policy = models.ForeignKey(SPOptionsIdPPolicy, + related_name="sp_options_policy", + verbose_name=_('service provider options policy'), blank=True, + null=True, + on_delete=models.SET_NULL) + users_can_manage_federations = models.BooleanField( + verbose_name=_('users can manage federation'), + default=True, + blank=True, + db_index=True) attributes = GenericRelation(SAMLAttribute) @@ -379,39 +394,6 @@ def get_all_custom_or_default(instance, name): except ObjectDoesNotExist: raise RuntimeError('Default %s is missing' % model) -# TODO: The IdP must look to the preferred binding order for sso in the SP metadata (AssertionConsumerService) -# expect if the protocol for response is defined in the request (ProtocolBinding attribute) -class LibertyServiceProvider(models.Model): - liberty_provider = models.OneToOneField(LibertyProvider, - primary_key = True, related_name = 'service_provider') - enabled = models.BooleanField(verbose_name = _('Enabled'), - default=False, db_index=True) - enable_following_sp_options_policy = models.BooleanField(verbose_name = \ - _('The following options policy will apply except if a policy for all service provider is defined.'), - default=False) - sp_options_policy = models.ForeignKey(SPOptionsIdPPolicy, - related_name="sp_options_policy", - verbose_name=_('service provider options policy'), blank=True, - null=True, - on_delete=models.SET_NULL) - users_can_manage_federations = models.BooleanField( - verbose_name=_('users can manage federation'), - default=True, - blank=True, - db_index=True) - - objects = managers.GetByLibertyProviderManager() - - def natural_key(self): - return (self.liberty_provider.slug,) - - def __unicode__(self): - return unicode(self.liberty_provider) - - class Meta: - verbose_name = _('SAML service provider') - verbose_name_plural = _('SAML service providers') - LIBERTY_SESSION_DUMP_KIND_SP = 0 LIBERTY_SESSION_DUMP_KIND_IDP = 1 @@ -462,7 +444,7 @@ class LibertyFederation(models.Model): it IdP or SP""" user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.SET_NULL) - sp = models.ForeignKey('LibertyServiceProvider', null=True, blank=True) + sp = models.ForeignKey('LibertyProvider', null=True, blank=True) name_id_format = models.CharField(max_length = 100, verbose_name = "NameIDFormat", blank=True, null=True) name_id_content = models.CharField(max_length = 100, diff --git a/tests/test_idp_saml2.py b/tests/test_idp_saml2.py index 7e5cbe8..86828ae 100644 --- a/tests/test_idp_saml2.py +++ b/tests/test_idp_saml2.py @@ -99,10 +99,6 @@ class SamlBaseTestCase(Authentic2TestCase): metadata=sp_meta) self.liberty_provider.clean() self.liberty_provider.save() - self.liberty_service_provider = saml_models.LibertyServiceProvider \ - .objects.create( - liberty_provider=self.liberty_provider, - enabled=True) self.default_sp_options_idp_policy = saml_models.SPOptionsIdPPolicy \ .objects.create( name='Default', -- 2.7.0