diff --git a/portail_citoyen2/apps/data_source_plugin/cms_plugins.py b/portail_citoyen2/apps/data_source_plugin/cms_plugins.py index 15b94f5..55b65ce 100644 --- a/portail_citoyen2/apps/data_source_plugin/cms_plugins.py +++ b/portail_citoyen2/apps/data_source_plugin/cms_plugins.py @@ -18,6 +18,7 @@ from cms.plugin_pool import plugin_pool from models import DataSourcePlugin as DataSourcePluginModel, DataSource, RawInlineTemplatePlugin as RawInlineTemplatePluginModel import signature +from allauth.socialaccount.models import SocialToken logger = logging.getLogger(__name__) @@ -32,7 +33,7 @@ class Data(object): DataSource.XML: 'xml', } - def __init__(self, data_source, context, limit, refresh): + def __init__(self, data_source, context, limit, refresh, request=None): self.data_source = data_source self.kind = self.MAPPING.get(data_source.mime_type) self.context = context @@ -42,21 +43,36 @@ class Data(object): self.key = hashlib.md5('datasource-{self.data_source.id}-{self.url}-{self.limit}-{self.refresh}'.format(self=self)).hexdigest() self.now = time.time() self.__content = CACHE_SENTINEL + self.request = request + def get_access_token(self): + user = self.request.user + try: + token = SocialToken.objects.get(account__provider='authentic2', + account__user=user) + logger.debug('retrieved access token: %r', token) + return token.token + except SocialToken.DoesNotExist: + logger.warning('unable to find a social token for user: %r', user) + return '' def update_content(self): content = None try: self.final_url = self.url if self.data_source.signature_key: + # remove the hmac- prefix + hash_algo = self.data_source.auth_mech[:5] self.final_url = signature.sign_url(self.final_url.encode('ascii'), self.data_source.signature_key.encode('utf-8'), - algo=self.data_source.hash_algo) + algo=hash_algo) logger.debug('getting data source %r from url %r', self.data_source.name, self.final_url) headers = { 'Accept': self.data_source.mime_type, } + if self.data_source.auth_mech == 'oauth2': + headers['Authorization'] = 'Bearer %s' % self.get_access_token() request = requests.get(self.final_url, headers=headers, verify=self.data_source.verify_certificate, allow_redirects=self.data_source.allow_redirects, @@ -72,9 +88,6 @@ class Data(object): except RequestException: logger.warning('HTTP Request failed when loading datasource' ' %s from URL %s', self.data_source.id, self.final_url) - except Exception: - logger.exception('Unkown exception when loading datasource %s' - ' from URL %s', self.data_source.id, self.final_url) else: try: content = getattr(self, 'get_content_'+self.kind)(request) @@ -148,8 +161,9 @@ class DataSourcePlugin(CMSPluginBase): text_enabled = True def get_sources(self, context, instance): + request = context['request'] for source in instance.sources.all(): - yield Data(source.source, context, instance.limit, instance.refresh) + yield Data(source.source, context, instance.limit, instance.refresh, request=request) def render(self, context, instance, placeholder): logger.debug('getting context of data source plugin %s', instance.id) diff --git a/portail_citoyen2/apps/data_source_plugin/migrations/0002_rename_hash_algo.py b/portail_citoyen2/apps/data_source_plugin/migrations/0002_rename_hash_algo.py new file mode 100644 index 0000000..c89214d --- /dev/null +++ b/portail_citoyen2/apps/data_source_plugin/migrations/0002_rename_hash_algo.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- +from south.db import db +from south.v2 import SchemaMigration + + +class Migration(SchemaMigration): + no_dry_run = True + + def forwards(self, orm): + db.rename_column('data_source_plugin_datasource', 'hash_algo', 'auth_mech') + for ds in orm.DataSource.objects.all(): + if ds.auth_mech.startswith('sha'): + ds.auth_mech = 'hmac-' + ds.auth_mech + ds.save() + + def backwards(self, orm): + db.rename_column('data_source_plugin_datasource', 'auth_mech', 'hash_algo') + for ds in orm.DataSource.objects.all(): + if ds.auth_mech.startswith('hmac-'): + ds.auth_mech = ds.auth_mech[5:] + ds.save() + if ds.auth_mech == 'oauth2': + ds.delete() + + models = { + 'cms.cmsplugin': { + 'Meta': {'object_name': 'CMSPlugin'}, + 'changed_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}), + 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.CMSPlugin']", 'null': 'True', 'blank': 'True'}), + 'placeholder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}), + 'plugin_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}), + 'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}) + }, + 'cms.placeholder': { + 'Meta': {'object_name': 'Placeholder'}, + 'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'slot': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}) + }, + u'data_source_plugin.datasource': { + 'Meta': {'ordering': "('name',)", 'object_name': 'DataSource'}, + 'allow_redirects': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'auth_mech': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '16', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'mime_type': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'signature_key': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128', 'blank': 'True'}), + 'timeout': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'url': ('django.db.models.fields.URLField', [], {'max_length': '1024'}), + 'verify_certificate': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'data_source_plugin.datasourceplugin': { + 'Meta': {'object_name': 'DataSourcePlugin'}, + u'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}), + 'limit': ('django.db.models.fields.IntegerField', [], {'default': '10'}), + 'refresh': ('django.db.models.fields.IntegerField', [], {'default': '60'}), + 'template_source': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}) + }, + u'data_source_plugin.plugindatasource': { + 'Meta': {'ordering': "('order', 'id')", 'object_name': 'PluginDataSource'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'plugin': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sources'", 'to': u"orm['data_source_plugin.DataSourcePlugin']"}), + 'source': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'plugins'", 'to': u"orm['data_source_plugin.DataSource']"}) + }, + u'data_source_plugin.rawinlinetemplateplugin': { + 'Meta': {'object_name': 'RawInlineTemplatePlugin'}, + u'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}), + 'template_source': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}) + } + } + + complete_apps = ['data_source_plugin'] diff --git a/portail_citoyen2/apps/data_source_plugin/models.py b/portail_citoyen2/apps/data_source_plugin/models.py index 71ac1bf..393c9dc 100644 --- a/portail_citoyen2/apps/data_source_plugin/models.py +++ b/portail_citoyen2/apps/data_source_plugin/models.py @@ -16,7 +16,7 @@ def validate_template(value): class DataSource(models.Model): - JSON = 'text/json' + JSON = 'application/json' RSS = 'application/rss+xml' HTML = 'text/html' XML = 'text/xml' @@ -30,8 +30,9 @@ class DataSource(models.Model): HASHES = ( ('', 'None'), - ('sha256', 'SHA 256'), - ('sha1', 'SHA 1'), + ('hmac-sha256', 'HMAC-SHA-256'), + ('hmac-sha1', 'HMAC-SHA-1'), + ('oauth2', 'OAuth2'), ) name = models.CharField(verbose_name=_('Name'), max_length=32) @@ -39,7 +40,7 @@ class DataSource(models.Model): choices=CHOICES) url = models.URLField(verbose_name=_('URL'), max_length=1024, validators=[validate_template]) - hash_algo = models.CharField(verbose_name=_('Hashing algorithm'), + auth_mech = models.CharField(verbose_name=_('Authentication mechanism'), max_length=16, choices=HASHES, default='', blank=True) signature_key = models.CharField(verbose_name=_('Signature key'), max_length=128, default='', blank=True) @@ -54,7 +55,7 @@ class DataSource(models.Model): 'failing to download a datasource')) def clean(self): - if self.signature_key and not self.hash_algo: + if self.signature_key and (not self.auth_mech or not self.auth_mech.startswith('hmac-')): raise ValidationError(_('You must choose a hashing algorithm if ' 'you set a signature key'))