Commit c69b4e86 authored by Sebastien Robin's avatar Sebastien Robin

fabien finished to implement authentication, and he added tests for it

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@14439 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 999e908f
......@@ -129,11 +129,6 @@ class ERP5Conduit(XMLSyncUtilsMixin):
xml = self.convertToXml(xml)
LOG('addNode',0,'xml_reconstitued: %s' % str(xml))
# In the case where this new node is a object to add
LOG('addNode',0,'object.id: %s' % object.getId())
LOG('addNode',0,'xml.nodeName: %s' % xml.nodeName)
LOG('addNode',0,'getSubObjectDepth: %i' % self.getSubObjectDepth(xml))
LOG('addNode',0,'isHistoryAdd: %i' % self.isHistoryAdd(xml))
LOG('addNode xml',0,repr(xml.toxml()))
if xml.nodeName in self.XUPDATE_INSERT_OR_ADD and self.getSubObjectDepth(xml)==0:
if self.isHistoryAdd(xml)!=-1: # bad hack XXX to be removed
for element in self.getXupdateElementList(xml):
......@@ -216,7 +211,6 @@ class ERP5Conduit(XMLSyncUtilsMixin):
object_id = self.getAttribute(xml,'id')
elif self.getSubObjectDepth(xml)==1:
object_id = self.getSubObjectId(xml)
#LOG('ERP5Conduit',0,'deleteNode, SubObjectDepth: %i' % self.getSubObjectDepth(xml))
elif self.getSubObjectDepth(xml)==2:
# we have to call delete node on a subsubobject
sub_object_id = self.getSubObjectId(xml)
......@@ -706,14 +700,11 @@ class ERP5Conduit(XMLSyncUtilsMixin):
for subnode in self.getElementNodeList(xml):
if not(subnode.nodeName in self.NOT_EDITABLE_PROPERTY):
keyword_type = self.getPropertyType(subnode)
LOG('newObject',0,str(subnode.childNodes))
# This is the case where the property is a list
keyword=str(subnode.nodeName)
if len(subnode.childNodes) > 0: # We check that this tag is not empty
data = subnode.childNodes[0].data
args[keyword]=data
LOG('newObject',0,'keyword: %s' % str(keyword))
LOG('newObject',0,'keywordtype: %s' % str(keyword_type))
#if args.has_key(keyword):
# LOG('newObject',0,'data: %s' % str(args[keyword]))
if args.has_key(keyword):
......@@ -722,8 +713,6 @@ class ERP5Conduit(XMLSyncUtilsMixin):
self.addNode(object=object,xml=subnode, force=1)
# We should first edit the object
args = self.getFormatedArgs(args=args)
LOG('newObject',0,"object.getphyspath: %s" % str(object.getPhysicalPath()))
LOG('newObject',0,"args: %s" % str(args))
# edit the object with a dictionnary of arguments,
# like {"telephone_number":"02-5648"}
#object._edit(**args)
......@@ -885,7 +874,6 @@ class ERP5Conduit(XMLSyncUtilsMixin):
dict_list = map(lambda x:x.split(':'),data[1:-1].split(','))
data = map(lambda (x,y):(x.replace(' ','').replace("'",''),int(y)),dict_list)
data = dict(data)
LOG('convertXmlValue',0,'data: %s' % str(data))
return data
# XXX is it the right place ? It should be in XupdateUtils, but here we
......@@ -997,7 +985,6 @@ class ERP5Conduit(XMLSyncUtilsMixin):
add_action = self.isWorkflowActionAddable(object=object,
status=status,wf_tool=wf_tool,
wf_id=wf_id,xml=xml)
#LOG('addNode, workflow_history wf_conflict_list:',0,wf_conflict_list)
LOG('addNode, workflow_history add_action:',0,add_action)
if add_action and not simulate:
LOG('addNode, setting status:',0,'ok')
......
......@@ -152,7 +152,9 @@ class Publication(Subscription):
constructors = (addPublication,)
# Constructor
def __init__(self, id, title, publication_url, destination_path, query, xml_mapping, conduit, gpg_key):
def __init__(self, id, title, publication_url, destination_path,
query, xml_mapping, conduit, gpg_key, auth_required=False,
authentication_format='', authentication_type=''):
"""
constructor
"""
......@@ -169,6 +171,9 @@ class Publication(Subscription):
self.setConduit(conduit)
Folder.__init__(self, id)
self.title = title
self.auth_required = auth_required
self.authentication_format = authentication_format
self.authentication_type = authentication_type
def getPublicationUrl(self):
"""
......@@ -189,6 +194,42 @@ class Publication(Subscription):
"""
self.publication_url = publication_url
def isAuthenticationRequired(self):
"""
return False if authentication not required, True else
"""
return getattr(self, 'auth_required', False)
def setAuthentication(self, auth):
"""
set the value of the authentication requirement
"""
self.auth_required = auth
def getAuthenticationFormat(self):
"""
return the format of authentication
"""
return getattr(self, 'authentication_format', '')
def getAuthenticationType(self):
"""
return the type of authentication
"""
return getattr(self, 'authentication_type', '')
def setAuthenticationFormat(self, authentication_format):
"""
set the format of authentication
"""
self.authentication_format = authentication_format
def setAuthenticationType(self, authentication_type):
"""
set the type of authentication
"""
self.authentication_type = authentication_type
def addSubscriber(self, subscriber):
"""
Add a new subscriber to the publication
......
......@@ -34,6 +34,10 @@ from xml.dom.minidom import parse, parseString
from XMLSyncUtils import XMLSyncUtils
from Conduit.ERP5Conduit import ERP5Conduit
from Products.CMFCore.utils import getToolByName
from Products.ERP5Security.ERP5UserManager import ERP5UserManager
from Products.PluggableAuthService.interfaces.plugins import\
IAuthenticationPlugin
from AccessControl.SecurityManagement import newSecurityManager
import commands
from zLOG import LOG
......@@ -43,7 +47,7 @@ class PublicationSynchronization(XMLSyncUtils):
"""
def PubSyncInit(self, publication=None, xml_client=None, subscriber=None,
sync_type=None, auth_required=0):
sync_type=None):
"""
Read the client xml message
Send the first XML message from the server
......@@ -52,8 +56,10 @@ class PublicationSynchronization(XMLSyncUtils):
#the session id is set at the same value of those of the client
subscriber.setSessionId(self.getSessionId(xml_client))
# for a new session, the message Id must be reset
subscriber.resetMessageId()
#same for the message id
subscriber.setMessageId(self.getMessageId(xml_client))
#at the begining of the synchronization the subscriber is not authenticated
subscriber.setAuthenticated(False)
#the last_message_id is 1 because the message that
#we are about to send is the message 1
subscriber.initLastMessageId(1)
......@@ -67,7 +73,8 @@ class PublicationSynchronization(XMLSyncUtils):
alert_code = self.getAlertCode(xml_client)
cred = self.checkCred(xml_client)
#XXX this is in developement, it's just for tests
if not cred and auth_required:
if publication.isAuthenticationRequired():
if not cred:
LOG('PubSyncInit',0,'authentication required')
# Prepare the xml message for the Sync initialization package
cmd_id = 1 # specifies a SyncML message-unique command identifier
......@@ -76,14 +83,15 @@ class PublicationSynchronization(XMLSyncUtils):
xml('<SyncML>\n')
# syncml header
xml(self.SyncMLHeader(subscriber.getSessionId(),
subscriber.incrementMessageId(), subscriber.getSubscriptionUrl(),
subscriber.getMessageId(), subscriber.getSubscriptionUrl(),
publication.getPublicationUrl()))
# syncml body
xml(' <SyncBody>\n')
# chal message
xml(self.SyncMLChal(cmd_id, "SyncHdr", publication.getPublicationUrl(),
subscriber.getSubscriptionUrl(), "b64", "syncml:auth-basic",
self.UNAUTHORIZED))
xml(self.SyncMLChal(cmd_id, "SyncHdr",
publication.getPublicationUrl(), subscriber.getSubscriptionUrl(),
publication.getAuthenticationFormat(),
publication.getAuthenticationType(), self.AUTH_REQUIRED))
cmd_id += 1
xml(' </SyncBody>\n')
......@@ -92,8 +100,56 @@ class PublicationSynchronization(XMLSyncUtils):
xml_a = ''.join(xml_list)
self.sendResponse(from_url=publication.getPublicationUrl(),
to_url=subscriber.getSubscriptionUrl(),sync_id=publication.getTitle(),
xml=xml_a,domain=publication)
to_url=subscriber.getSubscriptionUrl(),
sync_id=publication.getTitle(), xml=xml_a, domain=publication)
else:#(if the subscriber begin the session with a cred) -> to be tested
(authentication_format, authentication_type, data) = self.getCred(xml_client)
#at the begining, the code is initialised at UNAUTHORIZED
auth_code=self.UNAUTHORIZED
if authentication_format == publication.getAuthenticationFormat():
if authentication_type == publication.getAuthenticationType():
decoded = subscriber.decode(authentication_format, data)
if decoded not in ('', None) and decoded.__contains__(':'):
(login, password) = decoded.split(':')
uf = self.getPortalObject().acl_users
for plugin_name, plugin in uf._getOb('plugins').listPlugins(
IAuthenticationPlugin ):
if plugin.authenticateCredentials(
{'login':login, 'password':password}) is not None:
subscriber.setAuthenticated(True)
auth_code=self.AUTH_ACCEPTED
#here we must log in with the user authenticated :
user = uf.getUserById(login).__of__(uf)
newSecurityManager(None, user)
subscriber.setUser(login)
break
#in all others cases, the auth_code is set to UNAUTHORIZED
# Prepare the xml message for the Sync initialization package
cmd_id = 1 # specifies a SyncML message-unique command identifier
xml_list = []
xml = xml_list.append
xml('<SyncML>\n')
# syncml header
xml(self.SyncMLHeader(subscriber.getSessionId(),
subscriber.getMessageId(),
subscriber.getSubscriptionUrl(),
publication.getPublicationUrl()))
# syncml body
xml(' <SyncBody>\n')
xml(self.SyncMLStatus(cmd_id, subscriber.getSubscriptionUrl(),
publication.getPublicationUrl(), auth_code))
xml(' </SyncBody>\n')
xml('</SyncML>\n')
xml_a = ''.join(xml_list)
self.sendResponse(from_url=publication.getPublicationUrl(),
to_url=subscriber.getSubscriptionUrl(),
sync_id=publication.getTitle(), xml=xml_a, domain=publication)
else :
# If slow sync, then resend everything
if alert_code == self.SLOW_SYNC:
......@@ -103,8 +159,7 @@ class PublicationSynchronization(XMLSyncUtils):
# Check if the last time synchronization is the same as the client one
mess='\nsubscriber.getNextAnchor:\t%s\nsubscriber.getLastAnchor:\t%s\
\nlast_anchor:\t\t\t%s\nnext_anchor:\t\t\t%s' % (subscriber.getNextAnchor(),
subscriber.getLastAnchor(), last_anchor, next_anchor)
\nlast_anchor:\t\t\t%s\nnext_anchor:\t\t\t%s' % (subscriber.getNextAnchor(), subscriber.getLastAnchor(), last_anchor, next_anchor)
LOG('PubSyncInit',0,mess)
if subscriber.getNextAnchor() != last_anchor:
......@@ -112,8 +167,8 @@ class PublicationSynchronization(XMLSyncUtils):
LOG('PubSyncInit',0,'anchor null')
raise ValueError, "Sorry, the anchor was null"
else:
message = "bad anchors in PubSyncInit! " + subscriber.getNextAnchor() + \
" and " + last_anchor
message = "bad anchors in PubSyncInit! " + \
subscriber.getNextAnchor() + " and " + last_anchor
LOG('PubSyncInit',0,message)
else:
subscriber.setNextAnchor(next_anchor)
......@@ -123,8 +178,7 @@ class PublicationSynchronization(XMLSyncUtils):
# We have started the sync from the server (may be for a conflict resolution)
pass
if alert is not None and auth_required==0:
#if 1:
if alert is not None and not publication.isAuthenticationRequired():
# Prepare the xml message for the Sync initialization package
cmd_id = 1 # specifies a SyncML message-unique command identifier
xml_list = []
......@@ -202,6 +256,14 @@ class PublicationSynchronization(XMLSyncUtils):
alert_code in (self.TWO_WAY,self.SLOW_SYNC):
result = self.PubSyncInit(publication=publication,
xml_client=xml_client, subscriber=subscriber, sync_type=alert_code)
else:
#we log the user authenticated to do the synchronization with him
if publication.isAuthenticationRequired():
if subscriber.isAuthenticated():
uf = self.getPortalObject().acl_users
user = uf.getUserById(subscriber.getUser()).__of__(uf)
newSecurityManager(None, user)
result = self.PubSyncModif(publication, xml_client)
else:
result = self.PubSyncModif(publication, xml_client)
elif subscriber is not None:
......
......@@ -41,6 +41,11 @@ from zLOG import LOG
import md5
try:
from base64 import b64encode, b64decode
except ImportError:
from base64 import encodestring as b64encode, decodestring as b64decode
#class Conflict(SyncCode, Implicit):
class Conflict(SyncCode, Base):
"""
......@@ -53,8 +58,8 @@ class Conflict(SyncCode, Base):
isIndexable = 0
isPortalContent = 0 # Make sure RAD generated accessors at the class level
def __init__(self, object_path=None, keyword=None, xupdate=None, publisher_value=None,\
subscriber_value=None, subscriber=None):
def __init__(self, object_path=None, keyword=None, xupdate=None,
publisher_value=None, subscriber_value=None, subscriber=None):
self.object_path=object_path
self.keyword = keyword
self.setLocalValue(publisher_value)
......@@ -614,8 +619,8 @@ class Subscription(Folder, SyncCode):
isPortalContent = 1
isRADContent = 1
icon = None
isIndexable = 0
user = None
# Declarative properties
property_sheets = ( PropertySheet.Base
......@@ -635,7 +640,8 @@ class Subscription(Folder, SyncCode):
)
# Constructor
def __init__(self, id, title, publication_url, subscription_url, destination_path, query, xml_mapping, conduit, gpg_key):
def __init__(self, id, title, publication_url, subscription_url, destination_path, query, xml_mapping, conduit, gpg_key, login, password,
authentication_format='', authentication_type=''):
"""
We need to create a dictionnary of
signatures of documents which belong to the synchronisation
......@@ -652,6 +658,10 @@ class Subscription(Folder, SyncCode):
#self.signatures = PersistentMapping()
self.last_anchor = '00000000T000000Z'
self.next_anchor = '00000000T000000Z'
self.login=login
self.password=password
self.authentication_format=authentication_format
self.authentication_type=authentication_type
self.domain_type = self.SUB
self.gpg_key = gpg_key
self.setGidGenerator(None)
......@@ -714,28 +724,28 @@ class Subscription(Folder, SyncCode):
We will see if the last session id was the same
wich means that the same message was sent again
return 1 if the session id was not seen, 0 if already seen
return True if the session id was not seen, False if already seen
"""
last_session_id = getattr(self,'last_session_id',None)
if last_session_id == session_id:
return 0
return False
self.last_session_id = session_id
return 1
return True
def checkCorrectRemoteMessageId(self, message_id):
"""
We will see if the last message id was the same
wich means that the same message was sent again
return 1 if the message id was not seen, 0 if already seen
return True if the message id was not seen, False if already seen
"""
last_message_id = getattr(self,'last_message_id',None)
# LOG('checkCorrectRemoteMessageId last_message_id =',0,last_message_id)
# LOG('checkCorrectRemoteMessageId message_id =',0,message_id)
if last_message_id == message_id:
return 0
return False
self.last_message_id = message_id
return 1
return True
def initLastMessageId(self, last_message_id=None):
"""
......@@ -872,25 +882,74 @@ class Subscription(Folder, SyncCode):
"""
return self.gid_generator
def getLogin(self):
"""
This method return the login of this subscription
"""
return getattr(self, 'login', '')
def setLogin(self, new_login):
"""
set the login at new_login
"""
self.login=new_login
def getPassword(self):
"""
This method return the password of this subscription
"""
return getattr(self, 'password', '')
def setPassword(self, new_password):
"""
set the password at new_password
"""
self.password=new_password
def setAuthentication(self, auth):
"""
set the value of the authentication requirement
"""
self.auth_required = auth
def getAuthenticationFormat(self):
"""
return the format of authentication
"""
return getattr(self, 'authentication_format', '')
def getAuthenticationType(self):
"""
return the type of authentication
"""
return getattr(self, 'authentication_type', '')
def setAuthenticationFormat(self, authentication_format):
"""
set the format of authentication
"""
self.authentication_format = authentication_format
def setAuthenticationType(self, authentication_type):
"""
set the type of authentication
"""
self.authentication_type = authentication_type
def getGidFromObject(self, object):
"""
"""
o_base = aq_base(object)
o_gid = None
# LOG('getGidFromObject',0,'gidgenerator : _%s_' % repr(self.getGidGenerator()))
gid_gen = self.getGidGenerator()
if callable(gid_gen):
# LOG('getGidFromObject gid_generator',0,'is callable')
o_gid=gid_gen(object)
# LOG('getGidFromObject',0,'o_gid: %s' % repr(o_gid))
elif getattr(o_base, gid_gen, None) is not None:
# LOG('getGidFromObject',0,'there is the gid generator on o_base')
generator = getattr(object, gid_gen)
o_gid = generator() # XXX - used to be o_gid = generator(object=object) which is redundant
# LOG('getGidFromObject',0,'o_gid: %s' % repr(o_gid))
elif gid_gen is not None:
# It might be a script python
# LOG('getGidFromObject',0,'there is the gid generator')
generator = getattr(object,gid_gen)
o_gid = generator() # XXX - used to be o_gid = generator(object=object) which is redundant
# LOG('getGidFromObject',0,'o_gid: %s' % repr(o_gid))
......@@ -911,7 +970,6 @@ class Subscription(Folder, SyncCode):
# LOG('getObjectFromGid oject_list',0,object_list)
if signature is not None and signature.getId() is not None:
o_id = signature.getId()
# LOG('getObjectFromGid o_id',0,o_id)
o = None
try:
o = destination._getOb(o_id)
......@@ -920,7 +978,6 @@ class Subscription(Folder, SyncCode):
if o is not None and o in object_list:
return o
for o in object_list:
# LOG('getObjectFromGid',0,'working on : %s' % repr(o))
o_gid = self.getGidFromObject(o)
if o_gid == gid:
return o
......@@ -1074,6 +1131,12 @@ class Subscription(Folder, SyncCode):
"""
self.message_id = 0
def setMessageId(self, message_id):
"""
set the message id to message_id
"""
self.message_id = message_id
def getLastAnchor(self):
"""
return the id of the last synchronisation
......@@ -1230,3 +1293,67 @@ class Subscription(Folder, SyncCode):
o.setTempXML(None)
self.setRemainingObjectPathList(None)
def isAuthenticated(self):
"""
return True if the subscriber is authenticated for this session, False
in other case
"""
return self.is_authenticated
def setAuthenticated(self, value):
"""
set at True or False the value of is_authenticated is the subscriber
is authenticated for this session or not
"""
self.is_authenticated = value
def encode(self, format, string_to_encode):
"""
return the string_to_encode encoded with format format
"""
if format in ('', None):
return string_to_encode
if format == 'b64':
return b64encode(string_to_encode)
#elif format is .... put here the other formats
else:#if there is no format corresponding with format, raise an error
LOG('encode : unknown or not implemented format :', 0, format)
raise ValueError, "Sorry, the format %s is unknow or not implemented" % format
def decode(self, format, string_to_decode):
"""
return the string_to_decode decoded with format format
"""
string_to_decode = string_to_decode.encode('utf-8')
if format in ('', None):
return string_to_decode
if format == 'b64':
return b64decode(string_to_decode)
#elif format is .... put here the other formats
else:#if there is no format corresponding with format, raise an error
LOG('decode : unknown or not implemented format :', 0, format)
raise ValueError, "Sorry, the format %s is unknow or not implemented" % format
def isDecodeEncodeTheSame(self, string_encoded, string_decoded, format):
"""
return True if the string_encoded is equal to string_decoded encoded
in format
"""
isTheSame=False
if self.encode(format, string_decoded) == string_encoded:
isTheSame=True
return isTheSame
def setUser(self, user):
"""
save the user logged in to log him on each transaction
"""
self.user=user
def getUser(self):
"""
retrun the user logged in
"""
return self.user
......@@ -44,6 +44,8 @@ class SubscriptionSynchronization(XMLSyncUtils):
"""
LOG('SubSyncInit',0,'starting....')
cmd_id = 1 # specifies a SyncML message-unique command identifier
subscription.NewAnchor()
subscription.initLastMessageId()
xml_list = []
xml = xml_list.append
xml('<SyncML>\n')
......@@ -54,8 +56,6 @@ class SubscriptionSynchronization(XMLSyncUtils):
# syncml body
xml(' <SyncBody>\n')
subscription.NewAnchor()
subscription.initLastMessageId()
# We have to set every object as NOT_SYNCHRONIZED
subscription.startSynchronization()
......@@ -100,7 +100,21 @@ class SubscriptionSynchronization(XMLSyncUtils):
xml_client = msg
if isinstance(xml_client, str) or isinstance(xml_client, unicode):
xml_client = parseString(xml_client)
response = self.SubSyncModif(self.getSubscription(id),xml_client)
next_status = self.getNextSyncBodyStatus(xml_client, None)
#LOG('readResponse, next status :',0,next_status)
if next_status is not None:
status_code = self.getStatusCode(next_status)
#LOG('readResponse status code :',0,status_code)
if status_code == self.AUTH_REQUIRED:
#LOG('readResponse', 0, 'Authentication required')
response = self.SubSyncCred(id, xml_client)
elif status_code == self.UNAUTHORIZED:
#LOG('readResponse', 0, 'Bad authentication')
return {'has_response':0,'xml':xml_client}
else:
response = self.SubSyncModif(self.getSubscription(id), xml_client)
else:
response = self.SubSyncModif(self.getSubscription(id), xml_client)
if RESPONSE is not None:
......@@ -108,6 +122,53 @@ class SubscriptionSynchronization(XMLSyncUtils):
else:
return response
def SubSyncCred (self, id, msg=None, RESPONSE=None):
"""
This method send crendentials
"""
LOG('SubSyncCred',0,'starting... id: %s' % str(id))
LOG('SubSyncCred',0,'starting... msg: %s' % str(msg))
cmd_id = 1 # specifies a SyncML message-unique command identifier
subscription = self.getSubscription(id)
xml_list = []
xml = xml_list.append
xml('<SyncML>\n')
# syncml header
data = "%s:%s" % (subscription.getLogin(), subscription.getPassword())
data=subscription.encode(subscription.getAuthenticationFormat(), data)
xml(self.SyncMLHeader(subscription.getSessionId(),
subscription.incrementMessageId(), subscription.getPublicationUrl(),
subscription.getSubscriptionUrl(), dataCred=data,
authentication_format=subscription.getAuthenticationFormat(),
authentication_type=subscription.getAuthenticationType()))
# syncml body
xml(' <SyncBody>\n')
# alert message
xml(self.SyncMLAlert(cmd_id, subscription.getSynchronizationType(),
subscription.getPublicationUrl(),
subscription.getDestinationPath(),
subscription.getLastAnchor(),
subscription.getNextAnchor()))
cmd_id += 1
xml(' <Put>\n')
xml(' <CmdID>%s</CmdID>\n' % cmd_id)
cmd_id += 1
xml(' </Put>\n')
xml(' </SyncBody>\n')
xml('</SyncML>\n')
xml_a = ''.join(xml_list)
self.sendResponse(from_url=subscription.subscription_url,
to_url=subscription.publication_url, sync_id=subscription.getTitle(),
xml=xml_a,domain=subscription)
return {'has_response':1,'xml':xml_a}
def SubSyncModif(self, subscription, xml_client):
"""
Send the client modification, this happens after the Synchronization
......
......@@ -62,8 +62,8 @@ from zLOG import LOG
class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronization,
UniqueObject, Folder):
class SynchronizationTool( SubscriptionSynchronization,
PublicationSynchronization, UniqueObject, Folder):
"""
This tool implements the synchronization algorithm
......@@ -159,9 +159,11 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
+ '?manage_tabs_message=Tool+updated.'
)
security.declareProtected(Permissions.ModifyPortalContent, 'manage_addPublication')
security.declareProtected(Permissions.ModifyPortalContent,
'manage_addPublication')
def manage_addPublication(self, title, publication_url, destination_path,
query, xml_mapping, conduit, gpg_key, RESPONSE=None):
query, xml_mapping, conduit, gpg_key, auth_required=0,
authentication_format='', authentication_type='', RESPONSE=None):
"""
create a new publication
"""
......@@ -171,7 +173,8 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
folder = self.getObjectContainer()
new_id = self.getPublicationIdFromTitle(title)
pub = Publication(new_id, title, publication_url, destination_path,
query, xml_mapping, conduit, gpg_key)
query, xml_mapping, conduit, gpg_key, auth_required,
authentication_format, authentication_type)
folder._setObject( new_id, pub )
#if len(self.list_publications) == 0:
# self.list_publications = PersistentMapping()
......@@ -179,9 +182,12 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
if RESPONSE is not None:
RESPONSE.redirect('managePublications')
security.declareProtected(Permissions.ModifyPortalContent, 'manage_addSubscription')
security.declareProtected(Permissions.ModifyPortalContent,
'manage_addSubscription')
def manage_addSubscription(self, title, publication_url, subscription_url,
destination_path, query, xml_mapping, conduit, gpg_key, RESPONSE=None):
destination_path, query, xml_mapping, conduit, gpg_key,
login=None, password=None, authentication_format='',
authentication_type='',RESPONSE=None):
"""
XXX should be renamed as addSubscription
create a new subscription
......@@ -192,7 +198,9 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
folder = self.getObjectContainer()
new_id = self.getSubscriptionIdFromTitle(title)
sub = Subscription(new_id, title, publication_url, subscription_url,
destination_path, query, xml_mapping, conduit, gpg_key)
destination_path, query, xml_mapping, conduit, gpg_key,
login, password, authentication_format,
authentication_type)
folder._setObject( new_id, sub )
#if len(self.list_subscriptions) == 0:
# self.list_subscriptions = PersistentMapping()
......@@ -200,10 +208,12 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
if RESPONSE is not None:
RESPONSE.redirect('manageSubscriptions')
security.declareProtected(Permissions.ModifyPortalContent, 'manage_editPublication')
security.declareProtected(Permissions.ModifyPortalContent,
'manage_editPublication')
def manage_editPublication(self, title, publication_url, destination_path,
query, xml_mapping, conduit, gpg_key, id_generator,
gid_generator, RESPONSE=None):
gid_generator, auth_required=0, authentication_format='',
authentication_type='', RESPONSE=None):
"""
modify a publication
"""
......@@ -217,13 +227,19 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
pub.setGPGKey(gpg_key)
pub.setIdGenerator(id_generator)
pub.setGidGenerator(gid_generator)
pub.setAuthentication(auth_required)
pub.setAuthenticationFormat(authentication_format)
pub.setAuthenticationType(authentication_type)
if RESPONSE is not None:
RESPONSE.redirect('managePublications')
security.declareProtected(Permissions.ModifyPortalContent, 'manage_editSubscription')
security.declareProtected(Permissions.ModifyPortalContent,
'manage_editSubscription')
def manage_editSubscription(self, title, publication_url, subscription_url,
destination_path, query, xml_mapping, conduit, gpg_key, id_generator,
gid_generator, RESPONSE=None):
gid_generator,login='', password='', authentication_format='',
authentication_type='', RESPONSE=None):
"""
modify a subscription
"""
......@@ -238,10 +254,15 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
sub.setSubscriptionUrl(subscription_url)
sub.setIdGenerator(id_generator)
sub.setGidGenerator(gid_generator)
sub.setLogin(login)
sub.setPassword(password)
sub.setAuthenticationFormat(authentication_format)
sub.setAuthenticationType(authentication_type)
if RESPONSE is not None:
RESPONSE.redirect('manageSubscriptions')
security.declareProtected(Permissions.ModifyPortalContent, 'manage_deletePublication')
security.declareProtected(Permissions.ModifyPortalContent,
'manage_deletePublication')
def manage_deletePublication(self, title, RESPONSE=None):
"""
delete a publication
......@@ -252,7 +273,8 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
if RESPONSE is not None:
RESPONSE.redirect('managePublications')
security.declareProtected(Permissions.ModifyPortalContent, 'manage_deleteSubscription')
security.declareProtected(Permissions.ModifyPortalContent,
'manage_deleteSubscription')
def manage_deleteSubscription(self, title, RESPONSE=None):
"""
delete a subscription
......@@ -263,7 +285,8 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
if RESPONSE is not None:
RESPONSE.redirect('manageSubscriptions')
security.declareProtected(Permissions.ModifyPortalContent, 'manage_resetPublication')
security.declareProtected(Permissions.ModifyPortalContent,
'manage_resetPublication')
def manage_resetPublication(self, title, RESPONSE=None):
"""
reset a publication
......@@ -273,7 +296,8 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
if RESPONSE is not None:
RESPONSE.redirect('managePublications')
security.declareProtected(Permissions.ModifyPortalContent, 'manage_resetSubscription')
security.declareProtected(Permissions.ModifyPortalContent,
'manage_resetSubscription')
def manage_resetSubscription(self, title, RESPONSE=None):
"""
reset a subscription
......@@ -284,7 +308,8 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
if RESPONSE is not None:
RESPONSE.redirect('manageSubscriptions')
security.declareProtected(Permissions.ModifyPortalContent, 'manage_syncSubscription')
security.declareProtected(Permissions.ModifyPortalContent,
'manage_syncSubscription')
def manage_syncSubscription(self, title, RESPONSE=None):
"""
reset a subscription
......@@ -293,7 +318,8 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
if RESPONSE is not None:
RESPONSE.redirect('manageSubscriptions')
security.declareProtected(Permissions.AccessContentsInformation,'getPublicationList')
security.declareProtected(Permissions.AccessContentsInformation,
'getPublicationList')
def getPublicationList(self):
"""
Return a list of publications
......@@ -303,7 +329,8 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
object_list = filter(lambda x: x.id.find('pub')==0,object_list)
return object_list
security.declareProtected(Permissions.AccessContentsInformation,'getPublication')
security.declareProtected(Permissions.AccessContentsInformation,
'getPublication')
def getPublication(self, title):
"""
Return the publications with this id
......@@ -313,7 +340,8 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
return p
return None
security.declareProtected(Permissions.AccessContentsInformation,'getObjectContainer')
security.declareProtected(Permissions.AccessContentsInformation,
'getObjectContainer')
def getObjectContainer(self):
"""
this returns the external mount point if there is one
......@@ -325,7 +353,8 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
folder = root.external_mount_point
return folder
security.declareProtected(Permissions.AccessContentsInformation,'getSubscriptionList')
security.declareProtected(Permissions.AccessContentsInformation,
'getSubscriptionList')
def getSubscriptionList(self):
"""
Return a list of publications
......@@ -345,7 +374,8 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
return None
security.declareProtected(Permissions.AccessContentsInformation,'getSynchronizationList')
security.declareProtected(Permissions.AccessContentsInformation,
'getSynchronizationList')
def getSynchronizationList(self):
"""
Returns the list of subscriptions and publications
......@@ -353,7 +383,8 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
"""
return self.getSubscriptionList() + self.getPublicationList()
security.declareProtected(Permissions.AccessContentsInformation,'getSubscriberList')
security.declareProtected(Permissions.AccessContentsInformation,
'getSubscriberList')
def getSubscriberList(self):
"""
Returns the list of subscribers and subscriptions
......@@ -364,13 +395,15 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
s_list += publication.getSubscriberList()
return s_list
security.declareProtected(Permissions.AccessContentsInformation,'getConflictList')
security.declareProtected(Permissions.AccessContentsInformation,
'getConflictList')
def getConflictList(self, context=None):
"""
Retrieve the list of all conflicts
Here the list is as follow :
[conflict_1,conflict2,...] where conflict_1 is like:
['publication',publication_id,object.getPath(),property_id,publisher_value,subscriber_value]
['publication',publication_id,object.getPath(),property_id,
publisher_value,subscriber_value]
"""
path = self.resolveContext(context)
conflict_list = []
......@@ -385,7 +418,8 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
conflict_list += [conflict.__of__(subscriber)]
for subscription in self.getSubscriptionList():
sub_conflict_list = subscription.getConflictList()
LOG('SynchronizationTool.getConflictList, sub_conflict_list',0,sub_conflict_list)
LOG('SynchronizationTool.getConflictList, sub_conflict_list',0,
sub_conflict_list)
for conflict in sub_conflict_list:
if isinstance(conflict,str):
import pdb; pdb.set_trace()
......@@ -402,7 +436,8 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
# return new_list
return conflict_list
security.declareProtected(Permissions.AccessContentsInformation,'getDocumentConflictList')
security.declareProtected(Permissions.AccessContentsInformation,
'getDocumentConflictList')
def getDocumentConflictList(self, context=None):
"""
Retrieve the list of all conflicts for a given document
......@@ -411,7 +446,8 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
return self.getConflictList(context)
security.declareProtected(Permissions.AccessContentsInformation,'getSynchronizationState')
security.declareProtected(Permissions.AccessContentsInformation,
'getSynchronizationState')
def getSynchronizationState(self, context):
"""
context : the context on which we are looking for state
......@@ -464,7 +500,8 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
state_list += [[subscriber,state]]
return state_list
security.declareProtected(Permissions.ModifyPortalContent, 'applyPublisherValue')
security.declareProtected(Permissions.ModifyPortalContent,
'applyPublisherValue')
def applyPublisherValue(self, conflict):
"""
after a conflict resolution, we have decided
......@@ -494,7 +531,8 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
directory._delObject(copy_id)
signature.setStatus(self.PUB_CONFLICT_MERGE)
security.declareProtected(Permissions.ModifyPortalContent, 'applyPublisherDocument')
security.declareProtected(Permissions.ModifyPortalContent,
'applyPublisherDocument')
def applyPublisherDocument(self, conflict):
"""
apply the publisher value for all conflict of the given document
......@@ -506,7 +544,8 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
LOG('applyPublisherDocument, applying on conflict: ',0,conflict)
c.applyPublisherValue()
security.declareProtected(Permissions.AccessContentsInformation, 'getPublisherDocumentPath')
security.declareProtected(Permissions.AccessContentsInformation,
'getPublisherDocumentPath')
def getPublisherDocumentPath(self, conflict):
"""
apply the publisher value for all conflict of the given document
......@@ -514,7 +553,8 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
subscriber = conflict.getSubscriber()
return conflict.getObjectPath()
security.declareProtected(Permissions.AccessContentsInformation, 'getPublisherDocument')
security.declareProtected(Permissions.AccessContentsInformation,
'getPublisherDocument')
def getPublisherDocument(self, conflict):
"""
apply the publisher value for all conflict of the given document
......@@ -548,7 +588,8 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
directory._delObject(object_id)
# Import the conduit and get it
conduit_name = subscriber.getConduit()
conduit_module = __import__('.'.join([Conduit.__name__, conduit_name]), globals(), locals(), [''])
conduit_module = __import__('.'.join([Conduit.__name__, conduit_name]),
globals(), locals(), [''])
conduit = getattr(conduit_module, conduit_name)()
conduit.addNode(xml=publisher_xml,object=directory,object_id=object_id)
subscriber_document = directory._getOb(object_id)
......@@ -570,7 +611,8 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
object_id = repotool._getId(docid, new_rev)
return object_id
security.declareProtected(Permissions.AccessContentsInformation, 'getSubscriberDocumentPath')
security.declareProtected(Permissions.AccessContentsInformation,
'getSubscriberDocumentPath')
def getSubscriberDocumentPath(self, conflict):
"""
apply the publisher value for all conflict of the given document
......@@ -581,12 +623,20 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
subscriber = conflict.getSubscriber()
publisher_object_path = conflict.getObjectPath()
publisher_object = self.unrestrictedTraverse(publisher_object_path)
publisher_xml = self.getXMLObject(object=publisher_object,xml_mapping = subscriber.getXMLMapping())
publisher_xml = self.getXMLObject(object=publisher_object,
xml_mapping = subscriber.getXMLMapping())
directory = publisher_object.aq_inner.aq_parent
object_id = self._getCopyId(publisher_object)
# Import the conduit and get it
conduit_name = subscriber.getConduit()
conduit_module = __import__('.'.join([Conduit.__name__, conduit_name]), globals(), locals(), [''])
if conduit_name.startswith('Products'):
path = conduit_name
conduit_name = conduit_name.split('.')[-1]
conduit_module = __import__(path, globals(), locals(), [''])
conduit = getattr(conduit_module, conduit_name)()
else:
conduit_module = __import__('.'.join([Conduit.__name__, conduit_name]),
globals(), locals(), [''])
conduit = getattr(conduit_module, conduit_name)()
conduit.addNode(xml=publisher_xml,object=directory,object_id=object_id)
subscriber_document = directory._getOb(object_id)
......@@ -598,7 +648,8 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
conflict.setCopyPath(copy_path)
return copy_path
security.declareProtected(Permissions.AccessContentsInformation, 'getSubscriberDocument')
security.declareProtected(Permissions.AccessContentsInformation,
'getSubscriberDocument')
def getSubscriberDocument(self, conflict):
"""
apply the publisher value for all conflict of the given document
......@@ -607,7 +658,8 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
subscriber_object = self.unrestrictedTraverse(subscriber_object_path)
return subscriber_object
security.declareProtected(Permissions.ModifyPortalContent, 'applySubscriberDocument')
security.declareProtected(Permissions.ModifyPortalContent,
'applySubscriberDocument')
def applySubscriberDocument(self, conflict):
"""
apply the subscriber value for all conflict of the given document
......@@ -617,7 +669,8 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
if c.getSubscriber() == subscriber:
c.applySubscriberValue()
security.declareProtected(Permissions.ModifyPortalContent, 'applySubscriberValue')
security.declareProtected(Permissions.ModifyPortalContent,
'applySubscriberValue')
def applySubscriberValue(self, conflict,object=None):
"""
after a conflict resolution, we have decided
......@@ -636,7 +689,8 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
signature = subscriber.getSignature(object.getId()) # XXX may be change for rid
# Import the conduit and get it
conduit_name = subscriber.getConduit()
conduit_module = __import__('.'.join([Conduit.__name__, conduit_name]), globals(), locals(), [''])
conduit_module = __import__('.'.join([Conduit.__name__, conduit_name]),
globals(), locals(), [''])
conduit = getattr(conduit_module, conduit_name)()
for xupdate in conflict.getXupdateList():
conduit.updateNode(xml=xupdate,object=object,force=1)
......@@ -656,15 +710,18 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
directory._delObject(copy_id)
signature.setStatus(self.PUB_CONFLICT_MERGE)
security.declareProtected(Permissions.ModifyPortalContent, 'managePublisherValue')
def managePublisherValue(self, subscription_url, property_id, object_path, RESPONSE=None):
security.declareProtected(Permissions.ModifyPortalContent,
'managePublisherValue')
def managePublisherValue(self, subscription_url, property_id, object_path,
RESPONSE=None):
"""
Do whatever needed in order to store the local value on
the remote server
Suggestion (API)
add method to view document with applied xupdate
of a given subscriber XX (ex. viewSubscriberDocument?path=ddd&subscriber_id=dddd)
of a given subscriber XX
(ex. viewSubscriberDocument?path=ddd&subscriber_id=dddd)
Version=Version CPS
"""
# Retrieve the conflict object
......@@ -681,8 +738,10 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
if RESPONSE is not None:
RESPONSE.redirect('manageConflicts')
security.declareProtected(Permissions.ModifyPortalContent, 'manageSubscriberValue')
def manageSubscriberValue(self, subscription_url, property_id, object_path, RESPONSE=None):
security.declareProtected(Permissions.ModifyPortalContent,
'manageSubscriberValue')
def manageSubscriberValue(self, subscription_url, property_id, object_path,
RESPONSE=None):
"""
Do whatever needed in order to store the remote value locally
and confirmed that the remote box should keep it's value
......@@ -700,7 +759,8 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
if RESPONSE is not None:
RESPONSE.redirect('manageConflicts')
security.declareProtected(Permissions.ModifyPortalContent, 'manageSubscriberDocument')
security.declareProtected(Permissions.ModifyPortalContent,
'manageSubscriberDocument')
def manageSubscriberDocument(self, subscription_url, object_path):
"""
"""
......@@ -711,7 +771,8 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
break
self.managePublisherDocument(object_path)
security.declareProtected(Permissions.ModifyPortalContent, 'managePublisherDocument')
security.declareProtected(Permissions.ModifyPortalContent,
'managePublisherDocument')
def managePublisherDocument(self, object_path):
"""
"""
......@@ -742,7 +803,8 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
return context.getPhysicalPath()
security.declarePublic('sendResponse')
def sendResponse(self, to_url=None, from_url=None, sync_id=None,xml=None, domain=None, send=1):
def sendResponse(self, to_url=None, from_url=None, sync_id=None,xml=None,
domain=None, send=1):
"""
We will look at the url and we will see if we need to send mail, http
response, or just copy to a file.
......@@ -760,7 +822,9 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
decrypted.write(xml)
decrypted.close()
(status,output)=commands.getstatusoutput('gzip /tmp/%s' % filename)
(status,output)=commands.getstatusoutput('gpg --yes --homedir /var/lib/zope/Products/ERP5SyncML/gnupg_keys -r "%s" -se /tmp/%s.gz' % (gpg_key,filename))
(status,output)=commands.getstatusoutput('gpg --yes --homedir \
/var/lib/zope/Products/ERP5SyncML/gnupg_keys -r "%s" -se \
/tmp/%s.gz' % (gpg_key,filename))
LOG('readResponse, gpg output:',0,output)
encrypted = file('/tmp/%s.gz.gpg' % filename,'r')
xml = encrypted.read()
......@@ -775,7 +839,7 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
return None
# we will send an http response
domain = aq_base(domain)
LOG('sendResponse, will start sendHttpResponse, xml\n',0,xml)
LOG('sendResponse, will start sendHttpResponse, xml',0,'')
self.activate(activity='RAMQueue').sendHttpResponse(sync_id=sync_id,
to_url=to_url,
xml=xml, domain=domain)
......@@ -814,7 +878,8 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
pass_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
auth_handler = urllib2.HTTPBasicAuthHandler(pass_mgr)
proxy_auth_handler = urllib2.ProxyBasicAuthHandler(pass_mgr)
opener = urllib2.build_opener(proxy_handler, proxy_auth_handler,auth_handler,urllib2.HTTPHandler)
opener = urllib2.build_opener(proxy_handler, proxy_auth_handler,
auth_handler, urllib2.HTTPHandler)
urllib2.install_opener(opener)
to_encode = {'text':xml,'sync_id':sync_id}
encoded = urllib.urlencode(to_encode)
......@@ -825,7 +890,8 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
try:
result = urllib2.urlopen(request).read()
except socket.error, msg:
self.activate(activity='RAMQueue').sendHttpResponse(to_url=to_url,sync_id=sync_id,xml=xml,domain=domain)
self.activate(activity='RAMQueue').sendHttpResponse(to_url=to_url,
sync_id=sync_id, xml=xml, domain=domain)
LOG('sendHttpResponse, socket ERROR:',0,msg)
return
......@@ -853,7 +919,6 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
# Login as a manager to make sure we can create objects
uf = self.acl_users
user = UnrestrictedUser('syncml','syncml',['Manager','Member'],'')
#user = uf.getUserById('syncml').__of__(uf)
newSecurityManager(None, user)
message_list = self.portal_activities.getMessageList()
LOG('sync, message_list:',0,message_list)
......@@ -871,12 +936,12 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
LOG('readResponse, ',0,'starting')
LOG('readResponse, self.getPhysicalPath: ',0,self.getPhysicalPath())
LOG('readResponse, sync_id: ',0,sync_id)
#LOG('readResponse, text:',0,text)
# Login as a manager to make sure we can create objects
uf = self.acl_users
user = uf.getUserById('syncml').__of__(uf)
user = UnrestrictedUser('syncml','syncml',['Manager','Member'],'')
newSecurityManager(None, user)
status_code = None
if text is not None:
# XXX We will look everywhere for a publication/subsription with
......@@ -896,45 +961,37 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
encrypted = file('/tmp/%s.gz.gpg' % filename,'w')
encrypted.write(text)
encrypted.close()
(status,output)=commands.getstatusoutput('gpg --homedir /var/lib/zope/Products/ERP5SyncML/gnupg_keys -r "%s" --decrypt /tmp/%s.gz.gpg > /tmp/%s.gz' % (gpg_key,filename,filename))
LOG('readResponse, gpg output:',0,output)
(status,output)=commands.getstatusoutput('gpg --homedir \
/var/lib/zope/Products/ERP5SyncML/gnupg_keys -r "%s" --decrypt \
/tmp/%s.gz.gpg > /tmp/%s.gz' % (gpg_key, filename, filename))
LOG('readResponse, gpg output:', 0, output)
(status,output)=commands.getstatusoutput('gunzip /tmp/%s.gz' % filename)
decrypted = file('/tmp/%s' % filename,'r')
text = decrypted.read()
LOG('readResponse, text:',0,text)
LOG('readResponse, text:', 0, text)
decrypted.close()
commands.getstatusoutput('rm -f /tmp/%s' % filename)
commands.getstatusoutput('rm -f /tmp/%s.gz.gpg' % filename)
# Get the target and then find the corresponding publication or
# Subscription
LOG('readResponse, xml before parseSTring\n',0,text)
xml = parseString(text)
#XXX this function is not very optimized and should be improved
url = self.getTarget(xml)
for publication in self.getPublicationList():
if publication.getPublicationUrl()==url and publication.getTitle()==sync_id:
if publication.getPublicationUrl()==url and \
publication.getTitle()==sync_id:
result = self.PubSync(sync_id,xml)
# Then encrypt the message
xml = result['xml']
#must be commented because this method is alredy called
#xml = self.sendResponse(xml=xml,domain=publication,send=0)
return xml
for subscription in self.getSubscriptionList():
if subscription.getSubscriptionUrl()==url and \
subscription.getTitle()==sync_id:
next_status = self.getNextSyncBodyStatus(xml, None)
if next_status is not None:
status_code = self.getStatusCode(next_status)
LOG('readResponse status code :',0,status_code)
if status_code == self.UNAUTHORIZED or \
status_code == self.AUTH_REQUIRED:
LOG('readResponse', 0, 'Authentication required')
raise ValueError, "Authentication required"
else:
result = self.activate(activity='RAMQueue').SubSync(sync_id,xml)
result = self.activate(activity='RAMQueue').SubSync(sync_id,
text)
#result = self.SubSync(sync_id,xml)
# we use from only if we have a file
......@@ -953,14 +1010,16 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
xml = None
return xml
security.declareProtected(Permissions.ModifyPortalContent, 'getPublicationIdFromTitle')
security.declareProtected(Permissions.ModifyPortalContent,
'getPublicationIdFromTitle')
def getPublicationIdFromTitle(self, title):
"""
simply return an id from a title
"""
return 'pub_' + title
security.declareProtected(Permissions.ModifyPortalContent, 'getPublicationIdFromTitle')
security.declareProtected(Permissions.ModifyPortalContent,
'getPublicationIdFromTitle')
def getSubscriptionIdFromTitle(self, title):
"""
simply return an id from a title
......@@ -973,7 +1032,8 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
"""
# Import the conduit and get it
from Products.ERP5SyncML import Conduit
conduit_module = __import__('.'.join([Conduit.__name__, conduit]), globals(), locals(), [''])
conduit_module = __import__('.'.join([Conduit.__name__, conduit]),
globals(), locals(), [''])
conduit_object = getattr(conduit_module, conduit)()
return conduit_object.addNode(**kw)
......
......@@ -47,7 +47,8 @@ from zLOG import LOG
class XMLSyncUtilsMixin(SyncCode):
def SyncMLHeader(self, session_id, msg_id, target, source, target_name=None,
source_name=None):
source_name=None, dataCred=None, authentication_format='b64',
authentication_type='syncml:auth-basic'):
"""
Since the Header is always almost the same, this is the
way to set one quickly.
......@@ -61,14 +62,22 @@ class XMLSyncUtilsMixin(SyncCode):
xml(' <MsgID>%s</MsgID>\n' % msg_id)
xml(' <Target>\n')
xml(' <LocURI>%s</LocURI>\n' % target)
if target_name is not None:
if target_name not in (None, ''):
xml(' <LocName>%s</LocName>\n' %target_name)
xml(' </Target>\n')
xml(' <Source>\n')
xml(' <LocURI>%s</LocURI>\n' % source)
if source_name is not None:
if source_name not in (None, ''):
xml(' <LocName>%s</LocName>\n' % source_name)
xml(' </Source>\n')
if dataCred not in (None, ''):
xml(' <Cred>\n')
xml(' <Meta>\n')
xml(' <Format>%s</Format>\n' % authentication_format)
xml(' <Type>%s</Type>\n' % authentication_type)
xml(' </Meta>\n')
xml(' <Data>%s</Data>\n' % dataCred)
xml(' </Cred>\n')
xml(' </SyncHdr>\n')
xml_a = ''.join(xml_list)
return xml_a
......@@ -103,7 +112,7 @@ class XMLSyncUtilsMixin(SyncCode):
return xml_a
def SyncMLStatus(self, cmd_id, target_ref, source_ref, sync_code,
next_anchor):
next_anchor=None):
"""
Since the Status section is always almost the same, this is the
way to set one quickly.
......@@ -115,6 +124,7 @@ class XMLSyncUtilsMixin(SyncCode):
xml(' <TargetRef>%s</TargetRef>\n' % target_ref)
xml(' <SourceRef>%s</SourceRef>\n' % source_ref)
xml(' <Data>%s</Data>\n' % sync_code)
if next_anchor is not None:
xml(' <Item>\n')
xml(' <Data>\n')
xml(' <Anchor xmlns=\'syncml:metinf\'>\n')
......@@ -416,27 +426,6 @@ class XMLSyncUtilsMixin(SyncCode):
return int(subnode.childNodes[0].data)
return None
#def getStatusCode(self, xml):
# """
# Return the value of the alert code inside the xml_stream
# """
# # Get informations from the body
# first_node = xml.childNodes[0]
# if first_node.nodeName != "SyncML":
# print "This is not a SyncML message"
#
# client_body = first_node.childNodes[3]
# if client_body.nodeName != "SyncBody":
# print "This is not a SyncML Body"
#
# for subnode in client_body.childNodes:
# if subnode.nodeName=='Status':
# for subnode2 in subnode.childNodes:
# if subnode2.nodeType == subnode.ELEMENT_NODE and subnode2.nodeName == 'Data':
# return int(subnode2.childNodes[0].data)
# return None
def getStatusCommand(self, xml):
"""
Return the value of the command inside the xml_stream
......@@ -448,6 +437,44 @@ class XMLSyncUtilsMixin(SyncCode):
return subnode.childNodes[0].data
return None
def getCred(self, xml):
"""
return the credential information : type, format and data
"""
format=''
type=''
data=''
first_node = xml.childNodes[0]
if first_node.nodeName != "SyncML":
print "This is not a SyncML message"
# Get informations from the header
xml_header = first_node.childNodes[1]
if xml_header.nodeName != "SyncHdr":
LOG('PubSyncModif',0,'This is not a SyncML Header')
raise ValueError, "Sorry, This is not a SyncML Header"
for subnode in xml_header.childNodes:
if subnode.nodeType == subnode.ELEMENT_NODE and subnode.nodeName=='Cred':
for subnode2 in subnode.childNodes:
if subnode2.nodeType == subnode2.ELEMENT_NODE and \
subnode2.nodeName == 'Meta':
for subnode3 in subnode2.childNodes:
if subnode3.nodeType == subnode3.ELEMENT_NODE and \
subnode3.nodeName == 'Format':
if len(subnode3.childNodes) > 0:
format=subnode3.childNodes[0].data
if subnode3.nodeType == subnode3.ELEMENT_NODE and \
subnode3.nodeName == 'Type':
if len(subnode3.childNodes) > 0:
type=subnode3.childNodes[0].data
if subnode2.nodeType == subnode2.ELEMENT_NODE and \
subnode2.nodeName == 'Data':
if len(subnode2.childNodes) > 0:
data=subnode2.childNodes[0].data
return (format, type, data)
def getAlertCode(self, xml_stream):
"""
Return the value of the alert code inside the full syncml message
......@@ -739,13 +766,13 @@ class XMLSyncUtilsMixin(SyncCode):
# object_gid = gid_generator()
force = 0
if syncml_data.count('\n') < self.MAX_LINES and not object.id.startswith('.'): # If not we have to cut
LOG('getSyncMLData',0,'xml_mapping: %s' % str(domain.xml_mapping))
LOG('getSyncMLData',0,'code: %s' % str(self.getAlertCode(remote_xml)))
LOG('getSyncMLData',0,'gid_list: %s' % str(local_gid_list))
LOG('getSyncMLData',0,'hasSignature: %s' % str(subscriber.hasSignature(object_gid)))
LOG('getSyncMLData',0,'alert_code == slowsync: %s' % str(self.getAlertCode(remote_xml)==self.SLOW_SYNC))
#LOG('getSyncMLData',0,'xml_mapping: %s' % str(domain.xml_mapping))
#LOG('getSyncMLData',0,'code: %s' % str(self.getAlertCode(remote_xml)))
#LOG('getSyncMLData',0,'gid_list: %s' % str(local_gid_list))
#LOG('getSyncMLData',0,'hasSignature: %s' % str(subscriber.hasSignature(object_gid)))
#LOG('getSyncMLData',0,'alert_code == slowsync: %s' % str(self.getAlertCode(remote_xml)==self.SLOW_SYNC))
signature = subscriber.getSignature(object_gid)
LOG('getSyncMLData',0,'current object: %s' % str(object.getId()))
#LOG('getSyncMLData',0,'current object: %s' % str(object.getId()))
# Here we first check if the object was modified or not by looking at dates
if signature is not None:
signature.checkSynchronizationNeeded(object)
......@@ -774,7 +801,7 @@ class XMLSyncUtilsMixin(SyncCode):
rest_string = xml_string[len(short_string):]
#LOG('XMLSyncUtils',0,'rest_string: %s' % str(rest_string))
i += 1
LOG('getSyncMLData',0,'setPartialXML with: %s' % str(rest_string))
#LOG('getSyncMLData',0,'setPartialXML with: %s' % str(rest_string))
signature.setPartialXML(rest_string)
status =self.PARTIAL
signature.setAction('Add')
......@@ -787,8 +814,8 @@ class XMLSyncUtilsMixin(SyncCode):
elif signature.getStatus()==self.NOT_SYNCHRONIZED \
or signature.getStatus()==self.PUB_CONFLICT_MERGE: # We don't have synchronized this object yet
xml_object = self.getXMLObject(object=object,xml_mapping=domain.xml_mapping)
LOG('getSyncMLData',0,'checkMD5: %s' % str(signature.checkMD5(xml_object)))
LOG('getSyncMLData',0,'getStatus: %s' % str(signature.getStatus()))
#LOG('getSyncMLData',0,'checkMD5: %s' % str(signature.checkMD5(xml_object)))
#LOG('getSyncMLData',0,'getStatus: %s' % str(signature.getStatus()))
if signature.getStatus()==self.PUB_CONFLICT_MERGE:
xml_confirmation += self.SyncMLConfirmation(cmd_id,object.id,
self.CONFLICT_MERGE,'Replace')
......@@ -821,7 +848,7 @@ class XMLSyncUtilsMixin(SyncCode):
signature.setTempXML(xml_object)
# Now we can apply the xupdate from the subscriber
subscriber_xupdate = signature.getSubscriberXupdate()
LOG('getSyncMLData subscriber_xupdate',0,subscriber_xupdate)
#LOG('getSyncMLData subscriber_xupdate',0,subscriber_xupdate)
if subscriber_xupdate is not None:
old_xml = signature.getXML()
conduit.updateNode(xml=subscriber_xupdate, object=object,
......@@ -1080,7 +1107,6 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
if xml_header.nodeName != "SyncHdr":
LOG('PubSyncModif',0,'This is not a SyncML Header')
raise ValueError, "Sorry, This is not a SyncML Header"
return
subscriber = domain # If we are the client, this is fine
simulate = 0 # used by applyActionList, should be 0 for client
......@@ -1103,11 +1129,12 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
if last_xml != '':
has_response = 1
if domain.domain_type == self.PUB: # We always reply
self.sendResponse(from_url=domain.publication_url, to_url=subscriber.subscription_url,
sync_id=domain.getTitle(), xml=last_xml,domain=domain)
self.sendResponse(from_url=domain.publication_url,
to_url=subscriber.subscription_url, sync_id=domain.getTitle(),
xml=last_xml,domain=domain)
elif domain.domain_type == self.SUB:
self.sendResponse(from_url=domain.subscription_url, to_url=domain.publication_url,
sync_id=domain.getTitle(), xml=last_xml,domain=domain)
self.sendResponse(from_url=domain.subscription_url,
to_url=domain.publication_url, sync_id=domain.getTitle(), xml=last_xml,domain=domain)
return {'has_response':has_response,'xml':last_xml}
subscriber.setLastSentMessage('')
......@@ -1135,7 +1162,7 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
subscriber=subscriber,
remote_xml=remote_xml,
conduit=conduit, simulate=simulate)
LOG('SyncModif, has_next_action:',0,has_next_action)
#LOG('SyncModif, has_next_action:',0,has_next_action)
xml_list = []
xml = xml_list.append
......
......@@ -127,6 +127,36 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
<input type="text" name="gid_generator" value="<dtml-var getGidGenerator>" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Authentication Required
</label></div>
</td>
<td align="left" valign="top">
<input type="checkbox" name="auth_required" value="1" <dtml-if expr="isAuthenticationRequired()">CHECKED</dtml-if>>
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Format authentication
</label></div>
</td>
<td align="left" valign="top">
<input type="text" name="authentication_format" value="<dtml-var getAuthenticationFormat>" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Type authentication
</label></div>
</td>
<td align="left" valign="top">
<input type="text" name="authentication_type" value="<dtml-var getAuthenticationType>" size="40" />
</td>
</tr>
</table>
<table>
<tr>
......
......@@ -137,6 +137,46 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
<input type="text" name="gid_generator" value="<dtml-var getGidGenerator>" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Login
</label></div>
</td>
<td align="left" valign="top">
<input type="text" name="login" value="<dtml-var getLogin>" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Password
</label></div>
</td>
<td align="left" valign="top">
<input type="password" name="password" value="<dtml-var getPassword>" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Format authentication
</label></div>
</td>
<td align="left" valign="top">
<input type="text" name="authentication_format" value="<dtml-var getAuthenticationFormat>" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Type authentication
</label></div>
</td>
<td align="left" valign="top">
<input type="text" name="authentication_type" value="<dtml-var getAuthenticationType>" size="40" />
</td>
</tr>
</table>
<table>
<tr>
......
......@@ -123,6 +123,36 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
<input type="text" name="gid_generator" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Authentication Required
</label></div>
</td>
<td align="left" valign="top">
<input type="checkbox" name="auth_required" value="1">
</td>
</tr>
<tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Format authentication
</label></div>
</td>
<td align="left" valign="top">
<input type="text" name="authentication_format" size="40" />
</td>
</tr>
<td align="left" valign="top">
<div class="form-label">
Type authentication
</label></div>
</td>
<td align="left" valign="top">
<input type="text" name="authentication_type" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
</td>
......
......@@ -133,6 +133,45 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
<input type="text" name="gid_generator" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Login
</label></div>
</td>
<td align="left" valign="top">
<input type="text" name="login" size="40" /> </td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Password
</label></div>
</td>
<td align="left" valign="top">
<input type="password" name="password" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Format authentication
</label></div>
</td>
<td align="left" valign="top">
<input type="text" name="authentication_format" size="40" />
</td>
</tr>
<td align="left" valign="top">
<div class="form-label">
Type authentication
</label></div>
</td>
<td align="left" valign="top">
<input type="text" name="authentication_type" size="40" />
</td>
</tr>
<tr>
<tr>
<td align="left" valign="top">
</td>
......
......@@ -42,6 +42,10 @@ from Products.ERP5SyncML.Conduit.ERP5Conduit import ERP5Conduit
from Products.ERP5SyncML.SyncCode import SyncCode
from zLOG import LOG
try:
from base64 import b64encode, b64decode
except ImportError:
from base64 import encodestring as b64encode, decodestring as b64decode
class TestERP5SyncML(ERP5TypeTestCase):
# Different variables used for this test
......@@ -51,7 +55,7 @@ class TestERP5SyncML(ERP5TypeTestCase):
last_name1 = 'Robin'
# At the beginning, I was using iso-8859-15 strings, but actually
# erp5 is using utf-8 everywhere
#description1 = 'description1 --- $sdfr_sdfsdf_oisfsopf'
#description1 = 'description1 --- $sdfrç_sdfsçdf_oisfsopf'
description1 = 'description1 --- $sdfr\xc3\xa7_sdfs\xc3\xa7df_oisfsopf'
lang1 = 'fr'
format2 = 'html'
......@@ -59,12 +63,12 @@ class TestERP5SyncML(ERP5TypeTestCase):
format4 = 'txt'
first_name2 = 'Jean-Paul'
last_name2 = 'Smets'
#description2 = 'description2@ $*< <<< ----- >>>></title>&oekd'
#description2 = 'description2éà@ $*< <<< ----- >>>></title>&oekd'
description2 = 'description2\xc3\xa9\xc3\xa0@ $*< <<< ----- >>>></title>&oekd'
lang2 = 'en'
first_name3 = 'Yoshinori'
last_name3 = 'Okuji'
#description3 = 'description3 sdf__sdf_df___&&]]]'
#description3 = 'description3 çsdf__sdfççç_df___&&é]]]°°°°°°'
description3 = 'description3 \xc3\xa7sdf__sdf\xc3\xa7\xc3\xa7\xc3\xa7_df___&&\xc3\xa9]]]\xc2\xb0\xc2\xb0\xc2\xb0\xc2\xb0\xc2\xb0\xc2\xb0'
#description4 = 'description4 sdflkmooo^^^^]]]]]{{{{{{{'
description4 = 'description4 sdflkmooo^^^^]]]]]{{{{{{{'
......@@ -182,10 +186,10 @@ class TestERP5SyncML(ERP5TypeTestCase):
def login(self, quiet=0):
uf = self.getPortal().acl_users
uf._doAddUser('seb', '', ['Manager'], [])
uf._doAddUser('fab', 'myPassword', ['Manager'], [])
uf._doAddUser('ERP5TypeTestCase', '', ['Manager'], [])
uf._doAddUser('syncml', '', ['Manager'], [])
user = uf.getUserById('seb').__of__(uf)
user = uf.getUserById('fab').__of__(uf)
newSecurityManager(None, user)
def populatePersonServer(self, quiet=0, run=run_all_test):
......@@ -317,7 +321,6 @@ class TestERP5SyncML(ERP5TypeTestCase):
to define it here because it is specific to the unit testing
"""
portal_sync = self.getSynchronizationTool()
#portal_sync.email = None # XXX To be removed
subscription = portal_sync.getSubscription(id)
publication = None
for publication in portal_sync.getPublicationList():
......@@ -984,7 +987,7 @@ class TestERP5SyncML(ERP5TypeTestCase):
self.failUnless(person_s.getDescription()==self.description3)
self.failUnless(person_c1.getDescription()==self.description3)
def test_25_MultiNodeConflict(self, quiet=0, run=run_all_test):
def test_25_MultiNodeConflict(self, quiet=0, run=1):
"""
We will create conflicts with 3 differents nodes, and we will
solve it by taking one full version of documents.
......@@ -1097,9 +1100,9 @@ class TestERP5SyncML(ERP5TypeTestCase):
person_client1 = self.getPersonClient1()
person1_c = person_client1._getOb(self.id1)
person2_c = person_client1._getOb(self.id2)
person1_s.manage_setLocalRoles('seb',['Manager','Owner'])
person1_s.manage_setLocalRoles('fab',['Manager','Owner'])
person2_s.manage_setLocalRoles('jp',['Manager','Owner'])
person2_s.manage_delLocalRoles(['seb'])
person2_s.manage_delLocalRoles(['fab'])
self.synchronize(self.sub_id1)
self.synchronize(self.sub_id2)
role_1_s = person1_s.get_local_roles()
......@@ -1182,7 +1185,7 @@ class TestERP5SyncML(ERP5TypeTestCase):
def test_30_GetSynchronizationType(self, quiet=0, run=run_all_test):
# We will try to update some simple data, first
# we change on the server side, the on the client side
# we change on the server side, then on the client side
if not run: return
if not quiet:
ZopeTestCase._print('\nTest Get Synchronization Type ')
......@@ -1257,20 +1260,20 @@ class TestERP5SyncML(ERP5TypeTestCase):
self.assertEqual(role_1_s,role_1_c)
self.assertEqual(role_2_s,role_2_c)
def test_32_AddOneWaySubscription(self, quiet=0, run=1):
def test_32_AddOneWaySubscription(self, quiet=0, run=run_all_test):
if not run: return
if not quiet:
ZopeTestCase._print('\nTest Add One Way Subscription ')
LOG('Testing... ',0,'test_32_AddOneWaySubscription')
portal_id = self.getPortalId()
portal_sync = self.getSynchronizationTool()
portal_sync.manage_addSubscription(self.sub_id1,self.publication_url,
self.subscription_url1,'/%s/person_client1' % portal_id,'objectValues',
'','ERP5Conduit','')
portal_sync.manage_addSubscription(self.sub_id1, self.publication_url,
self.subscription_url1, '/%s/person_client1' % portal_id,
'objectValues', '', 'ERP5Conduit', '')
sub = portal_sync.getSubscription(self.sub_id1)
self.failUnless(sub is not None)
def test_33_OneWaySync(self, quiet=0, run=1):
def test_33_OneWaySync(self, quiet=0, run=run_all_test):
"""
We will test if we can synchronize only from to server to the client.
We want to make sure in this case that all modifications on the client
......@@ -1314,6 +1317,234 @@ class TestERP5SyncML(ERP5TypeTestCase):
self.assertEquals(person1_s.getFirstName(),self.first_name1)
def test_34_encoding(self, quiet=0, run=run_all_test):
"""
We will test if we can encode strings with b64encode to encode
the login and password for authenticated sessions
"""
#when there will be other format implemented with encode method,
#there will be tested here
if not run: return
self.test_08_FirstSynchronization(quiet=1,run=1)
if not quiet:
ZopeTestCase._print('\nTest Strings Encoding ')
LOG('Testing... ',0,'test_34_encoding')
#define some strings :
python = 'www.python.org'
awaited_result_python = "d3d3LnB5dGhvbi5vcmc="
long_string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNO\
PQRSTUVWXYZéèçà@^~µ&²0123456789!@#0^&*();:<>,. []{}\xc3\xa7sdf__\
sdf\xc3\xa7\xc3\xa7\xc3\xa7_df___&&\xc3\xa9]]]\xc2\xb0\xc2\xb0\xc2\
\xb0\xc2\xb0\xc2\xb0\xc2\xb0"
#= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZéèçà@^~µ&²012345
#6789!@#0^&*();:<>,. []{}çsdf__sdfççç_df___&&é]]]°°°°°°'"
awaited_result_long_string = 'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZH\
SElKS0xNTk9QUVJTVFVWV1hZWsOpw6jDp8OgQF5+wrUmwrIwMTIzNDU2Nzg5IUAjMF4mKigpOzo8Pi\
wuIFtde33Dp3NkZl9fc2Rmw6fDp8OnX2RmX19fJibDqV1dXcKwwrDCsMKwwrDCsA=='
#test just b64encode
self.assertEqual(b64encode(python), awaited_result_python)
self.assertEqual(b64encode(""), "")
self.assertEqual(b64encode(long_string), awaited_result_long_string)
self.assertEqual(b64decode(awaited_result_python), python)
self.assertEqual(b64decode(""), "")
self.assertEqual(b64decode(awaited_result_long_string), long_string)
# test with the ERP5 functions
portal_sync = self.getSynchronizationTool()
publication = portal_sync.getPublication(self.pub_id)
subscription1 = portal_sync.getSubscription(self.sub_id1)
string_encoded = subscription1.encode('b64', python)
self.assertEqual(string_encoded, awaited_result_python)
string_decoded = subscription1.decode('b64', awaited_result_python)
self.assertEqual(string_decoded, python)
self.failUnless(subscription1.isDecodeEncodeTheSame(string_encoded,
python, 'b64'))
self.failUnless(subscription1.isDecodeEncodeTheSame(string_encoded,
string_decoded, 'b64'))
string_encoded = subscription1.encode('b64', long_string)
self.assertEqual(string_encoded, awaited_result_long_string)
string_decoded = subscription1.decode('b64', awaited_result_long_string)
self.assertEqual(string_decoded, long_string)
self.failUnless(subscription1.isDecodeEncodeTheSame(string_encoded,
long_string, 'b64'))
self.failUnless(subscription1.isDecodeEncodeTheSame(string_encoded,
string_decoded, 'b64'))
self.assertEqual(subscription1.encode('b64', ''), '')
self.assertEqual(subscription1.decode('b64', ''), '')
self.failUnless(subscription1.isDecodeEncodeTheSame(
subscription1.encode('b64', ''), '', 'b64'))
def addAuthenticationToPublication(self, publication_id, login, password,
auth_format, auth_type):
"""
add authentication to the publication
"""
portal_sync = self.getSynchronizationTool()
pub = portal_sync.getPublication(publication_id)
pub.setAuthentication(True)
pub.setLogin(login)
pub.setPassword(password)
pub.setAuthenticationFormat(auth_format)
pub.setAuthenticationType(auth_type)
def addAuthenticationToSubscription(self, subscription_id, login, password,
auth_format, auth_type):
"""
add authentication to the subscription
"""
portal_sync = self.getSynchronizationTool()
sub = portal_sync.getSubscription(subscription_id)
sub.setAuthentication(True)
sub.setAuthenticated(False)
sub.setLogin(login)
sub.setPassword(password)
sub.setAuthenticationFormat(auth_format)
sub.setAuthenticationType(auth_type)
def verifyFirstNameAndLastNameAreSynchronized(self, first_name,
last_name, person_server, person_client):
"""
verify if the first and last name are synchronized
"""
self.failUnless(person_server.getFirstName()==first_name)
self.failUnless(person_server.getLastName()==last_name)
self.failUnless(person_client.getFirstName()==first_name)
self.failUnless(person_client.getLastName()==last_name)
def verifyFirstNameAndLastNameAreNotSynchronized(self, first_name,
last_name, person_server, person_client):
"""
verify that the first and last name are NOT synchronized
"""
self.failUnless(person_server.getFirstName()!=first_name)
self.failUnless(person_server.getLastName()!=last_name)
self.failUnless(person_client.getFirstName()==first_name)
self.failUnless(person_client.getLastName()==last_name)
def test_35_authentication(self, quiet=0, run=1):
"""
we will test
- if we can't synchronize without good authentication for an
autentication required publication.
- if we can synchronize without of with (and bad or good) authentication
for an not required autentication publication
"""
if not run: return
if not quiet:
ZopeTestCase._print('\nTest Authentication ')
LOG('Testing... ',0,'test_35_authentication')
self.test_08_FirstSynchronization(quiet=1,run=1)
# First we do only modification on client
portal_sync = self.getSynchronizationTool()
person_server = self.getPersonServer()
person1_s = person_server._getOb(self.id1)
person_client1 = self.getPersonClient1()
person1_c = person_client1._getOb(self.id1)
kw = {'first_name':self.first_name3,'last_name':self.last_name3}
person1_c.edit(**kw)
#check that it's not synchronize
self.verifyFirstNameAndLastNameAreNotSynchronized(self.first_name3,
self.last_name3, person1_s, person1_c)
self.synchronize(self.sub_id1)
#now it should be synchronize
self.checkSynchronizationStateIsSynchronized()
self.verifyFirstNameAndLastNameAreSynchronized(self.first_name3,
self.last_name3, person1_s, person1_c)
#adding authentication :
self.addAuthenticationToPublication(self.pub_id, 'fab', 'myPassword', 'b64',
'syncml:auth-basic')
# try to synchronize without authentication on the subscription, it
# should failed
kw = {'first_name':self.first_name2,'last_name':self.last_name2}
person1_c.edit(**kw)
self.verifyFirstNameAndLastNameAreNotSynchronized(self.first_name2,
self.last_name2, person1_s, person1_c)
# here, before and after synchronization, the person1_s shoudn't have
# the name as the person1_c because the user isn't authenticated
self.synchronize(self.sub_id1)
self.verifyFirstNameAndLastNameAreNotSynchronized(self.first_name2,
self.last_name2, person1_s, person1_c)
#try to synchronize whith an authentication on both the client and server
self.addAuthenticationToSubscription(self.sub_id1, 'fab', 'myPassword',
'b64', 'syncml:auth-basic')
#now it should be correctly synchronize
self.synchronize(self.sub_id1)
self.checkSynchronizationStateIsSynchronized()
self.verifyFirstNameAndLastNameAreSynchronized(self.first_name2,
self.last_name2, person1_s, person1_c)
#try to synchronize with a bad login and/or password
#test if login is case sensitive (it should be !)
self.addAuthenticationToSubscription(self.sub_id1, 'fAb', 'myPassword',
'b64', 'syncml:auth-basic')
kw = {'first_name':self.first_name1,'last_name':self.last_name1}
person1_c.edit(**kw)
self.synchronize(self.sub_id1)
self.verifyFirstNameAndLastNameAreNotSynchronized(self.first_name1,
self.last_name1, person1_s, person1_c)
#with a paswword case sensitive
self.addAuthenticationToSubscription(self.sub_id1, 'fab', 'mypassword',
'b64', 'syncml:auth-basic')
kw = {'first_name':self.first_name1,'last_name':self.last_name1}
person1_c.edit(**kw)
self.synchronize(self.sub_id1)
self.verifyFirstNameAndLastNameAreNotSynchronized(self.first_name1,
self.last_name1, person1_s, person1_c)
#with the good password
self.addAuthenticationToSubscription(self.sub_id1, 'fab', 'myPassword',
'b64', 'syncml:auth-basic')
#now it should be correctly synchronize
self.synchronize(self.sub_id1)
self.checkSynchronizationStateIsSynchronized()
self.verifyFirstNameAndLastNameAreSynchronized(self.first_name1,
self.last_name1, person1_s, person1_c)
#verify that the login and password with utf8 caracters are accecpted
# add a user with an utf8 login
uf = self.getPortal().acl_users
uf._doAddUser('\xc3\xa9pouet', 'ploum', ['Manager'], []) # \xc3\xa9pouet = épouet
user = uf.getUserById('\xc3\xa9pouet').__of__(uf)
newSecurityManager(None, user)
self.addAuthenticationToPublication(self.pub_id, '\xc3\xa9pouet', 'ploum',
'b64', 'syncml:auth-basic')
#first, try with a wrong login :
self.addAuthenticationToSubscription(self.sub_id1, 'pouet', 'ploum',
'b64', 'syncml:auth-basic')
kw = {'first_name':self.first_name3,'last_name':self.last_name3}
person1_c.edit(**kw)
self.verifyFirstNameAndLastNameAreNotSynchronized(self.first_name3,
self.last_name3, person1_s, person1_c)
self.synchronize(self.sub_id1)
self.verifyFirstNameAndLastNameAreNotSynchronized(self.first_name3,
self.last_name3, person1_s, person1_c)
#now with the good :
self.addAuthenticationToSubscription(self.sub_id1, '\xc3\xa9pouet', 'ploum',
'b64', 'syncml:auth-basic')
self.synchronize(self.sub_id1)
self.verifyFirstNameAndLastNameAreSynchronized(self.first_name3,
self.last_name3, person1_s, person1_c)
self.checkSynchronizationStateIsSynchronized()
if __name__ == '__main__':
framework()
......
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