data_source_plugin: add OAuth2 authentication
This commit is contained in:
parent
2a3788ff0f
commit
2a1eb70f19
|
@ -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)
|
||||
|
|
|
@ -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']
|
|
@ -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'))
|
||||
|
||||
|
|
Reference in New Issue