data_source_plugin: add OAuth2 authentication

This commit is contained in:
Benjamin Dauvergne 2014-03-31 11:34:14 +02:00
parent 2a3788ff0f
commit 2a1eb70f19
3 changed files with 106 additions and 11 deletions

View File

@ -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)

View File

@ -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']

View File

@ -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'))