| ====================== |
| eZ Components - Webdav |
| ====================== |
| |
| .. contents:: Table of Contents |
| :depth: 2 |
| |
| Introduction |
| ============ |
| |
| The Webdav component enables you to easily set up a WebDAV-enabled HTTP server. |
| Users can then create and edit site content by uploading and downloading files |
| to and from their desktop. The current implementation is compatible with `RFC |
| 2518`__ and realizes most parts of the changes defined in `RFC 4918`__. It |
| also supports clients that do not conform to the standard and provides an |
| interface to support these clients. |
| |
| __ http://tools.ietf.org/html/rfc2518 |
| __ http://tools.ietf.org/html/rfc4918 |
| |
| The component is intended to support you by providing access to your data |
| through the HTTP 1.1 protocol. The data can be stored in the file system, or |
| any other custom data storage. It is then served as a virtual directory tree to |
| the user. |
| |
| Terms |
| ===== |
| |
| There are some terms used in a WebDAV environment whose meanings differ slightly |
| from the usage in similar environments. |
| |
| Collection |
| When it comes to WebDAV, a collection means a set of files and other |
| collections, which may be compared with directories in a normal file |
| system. |
| |
| Resource |
| A resource equals a file, but we use a different term here to differentiate |
| between real files on the hard disk and the virtual resources (files) in |
| a WebDAV share. |
| |
| Properties |
| There are several default properties, like the modification time or file |
| size of WebDAV resources, but you can also store and modify custom |
| properties on all resources. |
| |
| Setting up a WebDAV server |
| ========================== |
| |
| To set up a basic WebDAV server, you must consider two steps: |
| |
| 1) You need to configure the WebDAV server to work correctly with the incoming |
| requests from WebDAV clients. This means that you need to set up some |
| rewriting for the request paths (which the client sends) to the paths that |
| are used in the back-end. |
| |
| 2) You need to set up the back-end, so that it points to the resources you want to |
| share through WebDAV. |
| |
| Path auto detection |
| ------------------- |
| |
| Using the default path factory, which tries to auto detect your setup and map |
| the paths accordingly, you need very little code to setup a WebDAV server. |
| |
| .. include:: tutorial/basic_server.php |
| :literal: |
| |
| As you can see in the example, we first create a new WebDAV server instance. |
| Then a file back-end is created, which just receives the directory as a |
| parameter, where your contents are stored. |
| |
| Finally we call the method handle() on ezcWebdavServer, which actually |
| parses and responds to the request with the created back-end as a parameter. |
| |
| Basic path factory |
| ------------------ |
| |
| A custom path factory enables you to specify the request path mapping to the |
| path of a resource in the repository. This can be used if the automatic |
| detection does not work. |
| |
| .. include:: tutorial/basic_path_factory.php |
| :literal: |
| |
| When assigning the server configuration to the new ezcWebdavBasicPathFactory |
| object, you provide the base URL, which will always be removed from |
| the request URLs. |
| |
| If you need more specialized mapping of request paths to repository paths, |
| you can write your own path factory, by implementing the ezcWebdavPathFactory |
| interface, or extending one of the existing path factories. |
| |
| Testing the server |
| ------------------ |
| |
| You can test the server directly with a WebDAV client of your choice. However, |
| most WebDAV clients have very poor debugging capabilities. |
| |
| The WebDAV client with the most verbose error reporting currently is the |
| `command line WebDAV client cadaver`__, where you might get more information |
| than failed request notifications. |
| |
| __ http://www.WebDAV.org/cadaver/ |
| |
| The second step you should take is to enable error logging, either by catching |
| all exceptions from WebDAV and logging them to a file, or by simply enabling |
| log_errors in php.ini. |
| |
| You can also access the WebDAV server with a browser, since WebDAV is just an |
| extension to the HTTP protocol. You should be able to get valid results out of |
| this, and also see possible errors. Remember that collections (or directories), |
| although they can contain other collections and resources, do not consist of |
| any data themselves. Therefore, if everything is working properly, you will get |
| a blank page when viewing collections in your browser. However, you should |
| still be able to download resources (or files) in the WebDAV share. |
| |
| Authentication and authorization |
| ================================ |
| |
| Since version 1.1, the Webdav component allows you to integrate your own |
| authentication and authorization mechanisms into the server. To achieve this, |
| you need to create a class which implements one of the following interfaces: |
| |
| - ezcWebdavAnonymousAuthenticator |
| - ezcWebdavBasicAuthenticator |
| - ezcWebdavDigestAuthenticator |
| |
| Each of the class extends the one listed before it. The anonymous |
| authenticator provides a method to authenticate the anonymous user (who did |
| not provide a user name or password at all). The second authorizer provides |
| `HTTP Basic authentication`__ to clients. This might not be supported by |
| some clients and is generally considered less secure than Digest. The third |
| interface makes the server provide Basic and `Digest authentication`__. To |
| ease the implementation of Digest authentication, you can extend the |
| abstract base class ezcWebdavDigestAuthenticatorBase. |
| |
| __ http://en.wikipedia.org/wiki/Basic_access_authentication |
| __ http://en.wikipedia.org/wiki/Digest_access_authentication |
| |
| While these interfaces only authenticate a user for general use of WebDAV, they |
| do not provide authorization support. That means, every authenticated user has |
| full write access to all resources. If you also want to have fine grained |
| authorization support, the class must in addition implement |
| ezcWebdavAuthorizer. |
| |
| The following source shows an example implementation for Digest authentication |
| with authorization support: |
| |
| .. include:: tutorial/custom_auth.php |
| :literal: |
| |
| The method authenticateAnonymous() is called, if the user did not provide any |
| credentials or the server was not able to parse the provided authentication |
| header. You need to decide here, if you want to grant anonymous access to your |
| WebDAV server, which is generally considered a bad idea (except if you restrict |
| authorization properly). |
| |
| authenticateBasic() is called by the server, when Basic authentication is |
| provided by the client. |
| |
| If the client provides Digest authentication, the method authenticateDigest() |
| is called instead. The given ezcWebdavDigestAuth contains all necessary |
| information to calculate the digest. This task is performed by the method |
| checkDigest() of the base class. It is recommended, that you implement the |
| digest calculation yourself, if you have the capability to e.g. do it right in |
| the database. This avoids the risk of copying the plain text password data into |
| PHPs memory scope. |
| |
| The auhtorize() method is called by the back-end, for each path that is affected |
| by a request. The back-end provides the $username of the user who wishes to gain |
| access and the type of access, which might be one of: |
| |
| - ezcWebdavAuthorizer::ACCESS_READ |
| - ezcWebdavAuthorizer::ACCESS_WRITE |
| |
| For recursive operations the back-end will call the authorize() method for each |
| sub-sequent path, too, so you don't need to worry about this yourself. In |
| addition it might happen that the back-end (or a plugin) calls the method for a |
| certain path multiple times during a request. It is therefore recommended that |
| you cache the data internally in your authorization object to avoid multiple |
| accesses to your permission storage. |
| |
| To activate authentication and authorization in the server you need to |
| instantiate your class and assign the created object to the servers $auth |
| property, like this: |
| |
| .. include:: tutorial/basic_server_auth.php |
| :literal: |
| |
| Locking |
| ======= |
| |
| Since version 1.1 the Webdav component has support for locking (WebDAV |
| compliance class 2). It is realized through a plugin, which means that you can |
| integrate it into your own WebDAV environment easily, without changing your |
| setup a lot. |
| |
| Pre-conditions |
| -------------- |
| |
| To make the lock plugin work in your environment, you need to fulfill the |
| following pre-conditions: |
| |
| - The back-end class needs to implement ezcWebdavLockBackend |
| - The authorization class needs to implement ezcWebdavLockAuthorizer |
| |
| The ezcWebdavLockBackend interface requires your back-end to implement 2 new |
| methods, that allow the lock plugin to gain an exclusive lock on the back-end. |
| This is necessary to ensure that the lock plugins operations are atomic. Note |
| that you don't need to support the LOCK and UNLOCK requests in your back-end. |
| Both are completely handled by the lock plugin, which communicates with the |
| back-end only by standard requests. For more information see `Writing a custom |
| back-end`_. The file back-end shipped with the Webdav component supports all |
| requirements. |
| |
| The ezcWebdavLockAuthorizer interface defines methods to let your authorization |
| mechanism know about the assignment between users and locks. You need to store |
| string lock tokens per user. |
| |
| Lock authorization |
| ------------------ |
| |
| To setup locking, you first need an authorization class, which implements the |
| ezcWebdavLockAuthorizer interface. In this example, the class presented in |
| `Authentication and authorization`_ is extended for that purpose. |
| |
| .. include:: tutorial/custom_lock_auth.php |
| :literal: |
| |
| The assignment between users and locks must persist between requests. For this |
| reason, the example uses a simple PHP file that stores an array of lock |
| assignments. The structure of this array looks a follows:: |
| |
| <?php |
| array( |
| '<user_name_a>' => array( |
| '<lock_token_1>' => true, |
| '<lock_token_2>' => true, |
| // ... |
| ), |
| // ... |
| ); |
| ?> |
| |
| A lock token is a string with length 52. You will most probably store this |
| information in a database, depending on your authorization mechanism. |
| |
| In the constructor, the contents of the token $storageFile (containing the |
| array shown above, if it exists) is received. It is stored again in the |
| destructor, to persist lock assignments. |
| |
| The method ezcWebdavLockAuthorizer->assignLock() is called by the lock plugin |
| to indicate that the user with name $user has achieved a new lock with the lock |
| token $lockToken. You need to store this assignment, while making sure that a |
| user can have an arbitrary number of lock tokens assigned. |
| |
| Using ezcWebdavLockAuthorizer->ownsLock(), the lock plugin asks if a certain |
| $user owns the given $lockToken. This is the authorization scheme itself. You |
| need to make sure that this method only returns true if the given $lockToken |
| has been assigned to the $user before, using |
| ezcWebdavLockAuthorizer->assignLock(). |
| |
| In case the user performs an UNLOCK request, the plugin will call |
| ezcWebdavLockAuthorizer->releaseLock(). You then need to remove the assignment |
| between $user and $lockToken from your storage. |
| |
| Setup |
| ----- |
| |
| The following example shows a typical setup for a Webdav server with locking: |
| |
| .. include:: tutorial/lock_plugin_server.php |
| :literal: |
| |
| An instance of your custom authentication/authorization class is assigned to |
| the server. You will need to adjust the location of the token assignment file, |
| if you want to get this example running. It must be writable by the user |
| running your web server and should be located outside of your web root. |
| |
| Lines 14-16 show how the lock plugin is activated. The |
| ezcWebdavLockPluginConfiguration class receives optionally an instance of |
| ezcWebdavLockPluginOptions, which you can use to adjust some settings. |
| |
| For the back-end initialization you will need to adjust the path, too. The same |
| rules as for the token.php file apply here: |
| |
| - make it readable and writable for the web server user |
| - put it outside your web root |
| |
| To test your setup properly, you should first access the URL using a web |
| browser. You should now see any exceptions, errors and warnings that might be |
| generated. If you see a white page, everything seems correct. Now try with a |
| WebDAV client. |
| |
| Purging locks |
| ------------- |
| |
| Whenever a clients successfully acquires a lock, a timeout value is assigned to |
| it. If the client does not access the lock for that number of seconds, it might |
| be considered orphan and can safely be removed. To achieve this removal of |
| locks, the Webdav component provides an API for you, that you might use in a |
| CRON job or a similar mechanism, to purge all outdated locks. |
| |
| .. include:: tutorial/lock_administrator.php |
| :literal: |
| |
| The setup for the lock administration should be identical to your web setup. |
| Except, you might leave out the authentication instance, since it will be |
| deactivated by the administrator object anyway. However, it does not hurt to |
| have it in place. |
| |
| The ezcWebdavLockAdministrator object is instantiated given the back-end it |
| should work on. The call to ezcWebdavLockAdministrator->purgeLocks() optionally |
| accepts a string parameter to indicate which paths should be searched for |
| orphan locks. Omitting this parameter, as shown in the example, searches the |
| complete back-end. |
| |
| Note that the process of purging locks will lock your back-end completely, as |
| the lock plugin does it during any request that needs to perform multiple |
| requests. |
| |
| Writing a custom back-end |
| ========================= |
| |
| The most common way of extending WebDAV is to provide a custom back-end |
| to your data. A back-end receives ezcWebdavRequest objects and generates |
| ezcWebdavResponse objects, which are displayed in a way that the current |
| client will understand. |
| |
| There are basically two ways for you to implement a custom back-end. You can |
| implement all the request object handling yourself, by extending |
| ezcWebdavBackend directly, or you can reuse the existing helper class |
| ezcWebdavSimpleBackend. |
| |
| The simple back-end |
| ------------------- |
| |
| The simple back-end, defined in the ezcWebdavSimpleBackend class, already |
| implements all request-to-response mapping, so you only need to implement |
| several methods that directly access the data in your back-end (like the file |
| back-end does). |
| |
| If you need more fine-grained control, or optimizations, you will still need |
| to extend the basic ezcWebdavBackend class directly. If you want to implement |
| a custom back-end you could use the file back-end or the memory back-end |
| (which as mainly intended for testing) as an implementation guide. |
| |
| If you do not extend ezcWebdavSimpleBackend, be sure to implement authorization |
| and to pay attention to the *If-Match* and *If-None-Match* headers. Both are |
| already handled by ezcWebdavSimpleBackend, so you don't need to take care about |
| that if you extend this class. |
| |
| Backends for locking |
| -------------------- |
| |
| If you want to use your custom back-end with the lock plugin, it needs to |
| implement some more interfaces. First of all, you need to implement all of the |
| basic back-end interfaces: |
| |
| - ezcWebdavBackendPut |
| - ezcWebdavBackendChange |
| - ezcWebdavBackendMakeCollection |
| |
| All of these are already implemented by ezcWebdavSimpleBackend. It is |
| recommended that you use this one as the basis for your custom lock back-end, |
| since it already handles authorization for you and processes the HTTP headers |
| *If-Match* and *If-None-Match*. You will need to handle both on your own, if |
| you do not extend ezcWebdavSimpleBackend. |
| |
| No matter which way you choose, you need to implement ezcWebdavLockBackend. |
| This interface requires you to implement 2 methods: |
| |
| public function lock( $waitTime, $timeout ); |
| This method is used by the lock plugin, whenever it needs to communicate |
| with the back-end to process a request. In this case, the lock plugin will |
| lock the back-end as soon as it receives a request it must react on. It will |
| the process all necessary operations, make the back-end process them and |
| unlock the back-end, when it created a response. The $waitTime parameter |
| defines how long the back-end should wait between attempts to achieve the |
| lock. The $timeout parameter defines after what time of trying to acquire a |
| lock the back-end must release a pending lock and restart trying. These |
| parameters are defined through ezcWebdavLockPluginOptions. |
| |
| public function unlock(); |
| The unlock() method is called when the lock plugin has finished all its |
| operations. In case a fatal error occurs in the Webdav component or your |
| back-end, a lock might not be released. For this case, the lock() method |
| must remove a pending lock after $timeout. |
| |
| The ezcWebdavFileBackend, which is shipped with the Webdav component, supports |
| all of the features mentioned above. |
| |
| |
| .. |
| Local Variables: |
| mode: rst |
| fill-column: 79 |
| End: |
| vim: et syn=rst tw=79 |