[Erp5-report] r14439 - in /erp5/trunk/products/ERP5SyncML: ./ Conduit/ dtml/ tests/

nobody at svn.erp5.org nobody at svn.erp5.org
Thu May 10 14:13:53 CEST 2007


Author: seb
Date: Thu May 10 14:13:52 2007
New Revision: 14439

URL: http://svn.erp5.org?rev=14439&view=rev
Log:
fabien finished to implement authentication, and he added tests for it

Modified:
    erp5/trunk/products/ERP5SyncML/Conduit/ERP5Conduit.py
    erp5/trunk/products/ERP5SyncML/Publication.py
    erp5/trunk/products/ERP5SyncML/PublicationSynchronization.py
    erp5/trunk/products/ERP5SyncML/Subscription.py
    erp5/trunk/products/ERP5SyncML/SubscriptionSynchronization.py
    erp5/trunk/products/ERP5SyncML/SynchronizationTool.py
    erp5/trunk/products/ERP5SyncML/XMLSyncUtils.py
    erp5/trunk/products/ERP5SyncML/dtml/managePublications.dtml
    erp5/trunk/products/ERP5SyncML/dtml/manageSubscriptions.dtml
    erp5/trunk/products/ERP5SyncML/dtml/manage_addPublication.dtml
    erp5/trunk/products/ERP5SyncML/dtml/manage_addSubscription.dtml
    erp5/trunk/products/ERP5SyncML/tests/testERP5SyncML.py

Modified: erp5/trunk/products/ERP5SyncML/Conduit/ERP5Conduit.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/Conduit/ERP5Conduit.py?rev=14439&r1=14438&r2=14439&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/Conduit/ERP5Conduit.py (original)
+++ erp5/trunk/products/ERP5SyncML/Conduit/ERP5Conduit.py Thu May 10 14:13:52 2007
@@ -129,11 +129,6 @@
     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 @@
         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 @@
     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 @@
         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 @@
         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 @@
     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')

Modified: erp5/trunk/products/ERP5SyncML/Publication.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/Publication.py?rev=14439&r1=14438&r2=14439&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/Publication.py (original)
+++ erp5/trunk/products/ERP5SyncML/Publication.py Thu May 10 14:13:52 2007
@@ -152,7 +152,9 @@
   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 @@
     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):
     """
@@ -188,6 +193,42 @@
       return the publication url
     """
     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):
     """

Modified: erp5/trunk/products/ERP5SyncML/PublicationSynchronization.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/PublicationSynchronization.py?rev=14439&r1=14438&r2=14439&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/PublicationSynchronization.py (original)
+++ erp5/trunk/products/ERP5SyncML/PublicationSynchronization.py Thu May 10 14:13:52 2007
@@ -34,6 +34,10 @@
 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,17 +47,19 @@
   """
 
   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
     """
     LOG('PubSyncInit',0,'Starting... publication: %s' % str(publication))
-   
+    
     #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,33 +73,83 @@
       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:
-        LOG('PubSyncInit',0,'authentication required')
-	      # 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.incrementMessageId(), 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))
-        cmd_id += 1
-
-        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)
+      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
+          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')
+	        # chal message
+          xml(self.SyncMLChal(cmd_id, "SyncHdr", 
+            publication.getPublicationUrl(), subscriber.getSubscriptionUrl(), 
+            publication.getAuthenticationFormat(), 
+            publication.getAuthenticationType(), self.AUTH_REQUIRED))
+          cmd_id += 1
+
+          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 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 @@
 
         # 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 @@
             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 @@
       # 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 = []
@@ -203,7 +257,15 @@
         result = self.PubSyncInit(publication=publication, 
             xml_client=xml_client, subscriber=subscriber, sync_type=alert_code)
       else:
-        result = self.PubSyncModif(publication, xml_client)
+        #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:
       # This looks like we are starting a synchronization after
       # a conflict resolution by the user

Modified: erp5/trunk/products/ERP5SyncML/Subscription.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/Subscription.py?rev=14439&r1=14438&r2=14439&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/Subscription.py (original)
+++ erp5/trunk/products/ERP5SyncML/Subscription.py Thu May 10 14:13:52 2007
@@ -41,6 +41,11 @@
 
 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 @@
   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 @@
   isPortalContent = 1
   isRADContent = 1
   icon = None
-
   isIndexable = 0
+  user = None
 
   # Declarative properties
   property_sheets = ( PropertySheet.Base
@@ -635,7 +640,8 @@
                               )
 
   # 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 @@
     #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 @@
     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 @@
     """
     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 @@
     # 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 @@
       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
