Two key aspects of securing an application are authentication and authorization. While authentication is the process of verifying the identity of a user, authorization means determining if the user is allowed to do what he or she is about to do. That implies that authentication is a precondition for authorization. Input validation and filtering is another aspect, which is especially important in web applications.
Users are essential for an authentication system. In wCMF users are represented as instances of classes implementing the
User interface. Since wCMF's authorization system is role based, users are organized in roles. Role classes implement the
Role interface. A user could have multiple roles, while multiple users could have the same role.
The concrete implementations of these interfaces are configured in
In wCMF authentication is handled by implementations of
DefaultAuthenticationManager is used. It implements a login/password based authentication procedure by matching the given user credentials against existing
User instances. These instances are provided by implementations of
The following code demonstrates the authentication process as implemented in
- The login of the user, that is associated with the current session - the current user - is obtained using the
Session::getAuthUsermethod. Before successful authentication, this login is
The configuration of the authentication process in the default application looks like the following:
DefaultPrincipalFactory retrieves user instances from the storage, it needs to configured with the appropriate entity types. If required, the default user type may be replaced by custom implementations of
The purpose of authorization is controlling access to application resources, which could be controllers or entity instances. To establish access control, rules have to be defined in the first place and enforced afterwards.
Access control rules are expressed as permissions. Permissions are either granted or denied to a role (see Users and Roles).
A permission definition in wCMF consists of two parts:
- The permission subject is a combination of a resource, a context and an action and the notation is the same as for action keys (see Action Key), except that the controller value is an arbitrary resource.
- The involved roles are listed space-separated and each one is prepended with a modifier (+ for granting and - for denying).
The following code illustrates the format:
Roles that are not listed in the permission are denied per default. The wildcard character (*) is used to define the permission for all roles that are not explicitly listed.
The following code grants the permission only to allowedRole
and is equivalent to
Granted roles are evaluated before denied ones. That means that the following code grants the permission to a user who has both roles (allowedRole and deniedRole) although the permission is denied for one of the roles.
The resource value could be set to any string to implement custom application specific permissions.
Besides this, wCMF uses the following built-in resources:
- Controller to restrict access on execution of a controller (e.g.
- Entity type to restrict access on all instances of an entity type (e.g.
- Entity property to restrict access on a certain property of an entity type (e.g.
- Entity instance to restrict access on one entity instance (e.g.
- Entity instance propery to restrict access on a certain property of one entity instance (e.g.
Besides assigning static roles to users, it's sometimes useful to have roles, that evaluate dynamically in the current context (requested resource, user and action). With these so called dynamic roles it is for example possible to assign permissions to the creator of an instance or realize date/time based permissions.
Dynamic roles implement the
DynamicRole interface and are defined in the
DynamicRoles configuration section.
- Dynamic roles are checked before static role assignments. That means you could define a dynamic role with the same name as a static role and add custom matching code.
Permissions on entity instances are passed to child entities in a composite relation (see Associations). The following image illustrates this:
If access to the
Book instance Book A is restricted for one role, the same restriction also applies for the
Chapter instances belonging to it (Chapter A1, Chapter A2). An inherited access restriction can be removed by explicitly granting the permission on the object in question.
The following code shows some permission examples:
Permission management includes creation, modification and deletion of permissions as well as handling authorization requests. In an wCMF application an instance of
PermissionManager is used for these tasks. It is configured in the
PermissionManager configuration section.
Generally there are two kinds of permissions to be defined in an application.
- Static permissions are already known when the application is designed, e.g. restriction to entity types or controllers. Since these permissions are not likely to be changed by application users, they can be stored in the application configuration.
- Dynamic permissions are defined on the application data. These permissions will be set when the appropriate data is created. To allow application users to change these permissions they are typically stored in the database.
Different permission types require different ways of permission management, particularly regarding storing permissions. To support this wCMF provides several implementations of the
StaticPermissionManageris used to retrieve permissions from the application configuration. The permissions are stored in the
Authorizationconfiguration section like shown in the following code:
DefaultPermissionManagerhandles permissions that are stored in the database. The entity type that actually stores the permissions is required to have a resource, context, action and roles attribute and is defined in the application configuration. The following lines are taken from the configuration of the default application:
ChainedPermissionManageris used to combine different
PermissionManagerinstances. When asked for authorization, it delegates the request to all it's managers, while creation, modification and deletion of permissions is always handled by the first contained manager. An example configuration of this manager is shown in the code below:
NullPermissionManageracts like there is no permission manager and is merely used for testing.
All implementations inherit from
AbstractPermissionManager, which already provides most of parts for handling authorization requests.
There are situations, where a piece of code requires a certain permission in the context of the current user, which is not possible to grant throughout the whole application. For example, if a user wants to sign in to the application,
PrincipalFactory needs to provide a user instance although the anonymous user is not allowed to read user instances. To accomplish this,
PermissionManager allows to set a transient, temporary permission like in the following example:
To test, if a user has the permission to access a resource in the requested way the method
PermissionManager::authorize is used. It returns a boolean value indicating whether the user is authorized or not.
The following code shows how to determine, if the current user is allowed to read the given object:
Default policies answer the question, what happens if no permission is defined on a requested resource. These rules depend on the authentication status of the current user and are defined in
AbstractPermissionManager as follows:
- Not authenticated: Resource is not accessible
- Authenticated: Resource is accessible
wCMF provides the following
DefaultSessionis a session that uses PHP's default server side session implementation in combination with a session cookie.
AuthTokenSessionadditionally requires clients to send an
ClientSideSessionhas no server state as it stores the data in cookies.
Generally all external input sent to the application should be considered harmful.
In PHP this input is stored in so called Superglobals, e.g. $_GET, $_POST, $_FILES. To avoid using these global variables directly in code, wCMF encapsulates them in the
Request instance and makes them available through the
Request::getValue method. Besides the variable name this method defines the following optional parameters:
$defaultdefault value if the value is not contained in the request
$validateDescdescription of the validation to be applied to the value
$validateDesc parameter is passed to the
Validator::validate method and
null is returned if validation fails.
The following code demonstrates the usage:
For more information about validation see Validation.
To avoid CSRF vulnerabilities in a web application, forms are usually protected by using CSRF tokens. The
Controller::generateCsrfToken method is used to generate a unique token value for each form display. The token is validated by using the
Controller::validateCsrfToken method when the form is submitted. For proper identification, tokens are referenced by a name (e.g. the form's name) which is passed to these methods.
The following example demonstrates the usage of these methods.
A new token value is generated each time the form is displayed:
This creates the
csrf_token response variable. The variable is used to add the token as a hidden field to the form:
After form submission the controller validates the token and acts appropriately: