Some custom Capifony tasks

Here are some custom made tasks for Capistrano, which might be helpful in the Symfony2 context. First, some cache clearing tasks, that don’t clear the whole cache but only translations or the Twig template cache.

namespace :foo do

  # Clear only translation cache
  task :clear_translation_cache do
      now = Time.now
      timestamp = now.strftime("%Y%m%d%H%M%S")
      run "[ -d #{latest_release}/app/cache/prod/translations ] && mv #{latest_release}/app/cache/prod/translations #{latest_release}/app/cache/prod/translations_#{timestamp} || echo 'no translation dir'"
  end

  # Clear only twig cache
  task :clear_twig_cache do
      now = Time.now
      timestamp = now.strftime("%Y%m%d%H%M%S")
      run "[ -d #{latest_release}/app/cache/prod/twig ] && mv #{latest_release}/app/cache/prod/twig #{latest_release}/app/cache/prod/twig_#{timestamp} || echo 'no translation dir'"
  end

end

The folder is accually not removed but renamed to not interfere with current processes. The folder is then automatically rebuilt by the framework.

Symfony2 has that great “assets version” feature, which adds a parameter to the URL of all assets. Then by changing the value, you can make sure that everyone has to load the latest version from the server. If you want to update the asset version automatically on every deploy, you might use the following setup:

Add a file app/config/assets_version.yml to your project containing:

parameters:
    assets_version: AsSeTvErSiOn

Add that file to the import section of your config.yml and use the parameter %assets_version% in the configuration.

Call that Capistrano task in your deployment process. It replaces the assets version with a value generated from the release name. It is the hexadecimal representation of the YYMMDDHHII timestamp.

namespace :foo do

  # Update asset version
  task :update_version, :roles => :web, :except => { :no_release => true } do
    capifony_pretty_print " --> Update assets version"
    file_path = "#{release_path}/app/config/assets_version.yml"
    assets_version = release_name[2, 10] # Extract YYMMDDHHII only
    assets_version = assets_version.to_i.to_s(16) # Convert to int, convert to hex
    capifony_pretty_print "     Assets version is #{assets_version}"
    run "echo 'parameters:\n    assets_version: #{assets_version}' > #{file_path}"
  end

end

PHPUnit: Remove non-deterministic dependencies

Unit testing is great! But sometimes there are situations where it can become really tough to write proper tests for your code. One of these situations is when your code doesn’t work totally predictable, when it has some kind of randomness in it, that is intended. Then you usually have one of those functions in your code:

  • tempnam(), uniqid()
  • rand(), mt_rand(), shuffle() or any other randomized function
  • time(), date(), new \DateTime() or anything that has to do with the current time

The list isn’t complete, just wanted to mention the most common ones. Those functions are bad for testing, because tests have to be repeatable and those ones will most likely produce a different result on every call.

So how to get rid of the randomness and create predictable and therefore repeatable unit test?
Read more

New features in my two-factor authentication bundle

In the last days SchebTwoFactorBundle has received some major updates. The current version is v0.3.0. I want to give you a brief overview of what has changed and how to use those features.

The biggest change so far was the refactoring of the authentication layer. I’ve removed a bunch of duplicate code and implemented an abstract interface for the two-factor authentication, which can be extended with any kind of authentication method. This enables the users of the bundle to implement their own authentication methods quite easily. Take a look at the documentation how it works.

Besides this I’ve added a “trusted computer” feature, that has been suggested by a contributor. An optional checkbox is shown in the authentication form, which makes it possible to flag your machine as “trusted”. Then the whole two-factor process will be skipped once you’ve completed the authentication process. The feature supports multiple computers and multiple users on the same machine.

I hope those features are useful and they help to secure your projects with an two-factor mechanism that’s easy to implement.

Say Hello to SchebTwoFactorBundle

I’ve recently done some posts about how to integrate two-factor authentication and Google Authenticator into Symfony2. I thought to myself “Why not make a bundle from it?” and that’s what i did: Say hello to SchebTwoFactorBundle. Now it’s dead simple to add two-factor authentication to your own website. Add the bundle via Composer and enable the authentication method you want.