@@ -1073,6 +1130,12 @@
       set the message id to 0
     """
     self.message_id = 0
+  
+  def setMessageId(self, message_id):
+    """
+      set the message id to message_id
+    """
+    self.message_id = message_id
 
   def getLastAnchor(self):
     """
@@ -1230,3 +1293,67 @@
         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

Modified: erp5/trunk/products/ERP5SyncML/SubscriptionSynchronization.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/SubscriptionSynchronization.py?rev=14439&r1=14438&r2=14439&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/SubscriptionSynchronization.py (original)
+++ erp5/trunk/products/ERP5SyncML/SubscriptionSynchronization.py Thu May 10 14:13:52 2007
@@ -44,6 +44,8 @@
     """
     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 @@
 
     # syncml body
     xml(' <SyncBody>\n')
-    subscription.NewAnchor()
-    subscription.initLastMessageId()
 
     # We have to set every object as NOT_SYNCHRONIZED
     subscription.startSynchronization()
@@ -100,13 +100,74 @@
       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:
       RESPONSE.redirect('manageSubscriptions')
     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):
     """

Modified: erp5/trunk/products/ERP5SyncML/SynchronizationTool.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/SynchronizationTool.py?rev=14439&r1=14438&r2=14439&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/SynchronizationTool.py (original)
+++ erp5/trunk/products/ERP5SyncML/SynchronizationTool.py Thu May 10 14:13:52 2007
@@ -62,8 +62,8 @@
 
 
 
-class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronization, 
-                           UniqueObject, Folder):
+class SynchronizationTool( SubscriptionSynchronization, 
+    PublicationSynchronization, UniqueObject, Folder):
   """
     This tool implements the synchronization algorithm
 
@@ -159,9 +159,11 @@
                     + '?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 @@
     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 @@
     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 @@
     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 @@
     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 @@
     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):
+      destination_path, query, xml_mapping, conduit, gpg_key, id_generator,
+      gid_generator,login='', password='', authentication_format='', 
+      authentication_type='', RESPONSE=None):
     """
       modify a subscription
     """
@@ -238,10 +254,15 @@
     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 @@
     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 @@
     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 @@
     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 @@
     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 @@
     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 @@
     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 @@
         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 @@
       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 @@
     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 @@
     """
     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 @@
       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 @@
             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 @@
     #  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 @@
     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 @@
               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 @@
           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 @@
         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 @@
     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,8 +588,9 @@
         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 = getattr(conduit_module, conduit_name)()
+        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)
         for c in self.getConflictList(conflict.getObjectPath()):
@@ -570,24 +611,33 @@
       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
     """
     copy_path = conflict.getCopyPath()
     if copy_path is not None:
-        return copy_path
+      return copy_path
     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(), [''])
-    conduit = getattr(conduit_module, conduit_name)()
+    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)
     subscriber_document._conflict_resolution = 1
@@ -598,7 +648,8 @@
     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 @@
     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 @@
       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 @@
     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 @@
             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 @@
     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 @@
     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 @@
           break
     self.managePublisherDocument(object_path)
   
-  security.declareProtected(Permissions.ModifyPortalContent, 'managePublisherDocument')
+  security.declareProtected(Permissions.ModifyPortalContent, 
+      'managePublisherDocument')
   def managePublisherDocument(self, object_path):
     """
     """
@@ -742,7 +803,8 @@
       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 @@
         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 @@
             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 @@
     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 @@
     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 @@
     # 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 @@
     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,46 +961,38 @@
         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.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 
     elif isinstance(from_url, str):
@@ -953,14 +1010,16 @@
           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 @@
     """
     # 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)
 

Modified: erp5/trunk/products/ERP5SyncML/XMLSyncUtils.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/XMLSyncUtils.py?rev=14439&r1=14438&r2=14439&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/XMLSyncUtils.py (original)
+++ erp5/trunk/products/ERP5SyncML/XMLSyncUtils.py Thu May 10 14:13:52 2007
@@ -47,7 +47,8 @@
 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 @@
     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 @@
     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,13 +124,14 @@
     xml('   <TargetRef>%s</TargetRef>\n' % target_ref)
     xml('   <SourceRef>%s</SourceRef>\n' % source_ref)
     xml('   <Data>%s</Data>\n' % sync_code)
