Commit 37265511 authored by Łukasz Nowak's avatar Łukasz Nowak

Implement Facebook based authentication.

parent 76e2be2e
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Cache Factory" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_count</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_mt_index</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>_tree</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>cache_duration</string> </key>
<value> <int>3600</int> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>facebook_token_cache_factory</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Cache Factory</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Length" module="BTrees.Length"/>
</pickle>
<pickle> <int>0</int> </pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Ram Cache" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>volatile_cache_plugin</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Ram Cache</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Folder" module="OFS.Folder"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>erp5_credential_facebook</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_body</string> </key>
<value> <string>raise NotImplementedError(\'This script shall be overlodad, as user creation is project specific\')\n
</string> </value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>tag, first_name, last_name, reference, email</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Base_createFacebookUser</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
2012/07/04 Łukasz Nowak
* Initial version
\ No newline at end of file
Nexedi
\ No newline at end of file
Facebook based credential system.
\ No newline at end of file
GPL
\ No newline at end of file
1
\ No newline at end of file
portal_caches/facebook_token_cache_factory
portal_caches/facebook_token_cache_factory/volatile_cache_plugin
\ No newline at end of file
erp5_credential_facebook
\ No newline at end of file
erp5_credential_facebook
\ No newline at end of file
0.1
\ No newline at end of file
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2012 Nexedi SA and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly advised to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
##############################################################################
from Products.ERP5Type.Globals import InitializeClass
from AccessControl import ClassSecurityInfo
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
from Products.PluggableAuthService.interfaces import plugins
from Products.PluggableAuthService.utils import classImplements
from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin
from Products.ERP5Security.ERP5UserManager import SUPER_USER
from Products.PluggableAuthService.PluggableAuthService import DumbHTTPExtractor
from AccessControl.SecurityManagement import getSecurityManager,\
setSecurityManager, newSecurityManager
from Products.ERP5Type.Cache import DEFAULT_CACHE_SCOPE
import socket
from Products.ERP5Security.ERP5UserManager import getUserByLogin
from zLOG import LOG, ERROR, INFO
try:
import facebook
except ImportError:
facebook = None
#Form for new plugin in ZMI
manage_addERP5FacebookExtractionPluginForm = PageTemplateFile(
'www/ERP5Security_addERP5FacebookExtractionPlugin', globals(),
__name__='manage_addERP5FacebookExtractionPluginForm')
def addERP5FacebookExtractionPlugin(dispatcher, id, title=None, REQUEST=None):
""" Add a ERP5FacebookExtractionPlugin to a Pluggable Auth Service. """
plugin = ERP5FacebookExtractionPlugin(id, title)
dispatcher._setObject(plugin.getId(), plugin)
if REQUEST is not None:
REQUEST['RESPONSE'].redirect(
'%s/manage_workspace'
'?manage_tabs_message='
'ERP5FacebookExtractionPlugin+added.'
% dispatcher.absolute_url())
class ERP5FacebookExtractionPlugin(BasePlugin):
"""
Plugin to authenicate as machines.
"""
meta_type = "ERP5 Facebook Extraction Plugin"
# cache_fatory_name proposal to begin configurable
cache_factory_name = 'facebook_token_cache_factory'
reference_prefix = 'fb_'
security = ClassSecurityInfo()
def __init__(self, id, title=None):
#Register value
self._setId(id)
self.title = title
#####################
# memcached helpers #
#####################
def _getCacheFactory(self):
portal = self.getPortalObject()
cache_tool = portal.portal_caches
cache_factory = cache_tool.getRamCacheRoot().get(self.cache_factory_name)
#XXX This conditional statement should be remove as soon as
#Broadcasting will be enable among all zeo clients.
#Interaction which update portal_caches should interact with all nodes.
if cache_factory is None \
and getattr(cache_tool, self.cache_factory_name, None) is not None:
#ram_cache_root is not up to date for current node
cache_tool.updateCache()
cache_factory = cache_tool.getRamCacheRoot().get(self.cache_factory_name)
if cache_factory is None:
raise KeyError
return cache_factory
def setFacebookToken(self, key, body):
cache_factory = self._getCacheFactory()
cache_duration = cache_factory.cache_duration
for cache_plugin in cache_factory.getCachePluginList():
cache_plugin.set(key, DEFAULT_CACHE_SCOPE,
body, cache_duration=cache_duration)
def getFacebookToken(self, key):
cache_factory = self._getCacheFactory()
for cache_plugin in cache_factory.getCachePluginList():
cache_entry = cache_plugin.get(key, DEFAULT_CACHE_SCOPE)
if cache_entry is not None:
return cache_entry.getValue()
raise KeyError('Key %r not found' % key)
def getFacebookEntry(self, token):
timeout = socket.getdefaulttimeout()
try:
# require really fast interaction
socket.setdefaulttimeout(5)
facebook_entry = facebook.GraphAPI(token).get_object("me")
except Exception:
facebook_entry = None
finally:
socket.setdefaulttimeout(timeout)
user_entry = {}
if facebook_entry is not None:
# sanitise value
try:
for k in ('first_name', 'last_name', 'id', 'email'):
if k == 'id':
user_entry['reference'] = self.reference_prefix + facebook_entry[k].encode(
'utf-8')
else:
user_entry[k] = facebook_entry[k].encode('utf-8')
except KeyError:
user_entry = None
return user_entry
####################################
#ILoginPasswordHostExtractionPlugin#
####################################
security.declarePrivate('extractCredentials')
def extractCredentials(self, request):
""" Extract facebook credentials from the request header. """
Base_createFacebookUser = getattr(self.getPortalObject(),
'Base_createFacebookUser', None)
if facebook is None or Base_createFacebookUser is None:
# no facebook library available or not configured
if facebook is None:
LOG('ERP5FacebookExtractionPlugin', INFO,
'No facebook module available, disabled authentication.')
if Base_createFacebookUser is None:
LOG('ERP5FacebookExtractionPlugin', INFO,
'No Base_createFacebookUser script available, install '
'erp5_credential_facebook, disabled authentication.')
return DumbHTTPExtractor().extractCredentials(request)
creds = {}
token = None
if request._auth is not None:
# 1st - try to fetch from Authorization header
if 'facebook' in request._auth.lower():
l = request._auth.split()
if len(l) == 2:
token = l[1]
if token is None:
# no token
return DumbHTTPExtractor().extractCredentials(request)
# token is available
user = None
facebook_entry = None
try:
user = self.getFacebookToken(token)
except KeyError:
facebook_entry = self.getFacebookEntry(token)
if facebook_entry is not None:
user = facebook_entry['reference']
if user is None:
# fallback to default way
return DumbHTTPExtractor().extractCredentials(request)
tag = '%s_user_creation_in_progress'
if self.getPortalObject().portal_activities.countMessageWithTag(tag) > 0:
self.REQUEST['USER_CREATION_IN_PROGRESS'] = user
else:
# create the user if not found
person_list = getUserByLogin(self.getPortalObject(), user)
if len(person_list) == 0:
sm = getSecurityManager()
if sm.getUser().getId() != SUPER_USER:
newSecurityManager(self, self.getUser(SUPER_USER))
try:
self.REQUEST['USER_CREATION_IN_PROGRESS'] = user
if facebook_entry is None:
facebook_entry = self.getFacebookEntry(token)
try:
self.Base_createFacebookUser(tag, **facebook_entry)
except Exception:
LOG('ERP5FacebookExtractionPlugin', ERROR,
'Issue while calling creation script:', error=True)
raise
finally:
setSecurityManager(sm)
try:
self.setFacebookToken(token, user)
except KeyError:
# allow to work w/o cache
pass
creds['external_login'] = user
creds['remote_host'] = request.get('REMOTE_HOST', '')
try:
creds['remote_address'] = request.getClientAddr()
except AttributeError:
creds['remote_address'] = request.get('REMOTE_ADDR', '')
return creds
manage_editERP5FacebookExtractionPluginForm = PageTemplateFile(
'www/ERP5Security_editERP5FacebookExtractionPlugin',
globals(),
__name__='manage_editERP5FacebookExtractionPluginForm')
#List implementation of class
classImplements( ERP5FacebookExtractionPlugin,
plugins.ILoginPasswordHostExtractionPlugin
)
InitializeClass(ERP5FacebookExtractionPlugin)
...@@ -28,6 +28,7 @@ import ERP5UserFactory ...@@ -28,6 +28,7 @@ import ERP5UserFactory
import ERP5KeyAuthPlugin import ERP5KeyAuthPlugin
import ERP5ExternalAuthenticationPlugin import ERP5ExternalAuthenticationPlugin
import ERP5BearerExtractionPlugin import ERP5BearerExtractionPlugin
import ERP5FacebookExtractionPlugin
def mergedLocalRoles(object): def mergedLocalRoles(object):
"""Returns a merging of object and its ancestors' """Returns a merging of object and its ancestors'
...@@ -64,6 +65,7 @@ registerMultiPlugin(ERP5UserFactory.ERP5UserFactory.meta_type) ...@@ -64,6 +65,7 @@ registerMultiPlugin(ERP5UserFactory.ERP5UserFactory.meta_type)
registerMultiPlugin(ERP5KeyAuthPlugin.ERP5KeyAuthPlugin.meta_type) registerMultiPlugin(ERP5KeyAuthPlugin.ERP5KeyAuthPlugin.meta_type)
registerMultiPlugin(ERP5ExternalAuthenticationPlugin.ERP5ExternalAuthenticationPlugin.meta_type) registerMultiPlugin(ERP5ExternalAuthenticationPlugin.ERP5ExternalAuthenticationPlugin.meta_type)
registerMultiPlugin(ERP5BearerExtractionPlugin.ERP5BearerExtractionPlugin.meta_type) registerMultiPlugin(ERP5BearerExtractionPlugin.ERP5BearerExtractionPlugin.meta_type)
registerMultiPlugin(ERP5FacebookExtractionPlugin.ERP5FacebookExtractionPlugin.meta_type)
def initialize(context): def initialize(context):
...@@ -130,6 +132,15 @@ def initialize(context): ...@@ -130,6 +132,15 @@ def initialize(context):
, icon='www/portal.gif' , icon='www/portal.gif'
) )
context.registerClass( ERP5FacebookExtractionPlugin.ERP5FacebookExtractionPlugin
, permission=ManageUsers
, constructors=(
ERP5FacebookExtractionPlugin.manage_addERP5FacebookExtractionPluginForm,
ERP5FacebookExtractionPlugin.addERP5FacebookExtractionPlugin, )
, visibility=None
, icon='www/portal.gif'
)
from AccessControl.SecurityInfo import ModuleSecurityInfo from AccessControl.SecurityInfo import ModuleSecurityInfo
ModuleSecurityInfo('Products.ERP5Security.ERP5UserManager').declarePublic( ModuleSecurityInfo('Products.ERP5Security.ERP5UserManager').declarePublic(
'getUserByLogin') 'getUserByLogin')
<h1 tal:replace="structure context/manage_page_header">PAGE HEADER</h1>
<h2 tal:define="form_title string:Add ERP5 Facebook Extraction Plugin"
tal:replace="structure context/manage_form_title">FORM TITLE</h2>
<p class="form-help">Please input the configuration</p>
<form action="addERP5FacebookExtractionPlugin" method="POST">
<table cellspacing="0" cellpadding="2" border="0">
<tr>
<td align="left" valign="top">
<div class="form-label">
Id
</div>
</td>
<td align="left" valign="top">
<input type="text" name="id" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Title
</div>
</td>
<td align="left" valign="top">
<input type="text" name="title" size="40" />
</td>
</tr>
<tr>
<td colspan="2"> <input type="submit" value="add plugin"/>
</td>
</tr>
</table>
</form>
<h1 tal:replace="structure context/manage_page_footer">PAGE FOOTER</h1>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment