Security¶
Security Framework¶
All tasks related to security of a FLOW3 application are handled centrally by the security framework. Besides other functionality, this includes especially features like authentication, authorization, channel security and a powerful policy component. This chapter describes how you can use FLOW3’s security features and how they work internally.
Activation and initialization¶
All the described features in this chapter can be enable or disabled by setting the following value in Settings.yaml configuration file:
Example: Enable the security features in the Settings.yaml configuration file
FLOW3: security: enable: TRUE
If set to “yes”, which is the default, the security framework engages with FLOW3 by vowing in two AOP advices into the MVC dispatcher and another two into the persistence layer classes.
Security context¶
The first security advice (initializeSecurity in the TYPO3\FLOW3\Security\Aspect\RequestDispatchingAspect) initializes the security context for the current request. The security context (TYPO3\FLOW3\Security\Context) shipped with FLOW3, lies in session scope and holds context data like the current authentication status. That means, if you need data related to security, the security context (you can get it easily with dependency injection) will be your main information source. The details of the context’s data will be described in the next chapters.
Authentication¶
One of the main things people associate with security is authentication. That means to identify your communication partner - the one sending a request to FLOW3. Therefore the framework provides an infrastructure to easily use different mechanisms for such a plausibility proof. The most important achievment of the provided infrastructure is its flexible extensibility. You can easily write your own authentication mechanisms and configure the framework to use them without touching the framework code itself. The details are explained in the section Implementing your own authentication mechanism.
Using the authentication controller¶
First, let’s see how you can use FLOW3’s authentication features. There is a special controller in the security package: the AuthenticationController. This controller has two actions, namely authenticateAction() and logoutAction(), an appropriate route is configured. If you call http://localhost/flow3/authenticate in your Browser, the default authentication mechanism will be triggered. This mechanism, implemented in a so called authentication provider, authenticates a user account by checking a username and password against accounts stored in the content repository. [1]
The configuration for this default provider, which is shipped with FLOW3’s default configuration looks like this:
Example: Configuration of the default username/password authentication mechanism in Settings.yaml
FLOW3:
security:
authentication:
providers:
DefaultProvider:
providerClass: PersistedUsernamePasswordProvider
This registers the PersistedUsernamePasswordProvider authentication provider under the name “DefaultProvider” as the only, global authentication mechanism. To successfully authenticate an account with this default provider, you’ll obviously have to provide a username and password. This is done by sending two POST variables to the authentication controller. Have a look at the following HTML snippet with a simple login form you can use for that:
Example: A simple login form
<form action="flow3/authenticate" method="post" name="loginform">
<input type="text" id="username"
name="__authentication[TYPO3][FLOW3][Security][Authentication][Token][UsernamePassword][username]"
value="" tabindex="1" />
<input type="password" id="password"
name="__authentication[TYPO3][FLOW3][Security][Authentication][Token][UsernamePassword][password]"
value="" tabindex="2" />
<input type="submit" value="Login" tabindex="3" />
</form>
After submitting the form, the internal authentication process will be triggered and if you provided valid credentials an account will be authenticated afterwards. [2]
Note
After authentication the authenticate() action will automatically redirect to the original request, if the authentication process has been triggerd due missing privileges while handling this original request.
The internal authentication process¶
Now that you know, how you can authenticate, let’s have a look at the internal process. The following sequence diagram shows the participating components and their interaction:
As already explained, the security framework is initialized in the dispatcher by vowing in an AOP advice, which resides in the RequestDispatchingAspect class. This advice intercepts the request dispatching before any controller is called. Regarding authentication, you can see, that a so called authentication token will be stored in the security context and some credentials will be updated in it.
Authentication tokens¶
An authentication token holds the status of a specific authentication mechanism, for example it receives the credentials (e.g. a username and password) needed for authentication and stores one of the following authentication states in the session. [3]
These constants are defined in the authentication token interface (TYPO3\FLOW3\Security\Authentication\TokenInterface) and the status can be obtained from the getAuthenticationStatus() method of any token.
Tip
If you only want to know, if authentication was successful, you can call the convenient method isAuthenticated().
- NO_CREDENTIALS_GIVEN
- This is the default state. The token is not authenticated and holds no credentials,
that could be used for authentication.
- WRONG_CREDENTIALS
It was tried to authenticate the token, but the credentials were wrong.
- AUTHENTICATION_SUCCESSFUL
The token has been successfully authenticated.
- AUTHENTICATION_NEEDED
This indicates, that the token received credentials, but has not been authenticated yet.
Now you might ask yourself, how a token receives its credentials. The simple answer is: It’s up to the token, to fetch them from somewhere. The default UsernamePassword token for example looks for a username and password in the two POST parameters: __authentication[TYPO3][FLOW3][Security][Authentication][Token][UsernamePassword][username] and __authentication[TYPO3][FLOW3][Security][Authentication][Token][UsernamePassword][password] (see Using the authentication controller). The framework only makes sure that updateCredentials() is called on every token, then the token has to set possibly available credentials itself, e.g. from available headers or parameters or anything else you can provide credentials with.
Authentication manager and provider¶
After the tokens have been initialized the original request will be processed by the resolved controller. In our case this is the special authentication controller (TYPO3\FLOW3\Security\Authentication\Controller\AuthenticationController) of FLOW3, which will call the authentication manager to authenticate the tokens. In turn the authentication manager calls all authentication providers in the configured order. A provider implements a specific authentication mechanism and is therefore responsible for a specific token type. E.g. the already mentioned PersistedUsernamePasswordProvider provider is able to authenticate the UsernamePassword token.
After checking the credentials, it is the responsibility of an authentication provider to set the correct authentication status (see above) and Roles in its corresponding token. The role implementation resides in the TYPO3\FLOW3\Security\Poilcy namespace. (see the Policy section for details).
Account management¶
In the previous section you have seen, how accounts can be authenticated in FLOW3. What was concealed so far is, how these accounts are created or what is exactly meant by the word “account”. First of all let’s define what accounts are in FLOW3 and how they are used for authentication. Following the OASIS CIQ V3.0 [4] specification, an account used for authentication is separated from a user or more general a party. The advantage of this separation is the possibility of one user having more than one account. E.g. a user could have an account for the UsernamePassword provider and one account connected to an LDAP authentication provider. Another scenario would be to have different accounts for different parts of your FLOW3 application. Read the next section Advanced authentication configuration to see how this can be accomplished.
As explained above, the account stores the credentials needed for authentication. Obviously these credentials are provider specific and therefore every account is only valid for a specific authentication provider. This provider - account connection is stored in a property of the account object named authenticationProviderName. Appropriate getters and setters are provided. The provider name is configured in the Settings.yaml file. If you look back to the default configuration, you’ll find the name of the default authentication provider: DefaultProvider. Besides that, each account has another property called credentialsSource, which points to the place or describes the credentials needed for this account. This could be an LDAP query string, or in case of the PersistedUsernamePasswordProvider provider, the username, password hash and salt are stored directly in this member variable.
It is the responsibility of the authentication provider to check the given credentials from the authentication token, find the correct account for them [5] and to decide about the authentication status of this account.
Note
In case of a directory service, the real authentication will probably not take place in the provider itself, but the provider will pass the result of the directory service on to the authentication token.
Creating accounts¶
Creating an account is as easy as creating a new account object and add it to the account repository. Look at the following example, which uses the TYPO3\FLOW3\Security\AccountFactory to create a simple username/password account for the DefaultProvider:
Example: Add a new username/password account
$identifier = 'andi';
$password = 'secret';
$roles = array('Administrator');
$authenticationProviderName = 'DefaultProvider';
$account = $this->accountFactory->createAccountWithPassword($identifier, $password, $roles, $authenticationProviderName);
$this->accountRepository->add($account);
The way the credentials are stored internally is completely up to the authentication provider. The PersistedUsernamePasswordProvider uses the TYPO3\FLOW3\Security\Cryptography\HashService to verify the given password. In the example above, the given plaintext password will be securely hashed by the HashService. The hashing is the main magic happening in the AccountFactory and the reason why we don’t create the account object directly. If you want to learn more about secure password hashing in FLOW3, you should read the section about Cryptography below. You can also see, that there is an array of roles added to the account. This is used by the policy system and will be explained in the according section below.
Note
This example expects the account factory and account repository to be available in $this->accountFactory and $this->accountRepository respectively. If you use this snippet in an action controller, these can be injected very easily by dependency injection.
Advanced authentication configuration¶
Parallel authentication¶
Now that you have seen all components, taking part in the authentication process, it is time to have a look at some advance configuration possibilities. Just to remember, here is again the configuration of the default authentication provider:
security:
authentication:
providers:
DefaultProvider:
providerClass: PersistedUsernamePasswordProvider
If you have a closer look at this configuration, you can see, that the word providers is plural. That means, you have the possibility to configure more than one provider and use them in “parallel”.
Note
You will have to make sure, that each provider has a unique name. In the example above the provider name is DefaultProvider.
Example: Configuration of two authentication providers
security:
authentication:
providers:
MyLDAPProvider:
providerClass: TYPO3\MyCoolPackage\Security\Authentication\MyLDAPProvider
options: 'Some LDAP configuration options'
DefaultProvider:
providerClass: PersistedUsernamePasswordProvider
This will advice the authentication manager to first authenticate over the LDAP provider and if that fails it will try to authenticate the default provider. So this configuration can be seen as an authentication fallback chain, of course you can configure as many providers as you like, but keep in mind that the order matters.
Note
As you can see in the example, the LDAP provider is provided with some options. These are specific configuration options for each provider, have a look in the detailed description to know if a specific provider needs more options to be configured and which.
Multi-factor authentication strategy¶
There is another configuration option to realize a multi-factor-authentication. It defaults to oneToken. A configurable authentication strategy of allTokens forces the authentication manager to always authenticate all configured providers and to make sure that every single provider returned a positive authentication status to one of its tokens. The authentication strategy atLeastOneToken will try to authenticate as many tokens as possible but at least one. This is helpful to realize policies with additional security only for some resources (e.g. SSL client certificates for an admin backend).
configuration:
security:
authentication:
authenticationStrategy: allTokens
Reuse of tokens and providers¶
There is another configuration option for authentication providers called tokenClass, which can be specified in the provider settings. By this option you can specify which token should be used for a provider. Remember the token is responsible for the credentials retrieval, i.e. if you want to authenticate let’s say via username and password this setting enables to to specify where these credentials come from. So e.g. you could reuse the one username/password provider class and specify, whether authentication credentials are sent in a POST request or set in an HTTP Basic authentication header.
Example: Specifying a specific token type for an authentication provider
security:
authentication:
providers:
DefaultProvider:
providerClass: PersistedUsernamePasswordProvider
tokenClass: UsernamePasswordHttpBasic
Request Patterns¶
Now that you know about the possibility of configuring more than one authentication provider another scenario may come to your mind. Just imagine an application with two areas: One user area and one administration area. Both must be protected, so we need some kind of authentication. However for the administration area we want a stronger authentication mechanism than for the user area. Have a look at the following provider configuration:
Example: Using request patterns
security:
authentication:
providers:
MyLDAPProvider:
providerClass: TYPO3\MyCoolPackage\Security\Authentication\MyLDAPProvider
options: 'Some LDAP configuration options'
requestPatterns:
controllerObjectName: TYPO3\MyApplication\AdministrationArea\.*
DefaultProvider:
providerClass: PersistedUsernamePasswordProvider
requestPatterns:
controllerObjectName: TYPO3\MyApplication\UserArea\.*
Look at the new configuration option requestPatterns. This enables or disables an authentication provider, depending on given patterns. The patterns will look into the data of the current request and tell the authentication system, if they match or not. The patterns in the example above will match, if the controller object name of the current request (the controller to be called) matches on the given regular expression. If a pattern does not match, the corresponding provider will be ignored in the whole authentication process. In the above scenario this means, all controllers responsible for the administration area will use the LDAP authentication provider, the user area controllers will be authenticated by the default username/password provider.
Note
You can use more than one pattern in the configuration. Then the provider will only be active, if all patterns match on the current request.
Tip
There can be patterns that match on different data of the request. Just imagine an IP pattern, that matches on the request IP. You could, e.g. provide different authentication mechanisms for people coming from your internal network, than for requests coming from the outside.
Tip
You can easily implement your own pattern. Just implement the interface TYPO3\FLOW3\Security\RequestPatternInterface and configure the pattern with its full qualified namespace.
Available request patterns
| Request Pattern | Match criteria | Configuration options |
|---|---|---|
| controllerObjectName | Matches on the object name of the controller that has been resolved by the MVC dispatcher for the current . request | Expects one regular expression, to match on the object name. For example.: F3MyApplicationAdministrationArea.* |
Authentication entry points¶
One question that has not been answered so far is: what happens if the authentication process fails? In this case the authentication manager will throw an AuthenticationRequired exception. It might not be the best idea to let this exception settle its way up to the browser, right? Therefore we introduced a concept called authentication entry points. These entry points catch the mentioned exception and should redirect the user to a place where she can provide proper credentials. This could be a login page for the username/password provider or an HTTP header for HTTP authentication. An entry point can be configured for each authentication provider. Look at the following example, that redirects to a login page (Using the WebRedirect entry point).
Example: Redirect an ``AuthenticationRequired`` exception to the login page
security:
authentication:
providers:
DefaultProvider:
providerClass: PersistedUsernamePasswordProvider
entryPoint:
WebRedirect:
uri: login/
Note
Of course you can implement your own entry point and configure it by using its full qualified class name. Just make sure to implement the TYPO3\FLOW3\Security\Authentication\EntryPointInterface interface.
Tip
If a request has been intercepted by an AuthenticationRequired exception, this request will be stored in the security context. By this, the authentication process can resume this request afterwards. Have a look at the FLOW3 authentication controller if you want to see this feature in action.
Available authentication entry points
| Entry Point | Description | Configuration options |
|---|---|---|
| WebRedirect | Triggers an HTTP redirect to a given uri. that has been resolved by the MVC dispatcher for the current . request | Expects an associtative array with one entry. For example.: uri: login/ |
| HttpBasic | Adds a WWW-Authenticate header to the response, which will trigger the browsers authenticaiton form. | Optionally takes an option realm, which will be displayed in the authentication prompt. |
Authentication mechanisms shipped with FLOW3¶
This section explains the details of each authentication mechanism shipped with FLOW3. Mainly the configuration options and usage will be exposed, if you want to know more about the entire authentication process and how the components will work together, please have a look in the previous sections.
Simple username/password authentication¶
Provider
The implementation of the corresponding authentication provider resides in the class TYPO3\FLOW3\Security\Authentication\Provider\PersistedUsernamePasswordProvider. It is able to authenticate tokens of the type TYPO3\FLOW3\Security\Authentication\Token\UsernamePassword. It expects a credentials array in the token which looks like that:
array(
'username' => 'admin',
'password' => 'plaintextPassword'
);
It will try to find an account in the TYPO3\FLOW3\Security\AccountRepository that has the username value as account identifier and fetch the credentials source, which has to be in the following format: HashOfThePassword,Salt
Tip
You should always use the FLOW3 hash service to generate hashes! This will make sure that you really have secure hashes.
The provider will explode the credentials source by the “,” and try to authenticate the token by asking the FLOW3 hash service to verfiy the hashed password against the given plaintext password in from the token. If you want to know more about accounts and how you can create them, look in the corresponding section above.
- Token*
The username/password token is implemented in the class TYPO3\FLOW3\Security\Authentication\Token\UsernamePassword. It fetches the credentials from the HTTP POST data, look at the following program listing for details:
$postArguments = $this->environment->getRawPostArguments();
$username = \TYPO3\FLOW3\Reflection\ObjectAccess::getPropertyPath($postArguments,
'__authentication.TYPO3.FLOW3.Security.Authentication.Token.UsernamePassword.username');
$password = \TYPO3\FLOW3\Reflection\ObjectAccess::getPropertyPath($postArguments,
'__authentication.TYPO3.FLOW3.Security.Authentication.Token.UsernamePassword.password'');
Note
The token expects a plaintext password in the POST data. That does not mean, you have to transfer plaintext passwords, however it is not the responsibility of the authentication layer to encrypt the transfer channel. Look in the section about Channel security for any details.
Implementing your own authentication mechanism¶
One of the main goals for the authentication architecture was to provide an easily extensible infrastructure. Now that the authentication process has been explained, you’ll here find the steps needed to implement your own authentication mechanism:
Authentication token
You’ll have to provide an authentication token, that implements the interface TYPO3\FLOW3\Security\Authentication\TokenInterface:
1. The most interesting method is updateCredentials(). There you’ll get the current request and you’ll have to make sure that credentials sent from the client will be fetched and stored in the token.
2. Implement the remaining methods of the interface. These are mostly getters and setters, have a look in one of the existing tokens (for example TYPO3\FLOW3\Security\Authentication\Token\UsernamePassword), if you need more information.
Authentication provider
After that you’ll have to implement your own authentication strategy by providing a class, that implements the interface TYPO3\FLOW3\Security\Authentication\AuthenticationProviderInterface:
1. In the constructor you will get the name, that has been configured for the provider and an optional options array. Basically you can decide on your own which options you need and how the corresponding yaml configuration will look like.
2. Then there has to be a canAuthenticate() method, which gets an authentication token and returns a boolean value whether your provider can authenticate that token or not. Most likely you will call getAuthenticationProviderName() on the token and check, if it matches the provider name given to you in your provider’s constructor. In addition to this, the method getTokenClassNames() has to return an array with all authentication token classes, your provider is able to authenticate.
3. All the magic will happen in the authenticate() method, which will get an appropriate authentication token. Basically you could do whatever you want in this method, the only thing you’ll have to make sure is to set the correct status (possible values are defined as constants in the token interface and explained above). If authentication succeeds you might also want to set an account in the given token, to add some roles to the current security context. However, here is the recommended way of what should be done in this method and if you don’t have really good reasons, you shouldn’t deviate from this procedure.
- Get the credentials provided by the client from the authentication token (getCredentials())
- Retrieve the corresponding account object from the account repository, which you should inject into your provider by dependency injection. The repository provides a convenient find method for this task: findActiveByAccountIdentifierAndAuthenticationProviderName().
- The credentialsSource property of the account will hold the credentials you’ll need to compare or at least the information, where these credentials lie.
- Start the authentication process (e.g. compare credentials/call directory service/...).
- Depending on the authentication result, set the correct status in the authentication token, by calling setAuthenticationStatus().
- Set the account in the authentication token, if authentication succeeded. This will add the roles of this token to the security context.
Authorization¶
In this section we will deal with the authorization features of FLOW3. You won’t find any advices, how to configure access rights here, please refer to the next section about Policies aka Access Control Lists (ACLs), which form the default method to model and configure access rules.
Authorize method invocations¶
The most general thing, which you want to protect in every application is the invocation of certain methods. By controlling, which methods are allowed to be called and which not, it can be globally ensured, that no unprivileged action will be executed at any time. This is what you would usually do, by adding an access check at the beginning of your privileged method. In FLOW3, there is the opportunity to enforce these checks without touching the actual method at all. Of course FLOW3’s AOP features are used to realize this completely new perspective on authorization. If you want to learn more about AOP, please refer to the corresponding chapter in this reference.
First, let’s have a look at the following sequence diagram to get an overview of what is happening when an authorization decision is formed and enforced:
As already said, the whole authorization starts with an intercepted method, or in other words with a method that should be protected and only be called by privileged users. In the chapter about AOP you’ve already read, that every method interception is implemented in a so called advice, which resides in an aspect class. Here we are: the TYPO3\FLOW3\Security\Aspect\PolicyEnforcementAspect. Inside this aspect there is the enforcePolicy() advice, which hands over to FLOW3’s authorization components.
The next thing to be called is a security interceptor. This interceptor calls the authentication manager before it continues with the authorization process, to make sure that the authentication status is up to date. Then an access decision manager is called, which has to decide, if it is allowed to call the intercepted method. If not it throws an access denied exception. If you want, you could implement your own access decision manager. However, there is a very flexible one shipped with FLOW3 (TYPO3\FLOW3\Security\Authorization\AccessDecisionVoterManager), which uses the following voting process to meet its decision:
- Check for registered access decision voters.
- Ask every voter, to vote for the given method call (or join point in AOP nomenclature).
- Count the votes and grant access, if there is at least one VOTE_GRANT vote and no VOTE_DENY vote. In all other cases an access denied exception will be thrown.
On access decision voters
As you have seen, the default way of deciding on access is done by voting. This makes the whole authorization process very flexible and very easily extensible. You can at any time write your own voter classes and register them, just make sure to implement the interface TYPO3\FLOW3\Security\Authorization\AccessDecisionVoterInterface. Then you have to register your custom voter as shown below:
security:
authorization:
accessDecisionVoters: [TYPO3\FLOW3\Security\Authorization\Voter\Policy, MyCompany\MyPackage\Security\MyCustomVoter]
Note
By default there is always one voter registered: TYPO3\FLOW3\Security\Authorization\Voter\Policy. This voter connects the authorization system to the policy component, by returning a vote depending on the configured security policy. Read the section about Policies, to learn more about the default policy handling in FLOW3.
If asked, each voter has to return one of the three possibles votes: grant, deny or abstain. There are appropriate constants defined in the voter interface, which you should use for that. You might imagine that a voter has to return an abstain vote, if it is not able to give a proper grant or deny vote.
Now it could be the case that all registered voters abstain. Usually the access decision manager will deny access then. However, you can change that behavior by configuring the following option:
security:
authorization:
allowAccessIfAllVotersAbstain: FALSE
Application firewall¶
Besides the AOP powered authorization, there is another line of defense: the filter firewall. This firewall is triggered directly when a request arrives at the MVC dispatcher. After that the request is analyzed and can be blocked/filtered out. This adds a second level of security right at the beginning of the whole framework run, which means that a minimal amount of potentially insecure code will be executed before that.
Blocking request with FLOW3’s filter firewall
The firewall itself is added to the MVC dispatcher by AOP, to completely decouple security from the MVC framework and to have the possibility of disabling security. Blocking requests with the firewall is not a big thing at all, basically a request filter object is called, which consists of a request pattern and a security interceptor. The simple rules is: if the pattern matches on the request, the interceptor is invoked. Request Patterns are also used by the authentication components and are explained in detail there. Talking about security interceptors: you already know the policy enforcement interceptor, which triggers the authorization process. Here is a table of available interceptors, shipped with FLOW3:
Note
Of course you can implement your own interceptor. Just make sure to implement the interface: TYPO3\FLOW3\Security\Authorization\InterceptorInterface.
FLOW3’s built-in security interceptors
| Security interceptor | Invocation action |
|---|---|
| PolicyEnforcement | Triggers the authorization process as described one section above. |
| RequireAuthenticaiton | Calls the authentication manager to authenticate all active tokens for the current request. |
Of course you are able to configure as many request filters as you like. Have a look at the following example to get an idea how a firewall configuration will look like:
Example: Firewall configuration in the *Settings.yaml file*
TYPO3:
FLOW3:
security:
firewall:
rejectAll: FALSE
filters:
-
patternType: 'URI'
patternValue: '/some/url/.*'
interceptor: 'AccessGrant'
-
patternType: 'URI'
patternValue: '/some/url/blocked.*'
interceptor: 'AccessDeny'
-
patternType: 'MyCompany\MyPackage\Security\MyOwnRequestPattern'
patternValue: 'some pattern value'
interceptor: 'MyCompany\MyPackage\Security\MyOwnSecurityInterceptor'
As you can see, you can easily use your own implementations for request patterns and security interceptors.
Note
You might have noticed the rejectAll option. If this is set to yes, only request which are explicitly allowed by a request filter will be able to pass the firewall.
Policies aka Access Control Lists (ACLs)¶
This section will introduce the recommended and default way of connecting authentication with authorization. The special and really powerful part of FLOW3’s way is the possibility to do that completely declarative. This gives you the possibility to change the security policy of your application without touching any PHP code. The policy system deals with three major objects, which are explained below: roles, resources and acl entries. All policy definitions are configured in the Policy.yaml files.
Roles
In the section about authentication so called roles were introduced. A role can be attached to a user’s security context, to determine which privileges should be granted to her. I.e. the access rights of a user are decoupled from the user object itself, making it a lot more flexible, if you want to change them. In FLOW3 a role is mainly just a string, which must be unique in the whole FLOW3 instance. Following there is an example configuration, that will proclaim the roles Administrator, Customer, and PrivilegedCustomer to the system.
Example: roles definition in the *Policy.yaml file*
roles:
Administrator: []
Customer: []
PrivilegedCustomer: [Customer]
The role PrivilegedCustomer is configured as a sub role of Customer, for example it will inherit the privileges from the Customer role.
FLOW3 will always add the magic Everybody role, which you don’t have to configure youreself. This role will also be present, if no account is authenticated.
Likewise, the magic role Anonymous is added to the security context if a user is not authenticated.
Resources
The counterpart to roles are resources. A resource in general is an object, you want to protect, for example you want to configure which roles are allowed to access a certain resource. The policy configuration deals with method and entity resources.
Entity resources are related to content security, which are explained in the Content security section below. In this section we will deal with method resources only.
Example: resources definition in the *Policy.yaml file*
resources:
methods:
listMethods: 'method(TYPO3\FooPackage\SomeClass->list.*())'
updateMethods: 'method(TYPO3\FooPackage\SomeClass->update.*())'
deleteMethods: 'method(TYPO3\FooPackage\.*->delete.*(force == TRUE))'
modifyMethods: 'TYPO3_FooPackage_update || TYPO3_FooPackage_delete'
Each resource is defined by a unique name [6] and a so called pointcut expression. Practically a pointcut expression is a regular expression that matches on certain methods. There are more pointcut expressions you can use to describe the methods addressed by a specific resource, the whole syntax is described in detail in the chapter about AOP.
ACL entries
The last step is to connect resources with roles by assigning access privileges. Let’s have a look at an example for such ACL entries:
Example: ACL entry definitions in the *Policy.yaml file*
acls:
Administrator:
methods:
listMethods: GRANT
modifyMethods: GRANT
Customer:
methods:
listMethods: GRANT
PriviledgedCustomer:
methods:
updateMethods: GRANT
deleteMethods: DENY
This will end up in Administrators being able to call all update* and list* methods in the class SomeClass and all delete* methods no matter which class in the whole package FooPackage. However, Customers are only able to call the list* methods, while PrivilegedCustomers are also allowed to call the update* methods. And all this without touching one line of PHP code, isn’t that convenient?
Privilege evaluation
Privilege evaluation is a really complex task, when you think carefully about it. However, if you remember the following two rules, you will have no problems or unexpected behaviour when writing your policies:
- If a DENY privilege is configured for one of the user’s roles, access will be denied
no matter how many grant privileges there are in other roles.
If no privilege has been defined for any of the user’s roles, access will be denied.
Runtime constraints
Runtime constraints are a very poweful feature of FLOW3’s AOP framework. A full reference of the possibilities can be found in the AOP chapter of this documentation. However, this features was mainly implemented to support sophisticated policy definitions and therefore here is a short introduction by two simple examples on how to use it:
Example: runtime constraints usage in the security policy
-
resources:
methods:
TYPO3_FooPackage_firstResource: 'method(TYPO3\FooPackage\SomeClass->updateProject(title != "FLOW3"))'
TYPO3_FooPackage_secondResource: TYPO3_FooPackage_firstResource && evaluate(current.securityContext.party.name == "Andi")
The above configuration defines a resource that matches on the updateProject method only if it is not called with the title arugment equal to “FLOW3”. The second resource matches if the first one matches and the name property of the currently authenticated party is equal to “Andi”.
Content security¶
Security for persisted objects¶
FIXME: This section is not complete yet
resources:
entities:
Acme_MyPackage_Domain_Model_Customer:
Acme_MyPackage_Customers_All: 'ANY'
Acme_MyPackage_Customers_Vip: 'this.vip == TRUE'
Acme_MyPackage_Customers_Me: 'current.securityContext.account != this.account && this.account != NULL'
The Acme_MyPacakge_Customer_All resource will match any customer object. The Acme_MyPacakge_Customer_Vip resource matches all customer’s which have their vip attribute set. The Acme_MyPackage_Customer_Me resource matches any customer object whose account property matches the currently logged in account.
- if an entity resource is defined, access is denied automatically to all who don’t have access granted to that new resource explictly defined in the ACLs.
- if there is no ANY resource defined, only objects explicitly matched by one of the other resources are denied by default.
- if there is a ANY resource define, all objects of this type will be denied for all users not have a grant privilege for this ANY resource.
- The key Acme_MyPackage_Domain_Model_Customer has to reflect the full qualified class name of your entity, while ‘’ is replaced by ‘_’ due to YAML syntax constraints.
- The DENY privilege works the same as for methods. If it is set for one of the resources you will never see entities matched by this resource, no matter how many GRANT privileges there might be set for other roles you also have.
- TODO: Explain query rewriting via aspect to the persistence layer
- NOTE: not working for DQL queries currently (only QOM!)
Security for files aka secure downloads¶
- add publishing configuration to resource objects
- publishing in subfolder named like session id
- optimization with role subdirs -> only publish once for a role
- server specific restriction publishing like .htaccess files for apache
Fluid (view) integration¶
Now that the policy is technically enforced, these rules should also be reflected in the view. E.g. a button or link to delete a customer should not be shown, if the user has not the privilege to do so. If you are using the recommended Fluid templating engine, you can simply use the security view helpers shipped with Fluid. Otherwise you would have to ask the policy service (TYPO3\FLOW3\Security\Policy\PolicyService) for the current privilege situation and implement the view logic on your own, however this seems not to be the best idea one can have. Below you’ll find a short description of the available Fluid view helpers.
ifAccess view helper¶
This view helper implements an ifAccess/else condition, have a look at the following example, which should be more or less self-explanatory:
Example: the ``ifAccess`` view helper
<f:security.ifAccess resource="someResource">
This is being shown in case you have access to the given resource
</f:security.ifAccess>
<f:security.ifAccess resource="someResource">
<f:then>
This is being shown in case you have access.
</f:then>
<f:else>
This is being displayed in case you do not have access.
</f:else>
</f:security.ifAccess>
As you can imagine, the main advantage is, that the view will automatically reflect the configured policy rules, without the need of changing any template code.
ifHasRole view helper¶
This view helper is pretty similar to the ifAccess view helper, however it does not check the access privilege for a given resource, but the availability of a certain role. For example you could check, if the current user has the Administrator role assigned:
Example: the ``ifHasRole`` view helper
<f:security.ifHasRole role="Administrator">
This is being shown in case you have the Administrator role (aka role).
</f:security.ifHasRole>
<f:security.ifHasRole role="Administrator">
<f:then>
This is being shown in case you have the role.
</f:then>
<f:else>
This is being displayed in case you do not have the role.
</f:else>
</f:security.ifHasRole>
Channel security¶
Currently channel security is an open task. Stay tuned for great features!
Cryptography¶
Hash service¶
- hashing/verifying hashes
- special hasing strategies/algorithms
- random number generation
RSA wallet service¶
- cli commands to safe keys
- crypting/decrypting/verifying signatures
| [1] | The details about the PersistedUsernamePasswordProvider provider are explained below, in the section about Authentication mechanisms shipped with FLOW3. |
| [2] | If you don’t know any credentials, you’ll have to read the section about Account management |
| [3] | Well, it holds them in member variables, but lies itself in the security context, which is a class configured as scope session. |
| [4] | The specification can be downloaded from http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=ciq. The implementation of this specification resides in the “Party” package, which is part of the official FLOW3 distribution. |
| [5] | The AccountRepository provides a convenient find method called findActiveByAccountIdentifierAndAuthenticationProviderName() for this task. |
| [6] | As a convention you have to prefix at least your package’s namespace to avoid ambiguity. |