-    xml('   <Item>\n')
-    xml('    <Data>\n')
-    xml('     <Anchor xmlns=\'syncml:metinf\'>\n')
-    xml('      <Next>%s</Next>\n' % next_anchor)
-    xml('     </Anchor>\n')
-    xml('    </Data>\n')
-    xml('   </Item>\n')
+    if next_anchor is not None:
+      xml('   <Item>\n')
+      xml('    <Data>\n')
+      xml('     <Anchor xmlns=\'syncml:metinf\'>\n')
+      xml('      <Next>%s</Next>\n' % next_anchor)
+      xml('     </Anchor>\n')
+      xml('    </Data>\n')
+      xml('   </Item>\n')
     xml('  </Status>\n')
     xml_a = ''.join(xml_list)
     return xml_a
@@ -416,27 +426,6 @@
           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
@@ -447,6 +436,44 @@
         if subnode.nodeType == subnode.ELEMENT_NODE and subnode.nodeName == 'Cmd':
           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):
     """
@@ -739,13 +766,13 @@
       #  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 @@
               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 @@
         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 @@
             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 @@
     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 @@
       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 @@
                                          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

Modified: erp5/trunk/products/ERP5SyncML/dtml/managePublications.dtml
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/dtml/managePublications.dtml?rev=14439&r1=14438&r2=14439&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/dtml/managePublications.dtml (original)
+++ erp5/trunk/products/ERP5SyncML/dtml/managePublications.dtml Thu May 10 14:13:52 2007
@@ -127,6 +127,36 @@
         <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>

Modified: erp5/trunk/products/ERP5SyncML/dtml/manageSubscriptions.dtml
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/dtml/manageSubscriptions.dtml?rev=14439&r1=14438&r2=14439&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/dtml/manageSubscriptions.dtml (original)
+++ erp5/trunk/products/ERP5SyncML/dtml/manageSubscriptions.dtml Thu May 10 14:13:52 2007
@@ -137,6 +137,46 @@
         <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>

Modified: erp5/trunk/products/ERP5SyncML/dtml/manage_addPublication.dtml
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/dtml/manage_addPublication.dtml?rev=14439&r1=14438&r2=14439&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/dtml/manage_addPublication.dtml (original)
+++ erp5/trunk/products/ERP5SyncML/dtml/manage_addPublication.dtml Thu May 10 14:13:52 2007
@@ -125,6 +125,36 @@
   </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>
     <td align="left" valign="top">
     <div class="form-element">

Modified: erp5/trunk/products/ERP5SyncML/dtml/manage_addSubscription.dtml
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/dtml/manage_addSubscription.dtml?rev=14439&r1=14438&r2=14439&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/dtml/manage_addSubscription.dtml (original)
+++ erp5/trunk/products/ERP5SyncML/dtml/manage_addSubscription.dtml Thu May 10 14:13:52 2007
@@ -135,6 +135,45 @@
   </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>
     <td align="left" valign="top">
     <div class="form-element">

Modified: erp5/trunk/products/ERP5SyncML/tests/testERP5SyncML.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/tests/testERP5SyncML.py?rev=14439&r1=14438&r2=14439&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/tests/testERP5SyncML.py (original)
+++ erp5/trunk/products/ERP5SyncML/tests/testERP5SyncML.py Thu May 10 14:13:52 2007
@@ -42,6 +42,10 @@
 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 @@
   last_name1 = 'Robin'
   # At the beginning, I was using iso-8859-15 strings, but actually
   # erp5 is using utf-8 everywhere
-  #description1 = 'description1 --- $sdfrç_sdfsçdf_oisfsopf'
+  #description1 = 'description1 --- $sdfrç_sdfsçdf_oisfsopf'
   description1 = 'description1 --- $sdfr\xc3\xa7_sdfs\xc3\xa7df_oisfsopf'
   lang1 = 'fr'
   format2 = 'html'
@@ -59,12 +63,12 @@
   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 @@
 
   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 @@
     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 @@
     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 @@
     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 @@
 
   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 @@
     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 @@
     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()




More information about the Erp5-report mailing list