[commits] [Wiki] changed: ActiveSync/Development

Michael Rubinsky mrubinsk at horde.org
Wed Nov 23 16:50:28 UTC 2016


mrubinsk  Wed, 23 Nov 2016 16:50:28 +0000

Modified page: https://wiki.horde.org/ActiveSync/Development
New Revision:  5
Change log:  Moving right along....

@@ -54,16 +54,48 @@
  : !DeviceId : This is a unique identifier for the client. This value  
is only unique to the client, not to the account. I.e., the same  
physical device/application will have the same !DeviceId. Multiple  
users can be associated with the same !DeviceId.

  Flow is turned over to //**Horde_ActiveSync::handleRequest()**//.  
This is where the interesting stuff starts to happen.

-+++Authentication
++++ Authentication
  First, we perform some checks, normalization, and call the  
versionCallback hook if it's present. If all is well, we finally  
attempt authentication. Authentication to Horde is a massive topic and  
it's only complicated more by layering ActiveSync on top of it. I will  
try to give a brief overview here. For a more detailed description of  
the general Horde authentication layer, see Jan's excellent series of  
posts on his [http://janschneider.de blog].

  Thanks to broken clients, different supported authentication  
mechanisms and other idiosyncrasies, we need to perform some magic to  
make sure we have the user's credentials. For this, there is  
//Horde_ActiveSync_Credentials//. This class, when constructed and  
injected with the //Horde_ActiveSync// object, will have two  
properties set: ''username'' and ''password''. For the purposes of  
this page, we will assume a typical setup where the client properly  
sends credentials using HTTP BASIC and we are NOT using X509  
certificates.

  Once we have found the credentials, we call  
//**Horde_ActiveSync::authenticate()**//. This performs a few checks  
then ultimately passes control to  
//**Horde_Core_ActiveSync_Driver::authenticate()**//. Since some  
broken clients always send the email address as the username, plus the  
fact the Autodiscover requests ALWAYS use the email address, we need  
to normalize the username to the correct form. This is handled in  
//**Horde_Core_ActiveSync_Driver::getUsernameFromEmail()**// and is  
partially affected by the  
$GLOBALS['conf']['activesync']['autodiscovery'] setting (yes, this  
name is misleading since it is now used for more than just  
autodiscovery).

-Once in //**Horde_Core_ActiveSync_Driver::authenticate()**// we use  
the //Horde_Core_ActiveSync_Auth// object that was injected when the  
driver was created to perform the actual authentication. The reason  
for this authentication wrapper is to allow for combinations of a  
"normal" Horde auth driver along with a transparent driver like X509  
to support clients that allow for certificates along WITH credentials.  
For this page, we will assume a "normal" authentication.
+Once in //**Horde_Core_ActiveSync_Driver::authenticate()**// we use  
the //Horde_Core_ActiveSync_Auth// object that was injected when the  
driver was created to perform the actual authentication. The reason  
for this authentication wrapper is to allow for combinations of a  
"normal" Horde auth driver along with a transparent driver like X509  
to support clients that allow for certificates along WITH credentials.  
For this page, we will assume a "normal" authentication - and as such,  
the actual authentication task is delegated to the 'base_driver' in  
//Horde_Core_ActiveSync_Auth//. That is, the authentication driver  
that is returned by
+<code>
+$injector->getInstance('Horde_Core_Factory_Auth')->create()
+</code>
+
++++ Device
+Note that for BC reasons we refer to the client as "Device". We treat  
these two terms are interchangeable unless explicitly mentioned that  
we are talking about the physical device that the client is running on.
+
+Once we have gotten authentication out the way, we perform some  
checks on the device/client. This includes things like making sure the  
device entry exists in our storage backend (or gets created if it  
doesn't), checking to see if the maximum protocol version we can  
handle has changed - and if so, notifying the client. We also call  
some hooks and callbacks to allow checking various permissions etc...  
All of this is handled in //Horde_ActiveSync::_handleDevice()//. At  
the end of that call (if successful), we know we have a device object  
available and the client is allowed to connect. Any failure here will  
result in an Exception being thrown and subsequently an appropriate  
HTTP error code to be sent back to the client.
+
++++ Provisioning
+Now we take care of setting the provisioning flag and read the WBXML  
header in from the input stream. Provisioning support allows the  
server to take control of certain security settings on the client and  
also enables the ability to remote-wipe the client from the server.  
The provisioning flag is from Horde's permissions system and indicates  
the level of security required:
+
+: Enable : This means the only clients that support full provisioning  
are allowed to connect to the server. Clients that don't support this  
or that have broken support (early Android clients) will NOT work.
+: Allow : This means that we enforce provisioning on clients that  
support it, but also allow non-compliant clients to connect.
+: Disable : No provisioning is done.
+
++++ Multipart
+Next we check to see if the client accepts Multipart responses. This  
is typically used when fetching large amounts of data like e.g, an  
email attachment. This is indicated by the presence of a specific  
header or GET variable. We will assume this is not the case for this  
page.
+
++++ Command
+
+Now we can take care of the actual command the client is attempting  
to perform. These are handled by Horde_ActiveSync_Request_* objects.  
Again, the name of these objects are a bit outdated and are planned to  
be renamed in H6. The object is instantiated and the handle() request  
is called. If all goes well, a value of true is returned and control  
is returned back to the RPC layer to finish up.
+
+The Horde_ActiveSync_Request_* objects are responsible for enforcing  
the schema of whatever request is being handled, passing off any  
incoming additions/deletions/changes to the backend and delegating the  
responsibility for storing any data that needs to be persisted. This  
includes the collection state and the sync cache. This are explained  
in the section on the client life cycle.
+
+There are almost 2 dozen different types of requests that handle  
things from synchronizing the folder structure to validating S/MIME  
certificates. The more common request objects are:
+
+: Horde_ActiveSync_Request_Sync : This is the main code responsible  
for accepting and sending object changes, handling "hanging syncs"  
(which are SYNC requests that also act as PING requests) as well as  
handling certain options that the client sets (such as truncation,  
sync window etc...).
+
+: Horde_ActiveSync_Request_Ping : This handles PING requests, which  
basically just continuously check for a change in the backend. It  
doesn't care what the change is, or how many there are. Much less  
resource intensive than a SYNC for this reason.
+
+This is where the bulk of the action is performed and this  
description is of course a gross over-simplification. There are many  
other objects that are involved in handling the request - the  
responsibility of each of these objects are outlined in the General  
Library Structure section.


  ++ Life Cycle of a Client.




More information about the commits mailing list