Author: Alexandru Stanoi
Date: 2007-05-04 16:58:45 +0200 (Fri, 04 May 2007)
New Revision: 5060
Log:
- Tie-in containing the Database filter from the Authentication component.
Added:
experimental/AuthenticationDatabaseTiein/
experimental/AuthenticationDatabaseTiein/CREDITS
experimental/AuthenticationDatabaseTiein/ChangeLog
experimental/AuthenticationDatabaseTiein/DEPS
experimental/AuthenticationDatabaseTiein/DESCRIPTION
experimental/AuthenticationDatabaseTiein/design/
experimental/AuthenticationDatabaseTiein/design/design.txt
experimental/AuthenticationDatabaseTiein/design/requirements.txt
experimental/AuthenticationDatabaseTiein/docs/
experimental/AuthenticationDatabaseTiein/docs/tutorial.txt
experimental/AuthenticationDatabaseTiein/docs/tutorial/
experimental/AuthenticationDatabaseTiein/docs/tutorial/tutorial_database.php
experimental/AuthenticationDatabaseTiein/src/
experimental/AuthenticationDatabaseTiein/src/authentication_database_autoload.php
experimental/AuthenticationDatabaseTiein/src/filters/
experimental/AuthenticationDatabaseTiein/src/filters/database/
experimental/AuthenticationDatabaseTiein/src/filters/database/database_filter.php
experimental/AuthenticationDatabaseTiein/src/filters/database/database_info.php
experimental/AuthenticationDatabaseTiein/src/filters/database/database_options.php
experimental/AuthenticationDatabaseTiein/tests/
experimental/AuthenticationDatabaseTiein/tests/filters/
experimental/AuthenticationDatabaseTiein/tests/filters/database/
experimental/AuthenticationDatabaseTiein/tests/filters/database/database_test.php
experimental/AuthenticationDatabaseTiein/tests/suite.php
Added: experimental/AuthenticationDatabaseTiein/CREDITS
===================================================================
--- experimental/AuthenticationDatabaseTiein/CREDITS 2007-05-04 14:55:21 UTC
(rev 5059)
+++ experimental/AuthenticationDatabaseTiein/CREDITS 2007-05-04 14:58:45 UTC
(rev 5060)
@@ -0,0 +1,16 @@
+CREDITS
+=======
+
+eZ components team
+------------------
+
+- Sergey Alexeev
+- Jan Borsodi
+- Raymond Bosman
+- Frederik Holljen
+- Kore Nordmann
+- Derick Rethans
+- Vadym Savchuk
+- Tobias Schlitt
+- Sebastian Bergmann
+- Alexandru Stanoi
Added: experimental/AuthenticationDatabaseTiein/ChangeLog
===================================================================
--- experimental/AuthenticationDatabaseTiein/ChangeLog 2007-05-04 14:55:21 UTC
(rev 5059)
+++ experimental/AuthenticationDatabaseTiein/ChangeLog 2007-05-04 14:58:45 UTC
(rev 5060)
@@ -0,0 +1,4 @@
+1.0beta1 - [RELEASEDATE]
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- Initial release of this package.
Added: experimental/AuthenticationDatabaseTiein/DEPS
===================================================================
--- experimental/AuthenticationDatabaseTiein/DEPS 2007-05-04 14:55:21 UTC
(rev 5059)
+++ experimental/AuthenticationDatabaseTiein/DEPS 2007-05-04 14:58:45 UTC
(rev 5060)
@@ -0,0 +1,2 @@
+Datebase: 1.1.4
+Authentication: 1.0
Added: experimental/AuthenticationDatabaseTiein/DESCRIPTION
===================================================================
--- experimental/AuthenticationDatabaseTiein/DESCRIPTION 2007-05-04
14:55:21 UTC (rev 5059)
+++ experimental/AuthenticationDatabaseTiein/DESCRIPTION 2007-05-04
14:58:45 UTC (rev 5060)
@@ -0,0 +1,3 @@
+The purpose of the Authentication component is to provide support for different
+means of identification and authentication of users using different providers
and
+protocols.
Added: experimental/AuthenticationDatabaseTiein/design/design.txt
===================================================================
--- experimental/AuthenticationDatabaseTiein/design/design.txt 2007-05-04
14:55:21 UTC (rev 5059)
+++ experimental/AuthenticationDatabaseTiein/design/design.txt 2007-05-04
14:58:45 UTC (rev 5060)
@@ -0,0 +1,242 @@
+eZ Components - Authentication, Design
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+:Author: Alexandru Stanoi
+:Revision: $Revision: $
+:Date: $Date: $
+
+
+Introduction
+============
+
+Description
+-----------
+
+The purpose of the Authentication component is to provide support for different
+means of identification and authentication of users using different providers
and
+protocols.
+
+
+Design
+======
+
+Base classes
+------------
+
+ezcAuthentication
+ Container for authentication filters. Runs all the specified filters in
+ sequence to check if all succeed or if any filter fails.
+
+ezcAuthenticationFilter
+ Abstract class which is a base class for all authentication filters. It can
+ be extended to create new filters.
+
+ezcAuthenticationCredentials
+ A structure which holds basic information to authenticate the user.
+ Developers can extend this class to include additional credentials.
+
+
+Authentication filters
+----------------------
+
+A list of supported authentication filters (plug-ins). This list can be
+extended by developers or by us in future releases:
+
+ezcAuthenticationSessionFilter
+ Support for storing authentication information in the session.
+
+ezcAuthenticationGroupFilter
+ Support for grouping two or more filters, where only one filter needs to
+ succeed for the group to succeed.
+
+ezcAuthenticationHtpasswdFilter
+ Handles authentication using a htpasswd file. Basic filter and not too secure
+ as the htpasswd file can be accessible to someone who has access to the
+ server. Should not be employed on critical application. It is used mostly as
+ an example.
+
+ezcAuthenticationTokenFilter
+ Handles authentication based on a server generated code. Similar to the
+ Ticket filter, but the code is not saved in a database, but it is generated
+ on every request. One use of such a filter is for CAPTCHA tests, or for
+ challenge responses to a server code with a user code (usually banks require
+ users to use code calculators).
+
+ezcAuthenticationLdapFilter
+ Handles authentication using an LDAP directory.
+
+ezcAuthenticationOpenidFilter
+ Handles authentication using the OpenID protocol.
+
+ezcAuthenticationTypekeyFilter
+ Handles authentication using the TypeKey protocol.
+
+
+Extended filters
+----------------
+
+A list of supported authentication filters (plug-ins). These filters will be
+implemented in a tie-in component as they depend on other components:
+
+ezcAuthenticationDatabaseFilter
+ Handles authentication using a database, by specifying the database name,
+ user and password to use the database, table name, fields to check and
+ whether to use encryption for each field or not.
+
+ezcAuthenticationTicketFilter
+ Handles authentication using a token (server generated code). It is basically
+ a database filter with only one field, and with deletion of the record after
+ authentication. One use of such a filter is for sending confirmation emails
+ to the user.
+
+
+Implementation
+==============
+
+ezcAuthentication
+-----------------
+
+Container for AuthenticationFilters. Contains these methods:
+
+run()
+ Runs through all the filters in their sequence. If all filters succeed, then
+ it will return true, otherwise returns false, and the $status property of the
+ authentication object will contain the statuses of all filters until that
+ moment.
+
+addFilter()
+ Adds a filter to the list of authentication filters.
+
+Contains these properties:
+
+status
+ A list of the error codes until the current moment (for example
+ ezcAuthenticationLdapFilter::STATUS_PASSWORD_INCORRECT).
+
+
+ezcAuthenticationFilter
+-----------------------
+
+Abstract class which is a base class for all authentication filters. It can
+be extended to create new filters. It contains these abstract methods:
+
+run()
+ Executes the filter authentication. Can return different statuses which are
+ used by the ezcAuthentication object to decide if the next filter is needed
+ to run or if the authentication process failed at the current filter.
+
+
+ezcAuthenticationSessionFilter
+------------------------------
+
+Support for storing authentication information in the session. Uses a specified
+key in the session to store if the user is authenticated or not. Cases:
+
+Login by starting a new session
+ Authentication will accept the new session and initiate authentication using
+ the other defined filters.
+
+Login by using an existing session
+ Authentication will check if the existing session is within the expiring
+ period (defined by the developer of the application using the session
+ options). If the session expired, then it will create a new session (new
+ session ID) and initiate authentication using the other defined filters. If
+ the session did not expire, then it will grant access bypassing the other
+ defined filters.
+
+Login by using another session ID than the one stored
+ Probably an attack. It will warn about the conflict, generate a new ID and
+ initiate the authentication using the other defined filters.
+
+
+ezcAuthenticationDatabaseFilter
+-------------------------------
+
+Implemented in a tie-in component as it depends on the DatabaseSchema and
+Database components. Works on an instance of ezcDbInstance. Options to specify
+include the table name and the user and password fields names.
+
+
+ezcAuthenticationTicketFilter
+-----------------------------
+
+Extends the ezcAuthenticationDatabaseFilter class. Implemented in a tie-in
+component as it depends on the Database and DatabaseSchema components.
+
+
+ezcAuthenticationTokenFilter
+----------------------------
+
+The token will be generated by a random function that the developer specifies.
+This filter just compares this token with the token entered by the user, using
+a specified function.
+
+
+Examples
+========
+
+Htpasswd authentication
+-----------------------
+
+The following example shows how to authenticate against an htpasswd file: ::
+
+ // get $user and $password from POST variables
+ $credentials = new ezcAuthenticationPasswordCredentials( $user, $password
);
+ $authentication = new ezcAuthentication( $credentials );
+
+ $authentication->addFilter( new ezcAuthenticationHtpasswdFilter(
+ '/home/root/.htpasswd' ) );
+
+ if ( !$authentication->run() )
+ {
+ // return the status of the authentication,
+ // for example
ezcAuthenticationHtpasswdFilter::STATUS_PASSWORD_INCORRECT
+ $status = $authentication->getStatus();
+
+ // use the returned status to provide the user with a friendly error
+ // message, such as "The password you entered is incorect. Please try
+ // again.", and don't allow the user to see the protected content.
+ }
+ else
+ {
+ // allow the user to see the protected content
+ }
+
+
+Database authentication
+-----------------------
+
+The following example shows how to authenticate using a database, and using the
+session to cache the authentication between requests: ::
+
+ // get $user and $password from POST variables or from session
+ $credentials = new ezcAuthenticationPasswordCredentials( $user, $password
);
+ $authentication = new ezcAuthentication( $credentials );
+ $authentication->session = new ezcAuthenticationSessionFilter();
+
+ $db = new ezcAuthenticationDatabaseInfo( ezcDbInstance::get(),
+ 'users', array( 'user', 'password' ) );
+ $authentication->addFilter( new ezcAuthenticationDatabaseFilter( $db ) );
+
+ if ( !$authentication->run() )
+ {
+ // return the status of the authentication,
+ // for example
ezcAuthenticationDatabaseFilter::STATUS_PASSWORD_INCORRECT
+ $status = $authentication->getStatus();
+
+ // use the returned status to provide the user with a friendly error
+ // message, such as "The password you entered is incorect. Please try
+ // again.", and don't allow the user to see the protected content.
+ }
+ else
+ {
+ // allow the user to see the protected content
+ }
+
+
+
+..
+ Local Variables:
+ mode: rst
+ fill-column: 79
+ End:
+ vim: et syn=rst tw=79 nocin
Property changes on: experimental/AuthenticationDatabaseTiein/design/design.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: experimental/AuthenticationDatabaseTiein/design/requirements.txt
===================================================================
--- experimental/AuthenticationDatabaseTiein/design/requirements.txt
2007-05-04 14:55:21 UTC (rev 5059)
+++ experimental/AuthenticationDatabaseTiein/design/requirements.txt
2007-05-04 14:58:45 UTC (rev 5060)
@@ -0,0 +1,261 @@
+eZ Components - Authentication, Requirements
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+:Author: Alexandru Stanoi
+:Revision: $Revision: $
+:Date: $Date: $
+
+
+Introduction
+============
+
+The purpose of the Authentication component is to provide support for different
+means of identification and authentication of users using different providers
and
+protocols.
+
+
+About authentication
+====================
+
+What is authentication?
+-----------------------
+
+Authentication is a process of verifying the identity of an user and enabling
+access for that user based on stored permissions.
+
+The steps of the authentication process are:
+
+Identification
+ Fetch the user credentials (eg. username + password, IP address, tokens).
+
+Authentication
+ Check user credentials with a security provider (eg. LDAP, database, htpasswd
+ file).
+
+Validation
+ Check if user is valid for the current context (eg. time of day).
+
+Authorization
+ Check if user has permissions for the current context (eg. printer access).
+
+The new Authentication component will implement only the Indentificaton and
+Authentication stages. Validation and Authorization will be implemented at a
+later point in time.
+
+
+Authentication examples
+-----------------------
+
+Login
+ The user enter their username (sometimes email address) and password.
+ Websites usually have a "Keep me logged-in" or "Remember me" checkbox to
+ avoid entering credentials everytime the user starts the application. The
+ application will compare the user's credentials against a database or another
+ provider, and allow the user access to the application. If user credentials
+ are not recognized, the application will deny access to the user. Sometimes
+ websites use other credentials in addition, like banks requiring users to use
+ a code generator (calculator) in response to the code generated by the
+ application.
+
+CAPTCHA
+ Not a proper authentication, it is just a way to secure certain features of
+ an application against scripts. The server generates a code and displays it
+ to the user in a scrambled image, and the user must enter the code in the
+ image to gain access to the secured feature (post comment, send email, etc).
+
+Confirmation ticket
+ Similar to CAPTCHA. The server generates a code and keeps it in a database
+ (or another storage), and sends an email to the user with the code. The user
+ uses this code in the application (usually by clicking a link in the email).
+ This is usually done to confirm the user's email address. The code is usually
+ deleted from the database after being used.
+
+
+Requirements
+============
+
+The Authentication component should support adding filters (plug-ins) to the
+authentication process. These filters will be processed in order, and only if
+the previous filters allow the process to continue. If one filter fails (for
+example if the password is incorrect), then the whole authentication process
+stops and the status of the authentication can be used by the application
+(for example to display "Password incorrect").
+
+
+Authentication filters
+----------------------
+
+A list of filters which are to be supported in the initial release of the
+component (this list might be changed later):
+
+Session
+ Uses the PHP session to make information about the authenticated user
+ persistent across requests. This information includes if the user is
+ authenticated and authentication timestamp (to be used to expire the
+ session).
+
+Group
+ A generic filter which is used to group two or more filters, where only one
+ filter needs to succeed in order to continue the authentication. Example:
+ grouping the LDAP and Database filters; only one of the filters needs to
+ succeed for the group to succeed.
+
+Htpasswd file
+ Uses a Unix htpasswd file to authenticate users. Basic filter and not too
+ secure as the htpasswd file can be accessible to someone who has access to
+ the server. Should not be employed on critical application. It is used mostly
+ as an example.
+
+Database
+ Uses an existing database to authenticate user credentials. Should use
+ database abstraction to be able to authenticate against different database
+ providers (MySql, Oracle, etc) - this can be achieved with the Database and
+ DatabaseSchema components.
+
+Token
+ Support for authenticating against a user generated code. The code is stored
+ in a database (or other storage), and is deleted after the user has
+ authenticated against it. It is usually used for confirmation of the user's
+ email address.
+
+OpendID
+ Uses the OpenID protocol to authenticate users. The central idea of this
+ distributed protocol is that users login only once using their own URL.
+ (blog, personal website). Info: http://openid.net
+
+TypeKey
+ In many ways similar with OpenID, in that users login only once. Info:
+ http://www.sixapart.com/typekey
+
+LDAP (Lightweight Directory Access Protocol)
+ Protocol for querying and updating directory services. Compatible with
+ other protocols (Novell eDirectory, Oracle Internet Directory, Windows Server
+ 2003 Active Directory). RFC: http://www.faqs.org/rfcs/rfc4510.html
+
+
+Design goals
+============
+
+The authentication process is divided into filters (plug-ins). The developer
+can decide which filters the application should employ and their order.
+
+The list of filters can be extended by developers, and creating new filters
+should not affect existing code.
+
+The session is optional and is used to make the authentication persistent
+across requests. If the session is enabled, the authentication process starts
+at the session, where it will check if the user is already authenticated. If
+the session expired or if the user is not authenticated, the other filters in
+the authentication process will be run. If the authentication process was
+successful, then the session will save the authentication information to be
+used in subsequent requests.
+
+The logging of authorization attempts will not be done by the Authentication
+component, but by the application (eventually using the EventLog component).
+
+
+Authentication process
+----------------------
+
+The filters run in sequence. Based on the return status of each filter, the
+authentication process will continue or stop as follows:
+
+Continue
+ The next filter will be run. If all filters are run and positive answer
+ is received from all of them, then the user is granted access to the
+ application.
+
+Error
+ The authentication process will stop (or not, depending on the options), and
+ a status object will contain the error. The application will decide, based on
+ the error status, what to do (for example display an error message like
+ "Password incorrect").
+
+Stop
+ The authentication process will stop regardless of what filters are still in
+ the queue, and the user is granted access to the application.
+
+Schematic: ::
+ START
+ |
+ V
+ NO (Error) -----------
+ -------------------------| Session |-------------------
+ | ----------- | YES (Stop)
+ | | YES (Continue) |
+ | V |
+ V NO (Error) ------------ V
+ ------------------------| Database |-----------------
+ | ------------ | YES (Stop)
+ | | YES (Continue) |
+ | V |
+ V NO (Error) ------------ V
+ ------------------------| LDAP |-----------------
+ | ------------ | YES (Stop)
+ | | YES (Continue) |
+ V V V
+ =================== ======================================
+ ( not authenticated ) ( authenticated )
+ =================== ======================================
+
+This behaviour can be changed with an option for each filter, which specifies
+if the filter stops the authentication process if it is successful, or allows
+the authentication process to continue.
+
+
+Session
+-------
+
+The use of a Session filter is optional (but enabled by default). Developers
can
+specify the provided Session filter or their own class.
+
+If the Session filter is enabled, then it will be checked before the
+authentication process, and it will save the authentication information for
+future requests, after the process.
+
+The Session filter is responsible for:
+ - saving and checking the timestamp (based on options)
+ - starting and ending the PHP session
+ - regenerating the session ID
+
+
+Grouping of filters
+-------------------
+
+The developer of the application can group similar filters together, where only
+one filter needs to succeed for the filter group to succeed.
+
+For example: the user credentials can be checked against the local database or
+an LDAP provider, but it needs not be present in both providers. In this case
+the LDAP and Database filters will be grouped together, and if at least one
+filter succeed, then the group filter will succeed.
+
+
+PHP requirements
+================
+
+Extensions needed for the authentication:
+ - TypeKey: bcmath or gmp
+ - OpenID: openssl
+ - LDAP: ldap; oci8 (plus Oracle enviroment) is required for OracleLDAP
+ - parsing of XML documents: simplexml (usually enabled by default)
+
+
+Security considerations
+=======================
+
+Due to the nature of this component, care must be taken to ensure secure
+handling of resources.
+
+Session attacks will be prevented with these methods:
+ - prevent session fixation (http://en.wikipedia.org/wiki/Session_fixation)
+ - regenerate session id if not authenticated
+
+
+
+
+..
+ Local Variables:
+ mode: rst
+ fill-column: 79
+ End:
+ vim: et syn=rst tw=79 nocin
Property changes on:
experimental/AuthenticationDatabaseTiein/design/requirements.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added:
experimental/AuthenticationDatabaseTiein/docs/tutorial/tutorial_database.php
===================================================================
---
experimental/AuthenticationDatabaseTiein/docs/tutorial/tutorial_database.php
2007-05-04 14:55:21 UTC (rev 5059)
+++
experimental/AuthenticationDatabaseTiein/docs/tutorial/tutorial_database.php
2007-05-04 14:58:45 UTC (rev 5060)
@@ -0,0 +1,37 @@
+<?php
+$credentials = new ezcAuthenticationPasswordCredentials( 'jan.modaal',
'b1b3773a05c0ed0176787a4f1574ff0075f7521e' );
+$database = new ezcAuthenticationDatabaseInfo( ezcDbInstance::get(), 'users',
array( 'user', 'password' ) );
+$authentication = new ezcAuthentication( $credentials );
+$authentication->addFilter( new ezcAuthenticationDatabaseFilter( $database ) );
+if ( !$authentication->run() )
+{
+ // authentication did not succeed, so inform the user
+ $status = $authentication->getStatus();
+ $err = array();
+ $err["user"] = "";
+ $err["password"] = "";
+ for ( $i = 0; $i < count( $status ); $i++ )
+ {
+ list( $key, $value ) = each( $status[$i] );
+ switch ( $key )
+ {
+ case 'ezcAuthenticationDatabaseFilter':
+ if ( $value ===
ezcAuthenticationDatabaseFilter::STATUS_USERNAME_INCORRECT )
+ {
+ $err["user"] = "<span class='error'>Username
incorrect</span>";
+ }
+ if ( $value ===
ezcAuthenticationDatabaseFilter::STATUS_PASSWORD_INCORRECT )
+ {
+ $err["password"] = "<span class='error'>Password
incorrect</span>";
+ }
+ break;
+ }
+ }
+ // use $err array (with a Template object for example) to display the
login form
+ // to the user with "Password incorrect" message next to the password
field, etc...
+}
+else
+{
+ // authentication succeeded, so allow the user to see his content
+}
+?>
Property changes on:
experimental/AuthenticationDatabaseTiein/docs/tutorial/tutorial_database.php
___________________________________________________________________
Name: svn:eol-style
+ native
Added: experimental/AuthenticationDatabaseTiein/docs/tutorial.txt
===================================================================
--- experimental/AuthenticationDatabaseTiein/docs/tutorial.txt 2007-05-04
14:55:21 UTC (rev 5059)
+++ experimental/AuthenticationDatabaseTiein/docs/tutorial.txt 2007-05-04
14:58:45 UTC (rev 5060)
@@ -0,0 +1,63 @@
+eZ Components - Authentication
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. contents:: Table of Contents
+
+Introduction
+============
+
+Description
+-----------
+
+The purpose of the Authentication component is to provide support for different
+means of identification and authentication of users using different providers
and
+protocols.
+
+
+Class overview
+==============
+
+An overview of the most important classes in the Authentication component.
+
+
+Base classes
+------------
+
+ezcAuthentication
+ Main class of Authentication. It is a container for authentication filters,
+ which will be run in sequence. The method run() returns true or false
+ depending on the success of the authentication filters.
+
+ezcAuthenticationCredentials
+ Structure which holds user credentials. Types are id credentials
+ (ezcAuthenticationIdCredentials) and id + password credentials
+ (ezcAuthenticationPasswordCredentials).
+
+
+Authentication filters
+----------------------
+
+ezcAuthenticationDatabaseFilter
+ Filter to authenticate against a database. Uses a database instance provided
+ by the Database component (via the ezcDbInstance::get() function).
+
+
+Authentication filters
+======================
+
+Database
+--------
+
+The following example shows how to authenticate against a database.
+
+.. include:: tutorial/tutorial_database.php
+ :literal:
+
+
+
+..
+ Local Variables:
+ mode: rst
+ fill-column: 79
+ End:
+ vim: et syn=rst tw=79 nocin
Property changes on: experimental/AuthenticationDatabaseTiein/docs/tutorial.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added:
experimental/AuthenticationDatabaseTiein/src/authentication_database_autoload.php
===================================================================
---
experimental/AuthenticationDatabaseTiein/src/authentication_database_autoload.php
2007-05-04 14:55:21 UTC (rev 5059)
+++
experimental/AuthenticationDatabaseTiein/src/authentication_database_autoload.php
2007-05-04 14:58:45 UTC (rev 5060)
@@ -0,0 +1,20 @@
+<?php
+/**
+ * Autoloader definition for the ezcAuthenticationDatabaseTiein component.
+ *
+ * @copyright Copyright (C) 2005-2007 eZ systems as. All rights reserved.
+ * @license http://ez.no/licenses/new_bsd New BSD License
+ * @filesource
+ * @package AuthenticationDatabaseTiein
+ * @version //autogen//
+ */
+
+return array(
+
+ // authentication Database filter
+ 'ezcAuthenticationDatabaseFilter' =>
'AuthenticationDatabaseTiein/filters/database/database_filter.php',
+ 'ezcAuthenticationDatabaseInfo' =>
'AuthenticationDatabaseTiein/filters/database/database_info.php',
+ 'ezcAuthenticationDatabaseOptions' =>
'AuthenticationDatabaseTiein/filters/database/database_options.php',
+
+ );
+?>
Property changes on:
experimental/AuthenticationDatabaseTiein/src/authentication_database_autoload.php
___________________________________________________________________
Name: svn:eol-style
+ native
Added:
experimental/AuthenticationDatabaseTiein/src/filters/database/database_filter.php
===================================================================
---
experimental/AuthenticationDatabaseTiein/src/filters/database/database_filter.php
2007-05-04 14:55:21 UTC (rev 5059)
+++
experimental/AuthenticationDatabaseTiein/src/filters/database/database_filter.php
2007-05-04 14:58:45 UTC (rev 5060)
@@ -0,0 +1,211 @@
+<?php
+/**
+ * File containing the ezcAuthenticationDatabaseFilter class.
+ *
+ * @copyright Copyright (C) 2005-2007 eZ systems as. All rights reserved.
+ * @license http://ez.no/licenses/new_bsd New BSD License
+ * @filesource
+ * @package AuthenticationDatabaseTiein
+ * @version //autogen//
+ */
+
+/**
+ * Filter to authenticate against a database.
+ *
+ * The database instance to use is specified using a
ezcAuthenticationDatabaseInfo
+ * structure. Table name and field names are specified in the same structure.
+ *
+ * Example:
+ * <code>
+ * $credentials = new ezcAuthenticationPasswordCredentials( 'jan.modaal',
'b1b3773a05c0ed0176787a4f1574ff0075f7521e' );
+ * $database = new ezcAuthenticationDatabaseInfo( ezcDbInstance::get(),
'users', array( 'user', 'password' ) );
+ * $authentication = new ezcAuthentication( $credentials );
+ * $authentication->addFilter( new ezcAuthenticationDatabaseFilter( $database
) );
+ * if ( !$authentication->run() )
+ * {
+ * // authentication did not succeed, so inform the user
+ * $status = $authentication->getStatus();
+ * $err = array();
+ * $err["user"] = "";
+ * $err["password"] = "";
+ * for ( $i = 0; $i < count( $status ); $i++ )
+ * {
+ * list( $key, $value ) = each( $status[$i] );
+ * switch ( $key )
+ * {
+ * case 'ezcAuthenticationDatabaseFilter':
+ * if ( $value ===
ezcAuthenticationDatabaseFilter::STATUS_USERNAME_INCORRECT )
+ * {
+ * $err["user"] = "<span class='error'>Username
incorrect</span>";
+ * }
+ * if ( $value ===
ezcAuthenticationDatabaseFilter::STATUS_PASSWORD_INCORRECT )
+ * {
+ * $err["password"] = "<span class='error'>Password
incorrect</span>";
+ * }
+ * break;
+ * }
+ * }
+ * // use $err array (with a Template object for example) to display the
login form
+ * // to the user with "Password incorrect" message next to the password
field, etc...
+ * }
+ * else
+ * {
+ * // authentication succeeded, so allow the user to see his content
+ * }
+ * </code>
+ *
+ * @property ezcAuthenticationDatabaseInfo $database
+ * Structure which holds a database instance, table name and fields
+ * which are used for authentication.
+ *
+ * @package AuthenticationDatabaseTiein
+ * @version //autogen//
+ * @mainclass
+ */
+class ezcAuthenticationDatabaseFilter extends ezcAuthenticationFilter
+{
+ /**
+ * Username is not found in the database.
+ */
+ const STATUS_USERNAME_INCORRECT = 1;
+
+ /**
+ * Password is incorrect.
+ */
+ const STATUS_PASSWORD_INCORRECT = 2;
+
+ /**
+ * Holds the properties of this class.
+ *
+ * @var array(string=>mixed)
+ */
+ private $properties = array();
+
+ /**
+ * Creates a new object of this class.
+ *
+ * @param ezcAuthenticationDatabaseInfo $database Database to use in
authentication
+ * @param ezcAuthenticationDatabaseOptions $options Options for this class
+ */
+ public function __construct( ezcAuthenticationDatabaseInfo $database,
ezcAuthenticationDatabaseOptions $options = null )
+ {
+ $this->options = ( $options === null ) ? new
ezcAuthenticationDatabaseOptions() : $options;
+ $this->database = $database;
+ }
+
+ /**
+ * Sets the property $name to $value.
+ *
+ * @throws ezcBasePropertyNotFoundException
+ * if the property $name does not exist
+ * @throws ezcBaseValueException
+ * if $value is not correct for the property $name
+ * @param string $name
+ * @param mixed $value
+ * @ignore
+ */
+ public function __set( $name, $value )
+ {
+ switch ( $name )
+ {
+ case 'database':
+ if ( $value instanceof ezcAuthenticationDatabaseInfo )
+ {
+ $this->properties[$name] = $value;
+ }
+ else
+ {
+ throw new ezcBaseValueException( $name, $value, 'instance
of ezcAuthenticationDatabaseInfo' );
+ }
+ break;
+
+ default:
+ throw new ezcBasePropertyNotFoundException( $name );
+ }
+ }
+
+ /**
+ * Returns the value of the property $name.
+ *
+ * @throws ezcBasePropertyNotFoundException
+ * if the property $name does not exist
+ * @param string $name
+ * @return mixed
+ * @ignore
+ */
+ public function __get( $name )
+ {
+ switch ( $name )
+ {
+ case 'database':
+ return $this->properties[$name];
+
+ default:
+ throw new ezcBasePropertyNotFoundException( $name );
+ }
+ }
+
+ /**
+ * Returns true if the property $name is set, otherwise false.
+ *
+ * @param string $name
+ * @return bool
+ * @ignore
+ */
+ public function __isset( $name )
+ {
+ switch ( $name )
+ {
+ case 'database':
+ return isset( $this->properties[$name] );
+
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Runs the filter and returns a status code when finished.
+ *
+ * @param ezcAuthenticationPasswordCredentials $credentials Authentication
credentials
+ * @return int
+ */
+ public function run( $credentials )
+ {
+ // see if username exists
+ $query = new ezcQuerySelect( $this->database->instance );
+ $e = $query->expr;
+ $query->select( 'COUNT(*)' )
+ ->from( $this->database->table )
+ ->where(
+ $e->eq( $this->database->fields[0], $query->bindValue(
$credentials->id ) )
+ );
+ $rows = $query->prepare();
+ $rows->execute();
+ $count = (int)$rows->fetchColumn( 0 );
+ if ( $count === 0 )
+ {
+ return self::STATUS_USERNAME_INCORRECT;
+ }
+
+ // see if username has the specified password
+ $query = new ezcQuerySelect( $this->database->instance );
+ $e = $query->expr;
+ $query->select( 'COUNT(*)' )
+ ->from( $this->database->table )
+ ->where( $e->lAnd(
+ $e->eq( $this->database->fields[0], $query->bindValue(
$credentials->id ) ),
+ $e->eq( $this->database->fields[1], $query->bindValue(
$credentials->password ) )
+ ) );
+ $rows = $query->prepare();
+ $rows->execute();
+ $count = (int)$rows->fetchColumn( 0 );
+ if ( $count === 0 )
+ {
+ return self::STATUS_PASSWORD_INCORRECT;
+ }
+
+ return self::STATUS_OK;
+ }
+}
+?>
Property changes on:
experimental/AuthenticationDatabaseTiein/src/filters/database/database_filter.php
___________________________________________________________________
Name: svn:eol-style
+ native
Added:
experimental/AuthenticationDatabaseTiein/src/filters/database/database_info.php
===================================================================
---
experimental/AuthenticationDatabaseTiein/src/filters/database/database_info.php
2007-05-04 14:55:21 UTC (rev 5059)
+++
experimental/AuthenticationDatabaseTiein/src/filters/database/database_info.php
2007-05-04 14:58:45 UTC (rev 5060)
@@ -0,0 +1,73 @@
+<?php
+/**
+ * File containing the ezcAuthenticationDatabaseInfo structure.
+ *
+ * @copyright Copyright (C) 2005-2007 eZ systems as. All rights reserved.
+ * @license http://ez.no/licenses/new_bsd New BSD License
+ * @filesource
+ * @package AuthenticationDatabaseTiein
+ * @version //autogen//
+ */
+
+/**
+ * Structure for defining the database and table to authenticate against.
+ *
+ * @package AuthenticationDatabaseTiein
+ * @version //autogen//
+ */
+class ezcAuthenticationDatabaseInfo extends ezcBaseStruct
+{
+ /**
+ * Database instance.
+ *
+ * @var ezcDbHandler
+ */
+ public $instance;
+
+ /**
+ * Table which stores the user credentials.
+ *
+ * @var string
+ */
+ public $table;
+
+ /**
+ * Fields which hold the user credentials.
+ *
+ * @var array(string)
+ */
+ public $fields;
+
+ /**
+ * Constructs a new ezcAuthenticationDatabaseInfo object.
+ *
+ * @param ezcDbHandler $instance Database instance to use
+ * @param string $table Table which stores usernames and passwords
+ * @param array(string) $fields The fields which hold usernames and
passwords
+ */
+ public function __construct( ezcDbHandler $instance, $table, array $fields
)
+ {
+ $this->instance = $instance;
+ $this->table = $table;
+ $this->fields = $fields;
+ }
+
+ /**
+ * Returns a new instance of this class with the data specified by $array.
+ *
+ * $array contains all the data members of this class in the form:
+ * array('member_name'=>value).
+ *
+ * __set_state makes this class exportable with var_export.
+ * var_export() generates code, that calls this method when it
+ * is parsed with PHP.
+ *
+ * @param array(string=>mixed)
+ * @return ezcAuthenticationDatabaseInfo
+ */
+ static public function __set_state( array $array )
+ {
+ return new ezcAuthenticationDatabaseInfo( $array['instance'],
$array['table'], $array['fields'] );
+ }
+}
+?>
Property changes on:
experimental/AuthenticationDatabaseTiein/src/filters/database/database_info.php
___________________________________________________________________
Name: svn:eol-style
+ native
Added:
experimental/AuthenticationDatabaseTiein/src/filters/database/database_options.php
===================================================================
---
experimental/AuthenticationDatabaseTiein/src/filters/database/database_options.php
2007-05-04 14:55:21 UTC (rev 5059)
+++
experimental/AuthenticationDatabaseTiein/src/filters/database/database_options.php
2007-05-04 14:58:45 UTC (rev 5060)
@@ -0,0 +1,54 @@
+<?php
+/**
+ * File containing the ezcAuthenticationDatabaseOptions class.
+ *
+ * @copyright Copyright (C) 2005-2007 eZ systems as. All rights reserved.
+ * @license http://ez.no/licenses/new_bsd New BSD License
+ * @filesource
+ * @package AuthenticationDatabaseTiein
+ * @version //autogen//
+ */
+
+/**
+ * Class containing the options for the database authentication filter.
+ *
+ * @package AuthenticationDatabaseTiein
+ * @version //autogen//
+ */
+class ezcAuthenticationDatabaseOptions extends ezcAuthenticationFilterOptions
+{
+ /**
+ * Constructs an object with the specified values.
+ *
+ * @throws ezcBasePropertyNotFoundException
+ * if $options contains a property not defined
+ * @throws ezcBaseValueException
+ * if $options contains a property with a value not allowed
+ * @param array(string=>mixed) $options
+ */
+ public function __construct( array $options = array() )
+ {
+ parent::__construct( $options );
+ }
+
+ /**
+ * Sets the option $name to $value.
+ *
+ * @throws ezcBasePropertyNotFoundException
+ * if the property $name is not defined
+ * @throws ezcBaseValueException
+ * if $value is not correct for the property $name
+ * @param string $name
+ * @param mixed $value
+ * @ignore
+ */
+ public function __set( $name, $value )
+ {
+ switch ( $name )
+ {
+ default:
+ parent::__set( $name, $value );
+ }
+ }
+}
+?>
Property changes on:
experimental/AuthenticationDatabaseTiein/src/filters/database/database_options.php
___________________________________________________________________
Name: svn:eol-style
+ native
Added:
experimental/AuthenticationDatabaseTiein/tests/filters/database/database_test.php
===================================================================
---
experimental/AuthenticationDatabaseTiein/tests/filters/database/database_test.php
2007-05-04 14:55:21 UTC (rev 5059)
+++
experimental/AuthenticationDatabaseTiein/tests/filters/database/database_test.php
2007-05-04 14:58:45 UTC (rev 5060)
@@ -0,0 +1,231 @@
+<?php
+/**
+ * @copyright Copyright (C) 2005-2007 eZ systems as. All rights reserved.
+ * @license http://ez.no/licenses/new_bsd New BSD License
+ * @filesource
+ * @package AuthenticationDatabaseTiein
+ * @version //autogen//
+ * @subpackage Tests
+ */
+
+/**
+ * @package AuthenticationDatabaseTiein
+ * @version //autogen//
+ * @subpackage Tests
+ */
+class ezcAuthenticationDatabaseTest extends ezcTestCase
+{
+ public static function suite()
+ {
+ return new PHPUnit_Framework_TestSuite(
"ezcAuthenticationDatabaseTest" );
+ }
+
+ public function setUp()
+ {
+ try
+ {
+ $this->db = ezcDbInstance::get();
+ $tables = array( 'users' => new ezcDbSchemaTable(
+ array (
+ 'id' => new ezcDbSchemaField( 'integer',
false, true, null, true ),
+ 'user' => new ezcDbSchemaField( 'text',
32, true ),
+ 'password' => new ezcDbSchemaField(
'text', 64, true ),
+ ),
+ array (
+ 'user' => new ezcDbSchemaIndex( array (
'user' => new ezcDbSchemaIndexField() ), false, false ),
+ ) ) );
+ $schema = new ezcDbSchema( $tables );
+ $schema->writeToDb( $this->db );
+ $query = new ezcQueryInsert( $this->db );
+ $query->insertInto( 'users' )
+ ->set( 'user', $query->bindValue( 'jan.modaal' ) )
+ ->set( 'password', $query->bindValue( sha1( 'qwerty' ) ) );
+ $stmt = $query->prepare();
+ $stmt->execute();
+ $query = new ezcQueryInsert( $this->db );
+ $query->insertInto( 'users' )
+ ->set( 'user', $query->bindValue( 'john.doe' ) )
+ ->set( 'password', $query->bindValue( crypt( 'foobar', 'jo'
) ) );
+ $stmt = $query->prepare();
+ $stmt->execute();
+ $query = new ezcQueryInsert( $this->db );
+ $query->insertInto( 'users' )
+ ->set( 'user', $query->bindValue( 'zhang.san' ) )
+ ->set( 'password', $query->bindValue( md5( 'asdfgh' ) ) );
+ $stmt = $query->prepare();
+ $stmt->execute();
+ $query = new ezcQueryInsert( $this->db );
+ $query->insertInto( 'users' )
+ ->set( 'user', $query->bindValue( 'hans.mustermann' ) )
+ ->set( 'password', $query->bindValue( 'abcdef' ) );
+ $stmt = $query->prepare();
+ $stmt->execute();
+ }
+ catch ( Exception $e )
+ {
+ $this->markTestSkipped();
+ }
+ }
+
+ public function tearDown()
+ {
+ $this->db->exec( 'DROP TABLE users' );
+ }
+
+ public function testDatabasePasswordNull()
+ {
+ $credentials = new ezcAuthenticationPasswordCredentials( 'jan.modaal',
null );
+ $database = new ezcAuthenticationDatabaseInfo( $this->db, 'users',
array( 'user', 'password' ) );
+ $authentication = new ezcAuthentication( $credentials );
+ $authentication->addFilter( new ezcAuthenticationDatabaseFilter(
$database ) );
+ $this->assertEquals( false, $authentication->run() );
+ }
+
+ public function testDatabaseSha1Correct()
+ {
+ $credentials = new ezcAuthenticationPasswordCredentials( 'jan.modaal',
'b1b3773a05c0ed0176787a4f1574ff0075f7521e' );
+ $database = new ezcAuthenticationDatabaseInfo( $this->db, 'users',
array( 'user', 'password' ) );
+ $authentication = new ezcAuthentication( $credentials );
+ $authentication->addFilter( new ezcAuthenticationDatabaseFilter(
$database ) );
+ $this->assertEquals( true, $authentication->run() );
+ }
+
+ public function testDatabaseSha1Fail()
+ {
+ $credentials = new ezcAuthenticationPasswordCredentials( 'jan.modaal',
'wrong password' );
+ $database = new ezcAuthenticationDatabaseInfo( $this->db, 'users',
array( 'user', 'password' ) );
+ $authentication = new ezcAuthentication( $credentials );
+ $authentication->addFilter( new ezcAuthenticationDatabaseFilter(
$database ) );
+ $this->assertEquals( false, $authentication->run() );
+ }
+
+ public function testDatabaseCryptCorrect()
+ {
+ $credentials = new ezcAuthenticationPasswordCredentials( 'john.doe',
'joB9EZ4O1cXDk' );
+ $database = new ezcAuthenticationDatabaseInfo( $this->db, 'users',
array( 'user', 'password' ) );
+ $authentication = new ezcAuthentication( $credentials );
+ $authentication->addFilter( new ezcAuthenticationDatabaseFilter(
$database ) );
+ $this->assertEquals( true, $authentication->run() );
+ }
+
+ public function testDatabaseCryptFail()
+ {
+ $credentials = new ezcAuthenticationPasswordCredentials( 'john.doe',
'wrong password' );
+ $database = new ezcAuthenticationDatabaseInfo( $this->db, 'users',
array( 'user', 'password' ) );
+ $authentication = new ezcAuthentication( $credentials );
+ $authentication->addFilter( new ezcAuthenticationDatabaseFilter(
$database ) );
+ $this->assertEquals( false, $authentication->run() );
+ }
+
+ public function testDatabaseMd5Correct()
+ {
+ $credentials = new ezcAuthenticationPasswordCredentials( 'zhang.san',
'a152e841783914146e4bcd4f39100686' );
+ $database = new ezcAuthenticationDatabaseInfo( $this->db, 'users',
array( 'user', 'password' ) );
+ $authentication = new ezcAuthentication( $credentials );
+ $authentication->addFilter( new ezcAuthenticationDatabaseFilter(
$database ) );
+ $this->assertEquals( true, $authentication->run() );
+ }
+
+ public function testDatabaseMd5Fail()
+ {
+ $credentials = new ezcAuthenticationPasswordCredentials( 'zhang.san',
'wrong password' );
+ $database = new ezcAuthenticationDatabaseInfo( $this->db, 'users',
array( 'user', 'password' ) );
+ $authentication = new ezcAuthentication( $credentials );
+ $authentication->addFilter( new ezcAuthenticationDatabaseFilter(
$database ) );
+ $this->assertEquals( false, $authentication->run() );
+ }
+
+ public function testDatabasePlainCorrect()
+ {
+ $credentials = new ezcAuthenticationPasswordCredentials(
'hans.mustermann', 'abcdef' );
+ $database = new ezcAuthenticationDatabaseInfo( $this->db, 'users',
array( 'user', 'password' ) );
+ $authentication = new ezcAuthentication( $credentials );
+ $authentication->addFilter( new ezcAuthenticationDatabaseFilter(
$database ) );
+ $this->assertEquals( true, $authentication->run() );
+ }
+
+ public function testDatabasePlainFail()
+ {
+ $credentials = new ezcAuthenticationPasswordCredentials(
'hans.mustermann', 'wrong password' );
+ $database = new ezcAuthenticationDatabaseInfo( $this->db, 'users',
array( 'user', 'password' ) );
+ $authentication = new ezcAuthentication( $credentials );
+ $authentication->addFilter( new ezcAuthenticationDatabaseFilter(
$database ) );
+ $this->assertEquals( false, $authentication->run() );
+ }
+
+ public function testDatabasePlainFailIncorrectUsername()
+ {
+ $credentials = new ezcAuthenticationPasswordCredentials( 'no such
user', 'wrong password' );
+ $database = new ezcAuthenticationDatabaseInfo( $this->db, 'users',
array( 'user', 'password' ) );
+ $authentication = new ezcAuthentication( $credentials );
+ $authentication->addFilter( new ezcAuthenticationDatabaseFilter(
$database ) );
+ $this->assertEquals( false, $authentication->run() );
+ }
+
+ public function testDatabaseInfo()
+ {
+ $database = ezcAuthenticationDatabaseInfo::__set_state( array(
'instance' => $this->db, 'table' => 'users', 'fields' => array( 'user',
'password' ) ) );
+ $this->assertEquals( $this->db, $database->instance );
+ $this->assertEquals( 'users', $database->table );
+ $this->assertEquals( array( 'user', 'password' ), $database->fields );
+ }
+
+ public function testDatabaseOptions()
+ {
+ $options = new ezcAuthenticationDatabaseOptions();
+
+ try
+ {
+ $options->no_such_option = 'wrong value';
+ $this->fail( "Expected exception was not thrown." );
+ }
+ catch ( ezcBasePropertyNotFoundException $e )
+ {
+ $this->assertEquals( "No such property name 'no_such_option'.",
$e->getMessage() );
+ }
+
+ $database = new ezcAuthenticationDatabaseInfo( $this->db, 'users',
array( 'user', 'password' ) );
+ $filter = new ezcAuthenticationDatabaseFilter( $database );
+ $filter->setOptions( $options );
+ $this->assertEquals( $options, $filter->getOptions() );
+ }
+
+ public function testDatabaseProperties()
+ {
+ $database = new ezcAuthenticationDatabaseInfo( $this->db, 'users',
array( 'user', 'password' ) );
+ $filter = new ezcAuthenticationDatabaseFilter( $database );
+ $this->assertEquals( true, isset( $filter->database ) );
+ $this->assertEquals( false, isset( $filter->no_such_property ) );
+
+ try
+ {
+ $filter->no_such_property = 'wrong value';
+ $this->fail( "Expected exception was not thrown." );
+ }
+ catch ( ezcBasePropertyNotFoundException $e )
+ {
+ $this->assertEquals( "No such property name 'no_such_property'.",
$e->getMessage() );
+ }
+
+ try
+ {
+ $filter->database = 'wrong value';
+ $this->fail( "Expected exception was not thrown." );
+ }
+ catch ( ezcBaseValueException $e )
+ {
+ $this->assertEquals( "The value 'wrong value' that you were trying
to assign to setting 'database' is invalid. Allowed values are: instance of
ezcAuthenticationDatabaseInfo.", $e->getMessage() );
+ }
+
+ try
+ {
+ $value = $filter->no_such_property;
+ $this->fail( "Expected exception was not thrown." );
+ }
+ catch ( ezcBasePropertyNotFoundException $e )
+ {
+ $this->assertEquals( "No such property name 'no_such_property'.",
$e->getMessage() );
+ }
+ }
+}
+?>
Property changes on:
experimental/AuthenticationDatabaseTiein/tests/filters/database/database_test.php
___________________________________________________________________
Name: svn:eol-style
+ native
Added: experimental/AuthenticationDatabaseTiein/tests/suite.php
===================================================================
--- experimental/AuthenticationDatabaseTiein/tests/suite.php 2007-05-04
14:55:21 UTC (rev 5059)
+++ experimental/AuthenticationDatabaseTiein/tests/suite.php 2007-05-04
14:58:45 UTC (rev 5060)
@@ -0,0 +1,39 @@
+<?php
+/**
+ * File containing the ezcAuthenticationDatabaseTieinSuite class.
+ *
+ * @copyright Copyright (C) 2005-2007 eZ systems as. All rights reserved.
+ * @license http://ez.no/licenses/new_bsd New BSD License
+ * @filesource
+ * @package AuthenticationDatabaseTiein
+ * @version //autogen//
+ * @subpackage Tests
+ */
+
+/**
+ * Including the tests
+ */
+require_once( "filters/database/database_test.php" );
+
+/**
+ * @package AuthenticationDatabaseTiein
+ * @version //autogen//
+ * @subpackage Tests
+ */
+class ezcAuthenticationDatabaseTieinSuite extends PHPUnit_Framework_TestSuite
+{
+ public function __construct()
+ {
+ parent::__construct();
+ $this->setName( "AuthenticationDatabaseTiein" );
+ ezcBase::addClassRepository( '/home/as/dev/ezcomponents/experimental',
'/home/as/dev/ezcomponents/experimental/autoload' );
+
+ $this->addTest( ezcAuthenticationDatabaseTest::suite() );
+ }
+
+ public static function suite()
+ {
+ return new ezcAuthenticationDatabaseTieinSuite();
+ }
+}
+?>
Property changes on: experimental/AuthenticationDatabaseTiein/tests/suite.php
___________________________________________________________________
Name: svn:eol-style
+ native
--
svn-components mailing list
[email protected]
http://lists.ez.no/mailman/listinfo/svn-components