[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