Currently it supports both the email and the Google Authenticator method I’ve blogged about. It already has some features for customization, but I’m thinking about making it more flexible. Maybe some generic two-factor implementation, which allows you to plug in different kinds of two-factor modules.

 

Google Authenticator in Symfony

If you want to add two-factor (2fa) authentication with Google Authenticator to your project, please use the scheb/2fa bundle for Symfony.
The approach in this blog post is no longer valid and potentially harmful to your application’s security. So don’t do it. Use the bundle instead.


This is the follow up to previous post about two-factor authentication in Symfony2. As promised I also want to show you how to integrate Google Authenticator into your project. If you haven’t read my first post, I’d suggest doing it now, because it explains the principle more in detail. The following example code is widely identical to SonataUserBundle‘s integration.

To get started, you’ll have to install the Sonata Google Authenticator package. If you’re using composer (I guess so), you can simply execute:

php composer.phar require sonata-project/google-authenticator dev-master

Read more

Two-Factor Authentication (2fa) in Symfony

If you want to add two-factor (2fa) authentication to your project, please use the scheb/2fa bundle for Symfony.
The approach in this blog post is no longer valid and potentially harmful to your application’s security. So don’t do it. Use the bundle instead.


For a project of mine I wanted to have some extra security because it contains critical features, only authorized people should have access to in any case. So I did some research if it’s possible to implement two-factor authentication in Symfony2. Sadly I didn’t find any good how-tos about that topic. Then I’ve found out that SonataUserBundle has Google Authenticator as an optional feature, so I did some reverse enginering to figure out how they did it.

This is how you implement two-factor authentication into Symfony2’s security layer. The following example will send out a random code to the user’s email address. I will do another post for Google Authenticator soon.

Read more

Password constraints in FOSUserBundle

I’ve recently tried to modify the password constraints of FOSUserBundle. To my surprise I’ve discovered that this is a little bit tricky.

Before you start I would suggest taking a look at validation.xml in the config directory of FOSUserBundle There you’ll find all the pre-defined constraints. As you can see, there is a class named FOS\UserBundle\Form\Model\ChangePassword. This is the data class, which is used in the change password form instead of the actual entity. So you have to change the password constraints on the User as well as on the ChangePassword class.

I have a bundle, which is extending FOSUserBundle, so I thought it is straight forward: Create a validation.xml which is overwriting the original one and put my own constraints in there. Unfortunatley that doesn’t work, instead my own constraints will simply be added to the default ones. So how to get rid of them? The trick is to define your own validation group.

This is how my configuration for the ChangePassword class looks like. The same goes for the User class.


    
        
            
            
        
        
        
        
            
            
            
        
    

By default FOSUserBundle is using the ChangePassword group to validate the change password form. With some extra lines in config.yml you can tell it to use a different one:

fos_user:
    change_password:
        form:
            validation_groups: [MyChangePassword, Default]

Now FOSUserBundle is using the MyChangePassword validation group for validation. This is also working for other forms like registration or the user profile. Take a look at the class FOS\UserBundle\DependencyInjection\Configuration and search for validation_groups to find out more about the configuration.

Default Values for Parameters

Sometimes it makes sense to have a default value for parameters. This can be done by creating a new file parameters_default.yml (can be any name you like) and adding it to config.yml just before parameters.yml.

imports:
    - { resource: parameters_default.yml }
    - { resource: parameters.yml }
parameters:
    locale: en
    secret: abcdefg1234567890

Now you can overwrite the default values in parameters.yml on demand but you’re not longer forced to define those parameters.

parameters:
    locale: de

Using Parameters in Routes

You can use parameters from parameters.yml in routes. There are serveral use-cases when this may be useful.

Let’s say you want to localize your routes but you want to store the list of available locales only once. Then you could define a parameter in config.yml:

parameters:
    allowed_locales: en|de|fr

Now you can use the parameter in your routing.yml:

some_bundle:
    resource: "@MyBundle/Controller/"
    prefix:   /{_locale}
    type:     annotation
    requirements:
        _locale: %allowed_locales%

Another use-case is routes based on a hostname. Instead of writing the hostname directly into routing.yml you could use a parameter, as seen in the official documentation.

As fas as I know parameters only work for routes defined in YAML, XML or PHP. If it also works for annotation routes, please let me know.