From bbea3c3ca8adc1757418e95b71f183bf79bd7db3 Mon Sep 17 00:00:00 2001 From: Matthias Noback Date: Sun, 29 Jul 2012 19:52:44 +0200 Subject: [PATCH 1/7] Added rough version of Security Component Documentation, about firewall, firewall map, firewall listeners, authentication manager, password encoder factory, password encoders, user providers --- components/index.rst | 1 + components/map.rst.inc | 4 + components/security/authentication.rst | 198 +++++++++++++++++++++++++ components/security/firewall.rst | 112 ++++++++++++++ components/security/index.rst | 10 ++ components/security/introduction.rst | 30 ++++ 6 files changed, 355 insertions(+) create mode 100644 components/security/authentication.rst create mode 100644 components/security/firewall.rst create mode 100644 components/security/index.rst create mode 100644 components/security/introduction.rst diff --git a/components/index.rst b/components/index.rst index 55ac4c8d3a7..a572203ac97 100644 --- a/components/index.rst +++ b/components/index.rst @@ -17,6 +17,7 @@ The Components locale process routing/index + security/index serializer templating yaml diff --git a/components/map.rst.inc b/components/map.rst.inc index 7a45073e7eb..41a5b0212e6 100644 --- a/components/map.rst.inc +++ b/components/map.rst.inc @@ -65,6 +65,10 @@ * :doc:`/components/serializer` +* **Security** + + * :doc:`/components/security/index` + * **Templating** * :doc:`/components/templating` diff --git a/components/security/authentication.rst b/components/security/authentication.rst new file mode 100644 index 00000000000..d3240967187 --- /dev/null +++ b/components/security/authentication.rst @@ -0,0 +1,198 @@ +.. index:: + single: Security, Authentication Manager + +Authentication +============== + +When a request points to a secured area, and one of the listeners from the +firewall map is able to extract the user's credentials from the current +:class:`Symfony\\Component\\HttpFoundation\\Request` object, it should create +a token, containing these credentials. The next thing the listener should +do is ask the authentication manager to validate the given token, and return +an authenticated token when the supplied credentials were found to be valid. +The listener should then store the authenticated token in the security context: + +.. code-block:: php + + use Symfony\Component\Security\Http\Firewall\ListenerInterface; + use Symfony\Component\Security\Core\SecurityContextInterface; + use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; + use Symfony\Component\HttpKernel\Event\GetResponseEvent; + use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; + + class SomeAuthenticationListener implements ListenerInterface + { + /* @var SecurityContextInterface */ + private $securityContext; + + /* @var AuthenticationManagerInterface */ + private $authenticationManager; + + // string Uniquely identifies the secured area + private $providerKey; + + // ... + + public function handle(GetResponseEvent $event) + { + $request = $event->getRequest(); + + $unauthenticatedToken = new UsernamePasswordToken( + $username, + $password, + $this->providerKey); + + $authenticatedToken = $this + ->authenticationManager + ->authenticate($unauthenticatedToken); + + $this->securityContext->setToken($authenticatedToken); + } + } + +.. note:: + + A token can be of any class, as long as it implements + :class:`Symfony\\Component\\Security\\Core\\Authentication\\Token\\TokenInterface`. + +The authentication manager +-------------------------- + +The default authentication manager is an instance of :class:`Symfony\\Component\\Security\\Core\\Authentication\\AuthenticationProviderManager`: + +.. code-block:: php + + use Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager; + + $providers = array( + // instances of Symfony\Component\Security\Core\Authentication\AuthenticationProviderInterface + ); + + $authenticationManager = new AuthenticationProviderManager($providers); + + try { + $authenticatedToken = $authenticationManager + ->authenticate($unauthenticatedToken); + } catch (AuthenticationException $failed) { + // authentication failed + } + +The ``AuthenticationProviderManager``, when instantiated, receives several +authentication providers, each supporting a different type of token. + +.. note:: + + You may of course write your own authentication manager, it only has + to implement :class:`Symfony\\Component\\Security\\Core\\Authentication\\AuthenticationManagerInterface`. + +Authentication providers +------------------------ + +Each provider (since it implements +:class:`Symfony\\Component\\Security\\Core\\Authentication\\Provider\\AuthenticationProviderInterface`) +has a method ``supports()`` by which the ``AuthenticationProviderManager`` +can determine if it supports the given token. If this is the case, the +manager then calls the provider's method ``authenticate()``. This method +should return an authenticated token or throw an :class:`Symfony\\Component\\Security\\Core\\Exception\\AuthenticationException` +(or any other exception extending it). + +Authenticating users by their username and password +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +An authentication provider will attempt to authenticate a user based on +the credentials he provided. Usually these are a username and a password. +Most web applications store their user's username and a hash of the user's +password combined with a randomly generated salt. This means that the average +authentication would consist of fetching the salt and the hashed password +from the user data storage, hash the password the user has just provided +(e.g. using a login form) with the salt and compare both to determine if +the given password is valid. + +This functionality is offered by the :class:`Symfony\\Component\\Security\\Core\\Authentication\\Provider\\DaoAuthenticationProvider`. +It fetches the user's data from a ``UserProvider``, uses a ``PasswordEncoder`` +to create a hash of the password and returns an authenticated token if the +password was valid. + +.. code-block:: php + + use Symfony\Component\Security\Core\Authentication\Provider\DaoAuthenticationProvider; + use Symfony\Component\Security\Core\User\UserChecker; + use Symfony\Component\Security\Core\User\InMemoryUserProvider; + use Symfony\Component\Security\Core\Encoder\EncoderFactory; + + $userProvider = new InMemoryUserProvider( + array('admin' => array( + // password is "foo" + 'password' => '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg==', + 'roles' => array('ROLE_ADMIN'), + ), + ); + + // for some extra checks: is account enabled, locked, expired, etc.? + $userChecker = new UserChecker(); + + $encoderFactory = new EncoderFactory(/* ... encoders */); + + $provider = new DaoAuthenticationProvider( + $userProvider, + $userChecker, + 'secured_area', + $encoderFactory + ); + + $provider->authenticate($unauthenticatedToken); + +.. note:: + + The example above demonstrates the use of the "in-memory" user provider, + but you may use any user provider, as long as it implements + :class:`Symfony\\Component\\Security\\Core\\User\\UserProviderInterface`. + It is also possible to let multiple user providers try to find the user's + data, using the :class:`Symfony\\Component\\Security\\Core\\User\\ChainUserProvider`. + +The password encoder factory +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``DaoAuthenticationProvider`` uses an encoder factory to create a password +encoder for a given type of user. This allows you to use different encoding +strategies for different types of users. +The default :class:`Symfony\\Component\\Security\\Core\\Encoder\\EncoderFactory` +receives an array of encoders: + +.. code-block:: php + + use Symfony\Component\Security\Core\Encoder\EncoderFactory; + use Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder; + + $defaultEncoder = new MessageDigestPasswordEncoder('sha512', true, 5000); + $weakEncoder = new MessageDigestPasswordEncoder('md5', true, 1); + + $encoderFactory = new EncoderFactory(array( + 'Symfony\\Component\\Security\\Core\\User\\User' => $defaultEncoder, + 'Acme\\Entity\\LegacyUser' => $weakEncoder, + )); + +Password encoders +~~~~~~~~~~~~~~~~~ + +When the ``getEncoder()`` method of the password encoder factory is called +with the user object as its first argument, it will return an encoder of +type :class:`Symfony\\Component\\Security\\Core\\Encoder\\PasswordEncoderInterface` +which should be used to encode this user's password: + +.. code-block:: php + + $user = // ... fetch a user of type Acme\Entity\LegacyUser + + $encoder = $encoderFactory->getEncoder($user); + + // will return $weakEncoder (see above) + + $encodedPassword = $encoder->encodePassword($password, $user->getSalt()); + + // or check if the password is valid: + + $validPassword = $encoder->isPasswordValid( + $user->getPassword(), + $password, + $user->getSalt()); diff --git a/components/security/firewall.rst b/components/security/firewall.rst new file mode 100644 index 00000000000..55b821a1069 --- /dev/null +++ b/components/security/firewall.rst @@ -0,0 +1,112 @@ +.. index:: + single: Security, Firewall + +The Security Context +==================== + +Central to the Security Component is the security context, which is an instance +of :class:`Symfony\\Component\\Security\\Core\\SecurityContext`. When all +steps in the process of authenticating the user have been taken successfully, +the security context may be asked if the authenticated user has access +to a certain action or resource of the application. + +.. code-block:: php + + use Symfony\Component\Security\SecurityContext; + use Symfony\Component\Security\Core\Exception\AccessDeniedException; + + $context = new SecurityContext(); + + // authenticate the user... + + if (!$context->isGranted('ROLE_ADMIN')) { + throw new AccessDeniedException(); + } + +A firewall for HTTP requests +============================ + +Authenticating a user is done by the firewall. An application may have +multiple secured areas, so the firewall is configured using a map of these +secured areas. For each of these areas, the map contains a request matcher +and a collection of listeners. The request matcher gives the firewall the +ability to find out if the current request points to a secured area. +The listeners are then asked if the current request can be used to authenticate +the user. + +.. code-block:: php + + use Symfony\Component\Security\Http\FirewallMap; + use Symfony\Component\HttpFoundation\RequestMatcher; + use Symfony\Component\Security\Http\Firewall\ExceptionListener; + + $map = new FirewallMap(); + + $requestMatcher = new RequestMatcher('^/secured-area/'); + + $listeners = array( + // ... + ); + $exceptionListener = new ExceptionListener(/* ... */); + + $map->add($requestMatcher, $listeners, $exceptionListener); + +The firewall map will be given to the firewall as it's first argument, together +with the event dispatcher that is used by the :class:`Symfony\\Component\\HttpKernel\\HttpKernel`. + +.. code-block:: php + + use Symfony\Component\Security\Http\Firewall; + use Symfony\Component\HttpKernel\KernelEvents; + + // $dispatcher is the EventDispatcher used by the HttpKernel + + $firewall = new Firewall($map, $dispatcher); + + $dispatcher->register(KernelEvents::REQUEST, array($firewall, 'onKernelRequest'); + +The firewall is registered to listen to the ``kernel.request`` event that +will be dispatched by the ``HttpKernel`` at the beginning of each request +it processes. This way, the firewall may prevent the user from going any +further than allowed. + +Firewall listeners +------------------ + +When the firewall gets notified of the ``kernel.request`` event, it asks +the firewall map if the request matches any of the secured areas. If it +does, the corresponding listeners (who each implement +:class:`Symfony\\Component\\Security\\Http\\Firewall\\ListenerInterface`) +will be asked to handle the current request. This basically means: find +out if the current request contains any information by which the user might +be authenticated (for instance the Basic HTTP authentication listener checks +if the request has a header called "PHP_AUTH_USER"). + +Exception listener +------------------ + +If any of the listeners throws an :class:`Symfony\\Component\\Security\\Core\\Exception\\AuthenticationException` +(or any exception extending this exception), the exception listener that +was provided when adding secured areas to the firewall map will jump in. + +The exception listener determines what happens next, based on the arguments +it received when it was created. It may start the authentication procedure, +maybe ask the user to supply his credentials again (when he has only been +authenticated based on a "remember-me" cookie), or transform the exception +into an :class:`Symfony\\Component\\HttpKernel\\Exception\\AccessDeniedHttpException`, +which will eventually result in an "HTTP/1.1 401: Access Denied" response. + +Entry points +------------ + +When the user is not authenticated at all (i.e. when the security context +has no token yet), the firewall's entry point will be called to "start" +the authentication process. An entry point should implement +:class:`Symfony\\Component\\Security\\Http\\EntryPoint\\AuthenticationEntryPointInterface`, +which has only one method: ``start()``. This method receives the +current :class:`Symfony\\Component\\HttpFoundation\\Request` object and +the exception by which the exception listener was triggered. The method +should return a :class:`Symfony\\Component\\HttpFoundation\\Response` object, +for instance the page containing the login form, or in the case of Basic +HTTP authentication a response with a "WWW-Authenticate" header, which will +prompt the user to supply his username and password. diff --git a/components/security/index.rst b/components/security/index.rst new file mode 100644 index 00000000000..c735740690d --- /dev/null +++ b/components/security/index.rst @@ -0,0 +1,10 @@ +Security +======== + +.. toctree:: + :maxdepth: 2 + + introduction + firewall + authentication + authorization \ No newline at end of file diff --git a/components/security/introduction.rst b/components/security/introduction.rst new file mode 100644 index 00000000000..0bc0c7516b2 --- /dev/null +++ b/components/security/introduction.rst @@ -0,0 +1,30 @@ +.. index:: + single: Security + +The Security Component +====================== + +Introduction +------------ + +The Security Component provides a complete security system for your web +application. It ships with facilities for authenticating using HTTP basic +or digest authentication, interactive form login or X.509 certificate login, +but also allows you to implement your own authentication strategies. +Furthermore, the component provides ways to authorize authenticated users +based on their roles, and it contains an advanced ACL system. + +Installation +------------ + +You can install the component in many different ways: + +* Use the official Git repository (https://github.com/symfony/Security); +* Install it via PEAR ( `pear.symfony.com/Security`); +* Install it via Composer (`symfony/security` on Packagist). + +Sections +-------- + +* :doc:`/components/security/firewall` +* :doc:`/components/security/authentication` From 9b4da8e8a02637eca425b64de205cae955e7fab7 Mon Sep 17 00:00:00 2001 From: Matthias Noback Date: Mon, 17 Sep 2012 22:14:33 +0200 Subject: [PATCH 2/7] Added third chapter, on authorization --- components/security/authentication.rst | 47 ++++-- components/security/authorization.rst | 224 +++++++++++++++++++++++++ components/security/firewall.rst | 26 +-- components/security/introduction.rst | 1 + 4 files changed, 272 insertions(+), 26 deletions(-) create mode 100644 components/security/authorization.rst diff --git a/components/security/authentication.rst b/components/security/authentication.rst index d3240967187..0d72106fb95 100644 --- a/components/security/authentication.rst +++ b/components/security/authentication.rst @@ -12,7 +12,7 @@ do is ask the authentication manager to validate the given token, and return an authenticated token when the supplied credentials were found to be valid. The listener should then store the authenticated token in the security context: -.. code-block:: php +:: use Symfony\Component\Security\Http\Firewall\ListenerInterface; use Symfony\Component\Security\Core\SecurityContextInterface; @@ -22,10 +22,14 @@ The listener should then store the authenticated token in the security context: class SomeAuthenticationListener implements ListenerInterface { - /* @var SecurityContextInterface */ + /** + * @var SecurityContextInterface + */ private $securityContext; - /* @var AuthenticationManagerInterface */ + /** + * @var AuthenticationManagerInterface + */ private $authenticationManager; // string Uniquely identifies the secured area @@ -37,6 +41,9 @@ The listener should then store the authenticated token in the security context: { $request = $event->getRequest(); + $username = ...; + $password = ...; + $unauthenticatedToken = new UsernamePasswordToken( $username, $password, @@ -60,13 +67,12 @@ The authentication manager The default authentication manager is an instance of :class:`Symfony\\Component\\Security\\Core\\Authentication\\AuthenticationProviderManager`: -.. code-block:: php +:: use Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager; - $providers = array( - // instances of Symfony\Component\Security\Core\Authentication\AuthenticationProviderInterface - ); + // instances of Symfony\Component\Security\Core\Authentication\AuthenticationProviderInterface + $providers = array(...); $authenticationManager = new AuthenticationProviderManager($providers); @@ -85,6 +91,8 @@ authentication providers, each supporting a different type of token. You may of course write your own authentication manager, it only has to implement :class:`Symfony\\Component\\Security\\Core\\Authentication\\AuthenticationManagerInterface`. +.. _authentication_providers: + Authentication providers ------------------------ @@ -113,7 +121,7 @@ It fetches the user's data from a ``UserProvider``, uses a ``PasswordEncoder`` to create a hash of the password and returns an authenticated token if the password was valid. -.. code-block:: php +:: use Symfony\Component\Security\Core\Authentication\Provider\DaoAuthenticationProvider; use Symfony\Component\Security\Core\User\UserChecker; @@ -131,7 +139,8 @@ password was valid. // for some extra checks: is account enabled, locked, expired, etc.? $userChecker = new UserChecker(); - $encoderFactory = new EncoderFactory(/* ... encoders */); + // an array of password encoders (see below) + $encoderFactory = new EncoderFactory(...); $provider = new DaoAuthenticationProvider( $userProvider, @@ -159,7 +168,7 @@ strategies for different types of users. The default :class:`Symfony\\Component\\Security\\Core\\Encoder\\EncoderFactory` receives an array of encoders: -.. code-block:: php +:: use Symfony\Component\Security\Core\Encoder\EncoderFactory; use Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder; @@ -167,10 +176,17 @@ receives an array of encoders: $defaultEncoder = new MessageDigestPasswordEncoder('sha512', true, 5000); $weakEncoder = new MessageDigestPasswordEncoder('md5', true, 1); - $encoderFactory = new EncoderFactory(array( + $encoders = array( 'Symfony\\Component\\Security\\Core\\User\\User' => $defaultEncoder, 'Acme\\Entity\\LegacyUser' => $weakEncoder, - )); + ... + ); + + $encoderFactory = new EncoderFactory($encoders); + +Each encoder should implement :class:`Symfony\\Component\\Security\\Core\\Encoder\\PasswordEncoderInterface` +or be an array with a ``class`` and an ``arguments`` key, which allows the +encoder factory to construct the encoder only when it is needed. Password encoders ~~~~~~~~~~~~~~~~~ @@ -180,9 +196,10 @@ with the user object as its first argument, it will return an encoder of type :class:`Symfony\\Component\\Security\\Core\\Encoder\\PasswordEncoderInterface` which should be used to encode this user's password: -.. code-block:: php +:: - $user = // ... fetch a user of type Acme\Entity\LegacyUser + // fetch a user of type Acme\Entity\LegacyUser + $user = ... $encoder = $encoderFactory->getEncoder($user); @@ -190,7 +207,7 @@ which should be used to encode this user's password: $encodedPassword = $encoder->encodePassword($password, $user->getSalt()); - // or check if the password is valid: + // check if the password is valid: $validPassword = $encoder->isPasswordValid( $user->getPassword(), diff --git a/components/security/authorization.rst b/components/security/authorization.rst new file mode 100644 index 00000000000..8871c011eb8 --- /dev/null +++ b/components/security/authorization.rst @@ -0,0 +1,224 @@ +.. index:: + single: Security, Authorization + +Authorization +============= + +When any of the authentication providers (see :ref:`authentication_providers`) +has verified the still unauthenticated token, an authenticated token will +be returned. The authentication listener should set this token directly +in the :class:`Symfony\\Component\\Security\\Core\\SecurityContext` using its +``setToken()`` method. + +From then on, the user is authenticated, i.e. means identified. +Now, other parts of the application can use the token to decide whether +or not the user may request a certain URI, or modify a certain object. +This decision will be made by an instance of :class:`Symfony\\Component\\Security\\Core\\Authorization\\AccessDecisionManagerInterface`. + +An authorization decision will always be based on a few things: + +The current token + The token`s ``getRoles()`` method will be used to retrieve the roles + of the current user (e.g. "ROLE_SUPER_ADMIN") +A set of attributes + Each attribute stands for a certain right the user should have, e.g. + "ROLE_ADMIN" to make sure the user is an administrator. +An object (optional) + Any object on which to decide, e.g. the current :class:`Symfony\\Component\\HttpFoundation\\Request` + object. + +Access decision manager +----------------------- + +Since choosing whether or not a user is authorized to perform a certain +action can be a complicated process, the standard :class:`Symfony\\Component\\Security\\Core\\Authorization\\AccessDecisionManager` +itself depends on multiple voters, and makes a final verdict based on all +the votes (either positive, negative or neutral) it has received. It +recognizes several strategies: + +``affirmative`` (default) + Grant access as soon as any voter returns an affirmative response + +``consensus`` + Grant access if there are more voters granting access then there are denying + +``unanimous`` + Only grant access if none of the voters has denied access + +:: + + use Symfony\Component\Security\Core\Authorization\AccessDecisionManager; + + // instances of Symfony\Component\Security\Core\Authorization\Voter\VoterInterface + $voters = array(...); + + // one of "affirmative", "consensus", "unanimous" + $strategy = ...; + + // whether or not to grant access when all voters abstain + $allowIfAllAbstainDecisions = ...; + + // whether or not to grant access when there is no majority (applies only to the "consensus" strategy) + $allowIfEqualGrantedDeniedDecisions = ...; + + $accessDecisionManager = new AccessDecisionManager($voters, $strategy, + $allowIfAllAbstainDecisions, $allowIfEqualGrantedDeniedDecisions); + +Voters +------ + +Voters are instances +of :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface`, +which means they have to implement a few methods which allows the decision +manager to use them: + +``supportsAttribute($attribute)`` + Will be used to check if the voter knows how to handle the given attribute. +``supportsClass($class)`` + Will be used to check if the voter is able to grant or deny access for + an object of the given class. +``vote(TokenInterface $token, $object, array $attributes)`` + This method will do the actual voting and return a value equal to one + of the class constants of :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface`, + i.e. ``VoterInterface::ACCESS_GRANTED``, ``VoterInterface::ACCESS_DENIED`` + or ``VoterInterface::ACCESS_ABSTAIN``. + +The security component contains some standard voters which cover many use +cases: + +The :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\AuthenticatedVoter` +voter supports the attributes "IS_AUTHENTICATED_FULLY", "IS_AUTHENTICATED_REMEMBERED", +and "IS_AUTHENTICATED_ANONYMOUSLY" and grants access based on the current +level of authentication, i.e. is the user fully authenticated, or only based +on a "remember-me" cookie, or even authenticated anonymously? + +:: + + use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver; + + $anonymousClass = 'Symfony\Component\Security\Core\Authentication\Token\AnonymousToken'; + $rememberMeClass = 'Symfony\Component\Security\Core\Authentication\Token\RememberMeToken'; + + $trustResolver = new AuthenticationTrustResolver($anonymousClass, $rememberMeClass); + + $authenticatedVoter = new AuthenticatedVoter($trustResolver); + + // instance of Symfony\Component\Security\Core\Authentication\Token\TokenInterface + $token = ... + + // any object + $object = ... + + $vote = $authenticatedVoter->vote($token, $object, array('IS_AUTHENTICATED_FULLY'); + +The :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\RoleVoter` +supports attributes starting with "ROLE_" and grants access to the user +when the required "ROLE_*" attributes can all be found in the array of +roles returned by the token's ``getRoles()`` method. + +:: + + use Symfony\Component\Security\Core\Authorization\Voter\RoleVoter; + + $roleVoter = new RoleVoter('ROLE_'); + + $roleVoter->vote($token, $object, 'ROLE_ADMIN'); + +The :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\RoleHierarchyVoter` +extends :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\RoleVoter` +and provides some additional functionality: it knows how to handle a +hierarchy of roles. For instance, a "ROLE_SUPER_ADMIN" role may have subroles +"ROLE_ADMIN" and "ROLE_USER", so that when a certain object requires the +user to have the "ROLE_ADMIN" role, it grants access to users who in fact +have the "ROLE_ADMIN" role, but also to users having the "ROLE_SUPER_ADMIN" +role. + +:: + + use Symfony\Component\Security\Core\Authorization\Voter\RoleHierarchyVoter; + use Symfony\Component\Security\Core\Role\RoleHierarchy; + + $hierarchy = array( + 'ROLE_SUPER_ADMIN' => array('ROLE_ADMIN', 'ROLE_USER'), + ); + + $roleHierarchy = new RoleHierarchy($hierarchy); + + $roleHierarchyVoter = new RoleHierarchyVoter($roleHierarchy); + +.. note:: + + When you make your own voter, you may of course use its constructor + to inject any dependencies it needs to come to a decision. + +Roles +----- + +Roles are objects that give expression to a certain right the user has. +The only requirement is that they implement :class:`Symfony\\Component\\Security\\Core\\Role\\RoleInterface`, +which means they should also have a ``getRole()`` method that returns a +string representation of the role itself. The default :class:`Symfony\\Component\\Security\\Core\\Role\\Role` +simply returns its first constructor argument: + +:: + + use Symfony\Component\Security\Core\Role\Role; + + $role = new Role('ROLE_ADMIN'); + + // will echo 'ROLE_ADMIN' + echo $role->getRole(); + +.. note:: + + Most authentication tokens extend from :class:`Symfony\\Component\\Security\\Core\\Authentication\\Token\\AbstractToken`, + which means that the roles given to its constructor, will be + automatically converted from strings to these simple ``Role`` objects. + +Using the decision manager +-------------------------- + +The access listener +~~~~~~~~~~~~~~~~~~~ + +Normally, the access decision manager will already be asked to decide whether +or not the current user is entitled to make the current request. This is done +by the :class:`Symfony\\Component\\Security\\Http\\Firewall\\AccessListener`, +which is one of the firewall listeners (see :ref:`firewall_listeners`) that +will be triggered for each request matching the firewall map (see :ref:`firewall`). + +It uses an access map (which should be an instance of :class:`Symfony\\Component\\Security\\Http\\AccessMapInterface`) +which contains request matchers and a corresponding set of attributes that +are required for the current user to get access to the application. + +:: + + use Symfony\Component\Security\Http\AccessMap; + use Symfony\Component\HttpFoundation\RequestMatcher; + use Symfony\Component\Security\Http\Firewall\AccessListener; + + $accessMap = new AccessMap(); + $requestMatcher = new RequestMatcher('^/admin'); + $accessMap->add($requestMatcher, array('ROLE_ADMIN')); + + $accessListener = new AccessListener($securityContext, $accessDecisionManager, + $accessMap, $authenticationManager); + +Security context +~~~~~~~~~~~~~~~~ + +The access decision manager is also available to other parts of the application +by means of the ``isGranted($attribute)`` method of the :class:`Symfony\\Component\\Security\\Core\\SecurityContext`. +A call to this method will directly delegate the question to the access +decision manager. + +:: + + use Symfony\Component\Security\SecurityContext; + use Symfony\Component\Security\Core\Exception\AccessDeniedException; + + $securityContext = new SecurityContext(); + + if (!$securityContext->isGranted('ROLE_ADMIN')) { + throw new AccessDeniedException(); + } diff --git a/components/security/firewall.rst b/components/security/firewall.rst index 55b821a1069..250a5f1630e 100644 --- a/components/security/firewall.rst +++ b/components/security/firewall.rst @@ -5,24 +5,26 @@ The Security Context ==================== Central to the Security Component is the security context, which is an instance -of :class:`Symfony\\Component\\Security\\Core\\SecurityContext`. When all +of :class:`Symfony\\Component\\Security\\Core\\SecurityContextInterface`. When all steps in the process of authenticating the user have been taken successfully, the security context may be asked if the authenticated user has access to a certain action or resource of the application. -.. code-block:: php +:: use Symfony\Component\Security\SecurityContext; use Symfony\Component\Security\Core\Exception\AccessDeniedException; - $context = new SecurityContext(); + $securityContext = new SecurityContext(); - // authenticate the user... + // ... authenticate the user - if (!$context->isGranted('ROLE_ADMIN')) { + if (!$securityContext->isGranted('ROLE_ADMIN')) { throw new AccessDeniedException(); } +.. _firewall: + A firewall for HTTP requests ============================ @@ -34,7 +36,7 @@ ability to find out if the current request points to a secured area. The listeners are then asked if the current request can be used to authenticate the user. -.. code-block:: php +:: use Symfony\Component\Security\Http\FirewallMap; use Symfony\Component\HttpFoundation\RequestMatcher; @@ -44,17 +46,17 @@ the user. $requestMatcher = new RequestMatcher('^/secured-area/'); - $listeners = array( - // ... - ); - $exceptionListener = new ExceptionListener(/* ... */); + // instances of Symfony\Component\Security\Http\Firewall\ListenerInterface + $listeners = array(...); + + $exceptionListener = new ExceptionListener(...); $map->add($requestMatcher, $listeners, $exceptionListener); The firewall map will be given to the firewall as it's first argument, together with the event dispatcher that is used by the :class:`Symfony\\Component\\HttpKernel\\HttpKernel`. -.. code-block:: php +:: use Symfony\Component\Security\Http\Firewall; use Symfony\Component\HttpKernel\KernelEvents; @@ -70,6 +72,8 @@ will be dispatched by the ``HttpKernel`` at the beginning of each request it processes. This way, the firewall may prevent the user from going any further than allowed. +.. _firewall_listeners: + Firewall listeners ------------------ diff --git a/components/security/introduction.rst b/components/security/introduction.rst index 0bc0c7516b2..6dee91130ca 100644 --- a/components/security/introduction.rst +++ b/components/security/introduction.rst @@ -28,3 +28,4 @@ Sections * :doc:`/components/security/firewall` * :doc:`/components/security/authentication` +* :doc:`/components/security/authorization` From 108b888ef6e765f075b25e18fcbc41d4759bc73f Mon Sep 17 00:00:00 2001 From: Matthias Noback Date: Mon, 1 Oct 2012 10:06:59 +0200 Subject: [PATCH 3/7] Modifications based on feedback by @WouterJ and @stof --- components/security/authentication.rst | 48 ++++++++---------- components/security/authorization.rst | 67 ++++++++++++++++---------- components/security/firewall.rst | 45 ++++++++--------- 3 files changed, 85 insertions(+), 75 deletions(-) diff --git a/components/security/authentication.rst b/components/security/authentication.rst index 0d72106fb95..f6a5cb35e11 100644 --- a/components/security/authentication.rst +++ b/components/security/authentication.rst @@ -10,9 +10,7 @@ firewall map is able to extract the user's credentials from the current a token, containing these credentials. The next thing the listener should do is ask the authentication manager to validate the given token, and return an authenticated token when the supplied credentials were found to be valid. -The listener should then store the authenticated token in the security context: - -:: +The listener should then store the authenticated token in the security context:: use Symfony\Component\Security\Http\Firewall\ListenerInterface; use Symfony\Component\Security\Core\SecurityContextInterface; @@ -32,7 +30,9 @@ The listener should then store the authenticated token in the security context: */ private $authenticationManager; - // string Uniquely identifies the secured area + /** + * @var string Uniquely identifies the secured area + */ private $providerKey; // ... @@ -65,9 +65,7 @@ The listener should then store the authenticated token in the security context: The authentication manager -------------------------- -The default authentication manager is an instance of :class:`Symfony\\Component\\Security\\Core\\Authentication\\AuthenticationProviderManager`: - -:: +The default authentication manager is an instance of :class:`Symfony\\Component\\Security\\Core\\Authentication\\AuthenticationProviderManager`:: use Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager; @@ -98,9 +96,10 @@ Authentication providers Each provider (since it implements :class:`Symfony\\Component\\Security\\Core\\Authentication\\Provider\\AuthenticationProviderInterface`) -has a method ``supports()`` by which the ``AuthenticationProviderManager`` +has a method :method:`Symfony\\Component\\Security\\Core\\Authentication\\Provider\\AuthenticationProviderInterface::supports` +by which the ``AuthenticationProviderManager`` can determine if it supports the given token. If this is the case, the -manager then calls the provider's method ``authenticate()``. This method +manager then calls the provider's method :class:`Symfony\\Component\\Security\\Core\\Authentication\\Provider\\AuthenticationProviderInterface::authenticate`. This method should return an authenticated token or throw an :class:`Symfony\\Component\\Security\\Core\\Exception\\AuthenticationException` (or any other exception extending it). @@ -117,11 +116,10 @@ from the user data storage, hash the password the user has just provided the given password is valid. This functionality is offered by the :class:`Symfony\\Component\\Security\\Core\\Authentication\\Provider\\DaoAuthenticationProvider`. -It fetches the user's data from a ``UserProvider``, uses a ``PasswordEncoder`` +It fetches the user's data from a :class:`Symfony\\Component\\Security\\Core\\User\\UserProviderInterface``, +uses a :class:`Symfony\\Component\\Security\\Core\\Encoder\\PasswordEncoderInterface` to create a hash of the password and returns an authenticated token if the -password was valid. - -:: +password was valid:: use Symfony\Component\Security\Core\Authentication\Provider\DaoAuthenticationProvider; use Symfony\Component\Security\Core\User\UserChecker; @@ -162,13 +160,11 @@ password was valid. The password encoder factory ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The ``DaoAuthenticationProvider`` uses an encoder factory to create a password -encoder for a given type of user. This allows you to use different encoding -strategies for different types of users. -The default :class:`Symfony\\Component\\Security\\Core\\Encoder\\EncoderFactory` -receives an array of encoders: - -:: +The :class:`Symfony\\Component\\Security\\Core\\Authentication\\Provider\\DaoAuthenticationProvider` +uses an encoder factory to create a password encoder for a given type of +user. This allows you to use different encoding strategies for different +types of users. The default :class:`Symfony\\Component\\Security\\Core\\Encoder\\EncoderFactory` +receives an array of encoders:: use Symfony\Component\Security\Core\Encoder\EncoderFactory; use Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder; @@ -179,7 +175,7 @@ receives an array of encoders: $encoders = array( 'Symfony\\Component\\Security\\Core\\User\\User' => $defaultEncoder, 'Acme\\Entity\\LegacyUser' => $weakEncoder, - ... + ..., ); $encoderFactory = new EncoderFactory($encoders); @@ -191,12 +187,10 @@ encoder factory to construct the encoder only when it is needed. Password encoders ~~~~~~~~~~~~~~~~~ -When the ``getEncoder()`` method of the password encoder factory is called -with the user object as its first argument, it will return an encoder of -type :class:`Symfony\\Component\\Security\\Core\\Encoder\\PasswordEncoderInterface` -which should be used to encode this user's password: - -:: +When the :method:`Symfony\\Component\\Security\\Core\\Encoder\\EncoderFactory::getEncoder` +method of the password encoder factory is called with the user object as +its first argument, it will return an encoder of type :class:`Symfony\\Component\\Security\\Core\\Encoder\\PasswordEncoderInterface` +which should be used to encode this user's password:: // fetch a user of type Acme\Entity\LegacyUser $user = ... diff --git a/components/security/authorization.rst b/components/security/authorization.rst index 8871c011eb8..5a73c46409f 100644 --- a/components/security/authorization.rst +++ b/components/security/authorization.rst @@ -7,8 +7,9 @@ Authorization When any of the authentication providers (see :ref:`authentication_providers`) has verified the still unauthenticated token, an authenticated token will be returned. The authentication listener should set this token directly -in the :class:`Symfony\\Component\\Security\\Core\\SecurityContext` using its -``setToken()`` method. +in the :class:`Symfony\\Component\\Security\\Core\\SecurityContextInterface` +using its :method:`Symfony\\Component\\Security\\Core\\SecurityContextInterface::setToken` +method. From then on, the user is authenticated, i.e. means identified. Now, other parts of the application can use the token to decide whether @@ -18,19 +19,21 @@ This decision will be made by an instance of :class:`Symfony\\Component\\Securit An authorization decision will always be based on a few things: The current token - The token`s ``getRoles()`` method will be used to retrieve the roles - of the current user (e.g. "ROLE_SUPER_ADMIN") + For instance, the token's :method:`Symfony\\Component\\Security\\Core\\Authentication\\Token\\TokenInterface::getRoles` + method may be used to retrieve the roles of the current user (e.g. + "ROLE_SUPER_ADMIN"), or a decision may be based on the class of the token. A set of attributes Each attribute stands for a certain right the user should have, e.g. "ROLE_ADMIN" to make sure the user is an administrator. An object (optional) Any object on which to decide, e.g. the current :class:`Symfony\\Component\\HttpFoundation\\Request` - object. + object, or an object for which access control needs to be checked, like + an article or a comment object. Access decision manager ----------------------- -Since choosing whether or not a user is authorized to perform a certain +Since deciding whether or not a user is authorized to perform a certain action can be a complicated process, the standard :class:`Symfony\\Component\\Security\\Core\\Authorization\\AccessDecisionManager` itself depends on multiple voters, and makes a final verdict based on all the votes (either positive, negative or neutral) it has received. It @@ -45,7 +48,7 @@ recognizes several strategies: ``unanimous`` Only grant access if none of the voters has denied access -:: +.. code-block:: php use Symfony\Component\Security\Core\Authorization\AccessDecisionManager; @@ -61,8 +64,12 @@ recognizes several strategies: // whether or not to grant access when there is no majority (applies only to the "consensus" strategy) $allowIfEqualGrantedDeniedDecisions = ...; - $accessDecisionManager = new AccessDecisionManager($voters, $strategy, - $allowIfAllAbstainDecisions, $allowIfEqualGrantedDeniedDecisions); + $accessDecisionManager = new AccessDecisionManager( + $voters, + $strategy, + $allowIfAllAbstainDecisions, + $allowIfEqualGrantedDeniedDecisions + ); Voters ------ @@ -92,7 +99,7 @@ and "IS_AUTHENTICATED_ANONYMOUSLY" and grants access based on the current level of authentication, i.e. is the user fully authenticated, or only based on a "remember-me" cookie, or even authenticated anonymously? -:: +.. code-block:: php use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver; @@ -104,19 +111,20 @@ on a "remember-me" cookie, or even authenticated anonymously? $authenticatedVoter = new AuthenticatedVoter($trustResolver); // instance of Symfony\Component\Security\Core\Authentication\Token\TokenInterface - $token = ... + $token = ...; // any object - $object = ... + $object = ...; $vote = $authenticatedVoter->vote($token, $object, array('IS_AUTHENTICATED_FULLY'); The :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\RoleVoter` supports attributes starting with "ROLE_" and grants access to the user when the required "ROLE_*" attributes can all be found in the array of -roles returned by the token's ``getRoles()`` method. +roles returned by the token's :method:`Symfony\\Component\\Security\\Core\\Authentication\\Token\\TokenInterface::getRoles` +method. -:: +.. code-block:: php use Symfony\Component\Security\Core\Authorization\Voter\RoleVoter; @@ -133,7 +141,7 @@ user to have the "ROLE_ADMIN" role, it grants access to users who in fact have the "ROLE_ADMIN" role, but also to users having the "ROLE_SUPER_ADMIN" role. -:: +.. code-block:: php use Symfony\Component\Security\Core\Authorization\Voter\RoleHierarchyVoter; use Symfony\Component\Security\Core\Role\RoleHierarchy; @@ -156,11 +164,10 @@ Roles Roles are objects that give expression to a certain right the user has. The only requirement is that they implement :class:`Symfony\\Component\\Security\\Core\\Role\\RoleInterface`, -which means they should also have a ``getRole()`` method that returns a -string representation of the role itself. The default :class:`Symfony\\Component\\Security\\Core\\Role\\Role` -simply returns its first constructor argument: - -:: +which means they should also have a :method:`Symfony\\Component\\Security\\Core\\Role\\Role\\RoleInterface::getRole` +method that returns a string representation of the role itself. The default +:class:`Symfony\\Component\\Security\\Core\\Role\\Role` simply returns its +first constructor argument:: use Symfony\Component\Security\Core\Role\Role; @@ -191,7 +198,7 @@ It uses an access map (which should be an instance of :class:`Symfony\\Component which contains request matchers and a corresponding set of attributes that are required for the current user to get access to the application. -:: +.. code-block:: php use Symfony\Component\Security\Http\AccessMap; use Symfony\Component\HttpFoundation\RequestMatcher; @@ -201,23 +208,31 @@ are required for the current user to get access to the application. $requestMatcher = new RequestMatcher('^/admin'); $accessMap->add($requestMatcher, array('ROLE_ADMIN')); - $accessListener = new AccessListener($securityContext, $accessDecisionManager, - $accessMap, $authenticationManager); + $accessListener = new AccessListener( + $securityContext, + $accessDecisionManager, + $accessMap, + $authenticationManager + ); Security context ~~~~~~~~~~~~~~~~ The access decision manager is also available to other parts of the application -by means of the ``isGranted($attribute)`` method of the :class:`Symfony\\Component\\Security\\Core\\SecurityContext`. +by means of the :method:`Symfony\\Component\\Security\\Core\\SecurityContext::isGranted` +method of the :class:`Symfony\\Component\\Security\\Core\\SecurityContext`. A call to this method will directly delegate the question to the access decision manager. -:: +.. code-block:: php use Symfony\Component\Security\SecurityContext; use Symfony\Component\Security\Core\Exception\AccessDeniedException; - $securityContext = new SecurityContext(); + $securityContext = new SecurityContext( + $authenticationManager, + $accessDecisionManager + ); if (!$securityContext->isGranted('ROLE_ADMIN')) { throw new AccessDeniedException(); diff --git a/components/security/firewall.rst b/components/security/firewall.rst index 250a5f1630e..a89ae5e75aa 100644 --- a/components/security/firewall.rst +++ b/components/security/firewall.rst @@ -10,7 +10,7 @@ steps in the process of authenticating the user have been taken successfully, the security context may be asked if the authenticated user has access to a certain action or resource of the application. -:: +.. code-block:: php use Symfony\Component\Security\SecurityContext; use Symfony\Component\Security\Core\Exception\AccessDeniedException; @@ -36,7 +36,7 @@ ability to find out if the current request points to a secured area. The listeners are then asked if the current request can be used to authenticate the user. -:: +.. code-block:: php use Symfony\Component\Security\Http\FirewallMap; use Symfony\Component\HttpFoundation\RequestMatcher; @@ -56,12 +56,13 @@ the user. The firewall map will be given to the firewall as it's first argument, together with the event dispatcher that is used by the :class:`Symfony\\Component\\HttpKernel\\HttpKernel`. -:: +.. code-block:: php use Symfony\Component\Security\Http\Firewall; use Symfony\Component\HttpKernel\KernelEvents; - // $dispatcher is the EventDispatcher used by the HttpKernel + // the EventDispatcher used by the HttpKernel + $dispatcher = ...; $firewall = new Firewall($map, $dispatcher); @@ -78,27 +79,27 @@ Firewall listeners ------------------ When the firewall gets notified of the ``kernel.request`` event, it asks -the firewall map if the request matches any of the secured areas. If it -does, the corresponding listeners (who each implement -:class:`Symfony\\Component\\Security\\Http\\Firewall\\ListenerInterface`) -will be asked to handle the current request. This basically means: find -out if the current request contains any information by which the user might -be authenticated (for instance the Basic HTTP authentication listener checks -if the request has a header called "PHP_AUTH_USER"). +the firewall map if the request matches one of the secured areas. The first +secured area that matches the request, will return a set of corresponding +firewall listeners (which each implement :class:`Symfony\\Component\\Security\\Http\\Firewall\\ListenerInterface`). +These listeners will all be asked to handle the current request. This basically +means: find out if the current request contains any information by which +the user might be authenticated (for instance the Basic HTTP authentication +listener checks if the request has a header called "PHP_AUTH_USER"). Exception listener ------------------ -If any of the listeners throws an :class:`Symfony\\Component\\Security\\Core\\Exception\\AuthenticationException` -(or any exception extending this exception), the exception listener that -was provided when adding secured areas to the firewall map will jump in. +If any of the listeners throws an :class:`Symfony\\Component\\Security\\Core\\Exception\\AuthenticationException`, +the exception listener that was provided when adding secured areas to the +firewall map will jump in. The exception listener determines what happens next, based on the arguments it received when it was created. It may start the authentication procedure, maybe ask the user to supply his credentials again (when he has only been authenticated based on a "remember-me" cookie), or transform the exception into an :class:`Symfony\\Component\\HttpKernel\\Exception\\AccessDeniedHttpException`, -which will eventually result in an "HTTP/1.1 401: Access Denied" response. +which will eventually result in an "HTTP/1.1 403: Access Denied" response. Entry points ------------ @@ -107,10 +108,10 @@ When the user is not authenticated at all (i.e. when the security context has no token yet), the firewall's entry point will be called to "start" the authentication process. An entry point should implement :class:`Symfony\\Component\\Security\\Http\\EntryPoint\\AuthenticationEntryPointInterface`, -which has only one method: ``start()``. This method receives the -current :class:`Symfony\\Component\\HttpFoundation\\Request` object and -the exception by which the exception listener was triggered. The method -should return a :class:`Symfony\\Component\\HttpFoundation\\Response` object, -for instance the page containing the login form, or in the case of Basic -HTTP authentication a response with a "WWW-Authenticate" header, which will -prompt the user to supply his username and password. +which has only one method: :method:`Symfony\\Component\\Security\\Http\\EntryPoint\\AuthenticationEntryPointInterface::start`. +This method receives the current :class:`Symfony\\Component\\HttpFoundation\\Request` +object and the exception by which the exception listener was triggered. +The method should return a :class:`Symfony\\Component\\HttpFoundation\\Response` +object, for instance the page containing the login form, or in the case +of Basic HTTP authentication a response with a "WWW-Authenticate" header, +which will prompt the user to supply his username and password. From d289a22d259659ceedce716ac6a1754d4d167f7a Mon Sep 17 00:00:00 2001 From: Matthias Noback Date: Mon, 26 Nov 2012 19:56:47 +0100 Subject: [PATCH 4/7] Wrong method name 'register' should be 'addListener' --- components/security/firewall.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/security/firewall.rst b/components/security/firewall.rst index a89ae5e75aa..72f74a344c4 100644 --- a/components/security/firewall.rst +++ b/components/security/firewall.rst @@ -66,7 +66,7 @@ with the event dispatcher that is used by the :class:`Symfony\\Component\\HttpKe $firewall = new Firewall($map, $dispatcher); - $dispatcher->register(KernelEvents::REQUEST, array($firewall, 'onKernelRequest'); + $dispatcher->addListener(KernelEvents::REQUEST, array($firewall, 'onKernelRequest'); The firewall is registered to listen to the ``kernel.request`` event that will be dispatched by the ``HttpKernel`` at the beginning of each request From a2c224817020190694f647763d7d26d60624c829 Mon Sep 17 00:00:00 2001 From: Matthias Noback Date: Mon, 26 Nov 2012 20:20:08 +0100 Subject: [PATCH 5/7] Modified the index to contain all chapters --- components/map.rst.inc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/components/map.rst.inc b/components/map.rst.inc index 41a5b0212e6..1b31ab9054c 100644 --- a/components/map.rst.inc +++ b/components/map.rst.inc @@ -65,9 +65,12 @@ * :doc:`/components/serializer` -* **Security** +* :doc:`/components/security/index` - * :doc:`/components/security/index` + * :doc:`/components/security/introduction` + * :doc:`/components/security/firewall` + * :doc:`/components/security/authentication` + * :doc:`/components/security/authorization` * **Templating** From beafa110528758a282c65db8dbe1675c010c6dd5 Mon Sep 17 00:00:00 2001 From: Matthias Noback Date: Mon, 26 Nov 2012 21:29:37 +0100 Subject: [PATCH 6/7] Style modifications based on feedback from @WouterJ --- components/security/authentication.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/components/security/authentication.rst b/components/security/authentication.rst index f6a5cb35e11..bca9a8b1ddb 100644 --- a/components/security/authentication.rst +++ b/components/security/authentication.rst @@ -47,7 +47,8 @@ The listener should then store the authenticated token in the security context:: $unauthenticatedToken = new UsernamePasswordToken( $username, $password, - $this->providerKey); + $this->providerKey + ); $authenticatedToken = $this ->authenticationManager @@ -65,7 +66,8 @@ The listener should then store the authenticated token in the security context:: The authentication manager -------------------------- -The default authentication manager is an instance of :class:`Symfony\\Component\\Security\\Core\\Authentication\\AuthenticationProviderManager`:: +The default authentication manager is an instance of +:class:`Symfony\\Component\\Security\\Core\\Authentication\\AuthenticationProviderManager`:: use Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager; From 57c306c6283257d62947bc32cfed81b71b97322e Mon Sep 17 00:00:00 2001 From: Drak Date: Tue, 25 Dec 2012 21:22:55 +0000 Subject: [PATCH 7/7] Added some notes about Windows and colours. --- components/console/introduction.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/components/console/introduction.rst b/components/console/introduction.rst index 47313027092..bd5a2e601c3 100755 --- a/components/console/introduction.rst +++ b/components/console/introduction.rst @@ -20,6 +20,15 @@ You can install the component in many different ways: * Use the official Git repository (https://github.com/symfony/Console); * :doc:`Install it via Composer` (``symfony/console`` on `Packagist`_). +.. note:: + + Windows does not support ANSI colors by default so the Console Component detects and + disables colors where Windows does not have support. However, if Windows is not + configured with an ANSI driver and your console commands invoke other scripts which + emit ANSI color sequences, they will be shown as raw escape characters. + + To enable ANSI colour support for Windows, please install `ANSICON_`. + Creating a basic Command ------------------------ @@ -452,3 +461,4 @@ Learn More! * :doc:`/components/console/single_command_tool` .. _Packagist: https://packagist.org/packages/symfony/console +.. _ANSICON: http://adoxa.3eeweb.com/ansicon/