1

By default, Symfony2 matches usernames case-sensitively. I want users to be able to enter johnsmith, JohnSmith, johnSMITH, or any variant of that, and have them all register as johnsmith. How do I do this?

I though the easiest way would be to always convert each username to lower-case before comparing them. This is easy to do on the one side (just throw a lower() into the SQL statement), but how do I do that for what the user types in in the login form? Since Symfony automatically takes care of the login_check route handling, I can't figure it out.

Better yet, is there some option I can set to enable case-insensitivity instead?

user5670895
  • 1,488
  • 17
  • 29

5 Answers5

2

Just write correct loadUserByUsername function in UserRepository. It must not be case sensitive:

public function loadUserByUsername($username)
{
    $user = $this->createQueryBuilder('u')
        ->where('LOWER(u.email) = :username')
        ->setParameter('username', strtolower($username))
        ->getQuery()
        ->getOneOrNullResult();

    if ($user){
       return $user;
    }

    throw new UsernameNotFoundException('Unable to find user "' . $username . '"');
}
1

You already fixed it, but I will explain another solution for users with similar problems:

  1. You have to implement your own Provider this way: http://symfony.com/doc/current/cookbook/security/entity_provider.html#authenticating-someone-with-a-custom-entity-provider
  2. Use the following query instead in method loadUserByUsername:

    $user = $this->findOneBy(array('username' => strtolower($username)));

This worked for me. (Also in Doctrine Mongo ODM)

Xover
  • 588
  • 5
  • 10
0

Did you try to convert the input into lowercase using CSS ? There are actually ways to control data input before it is handed to the login_check controller, but if you want a quick fix :

p {
    text-transform: lowercase;
}
0

In your setUsername you could just have the text changed to lowercase like..

public function setUsername($username)
{
    $this->username = mb_strtolower($username);

    return $this;
}

For reference, FOSUserBundle handles this by "canonicalizing" the username (to usernameCanonical) and email address (to canonicalEmail) in the updateUser call (see Doctrine\UserManager that calls the Canonicalizer) which it then uses for searches.

qooplmao
  • 17,622
  • 2
  • 44
  • 69
  • That's not what I'm asking. That will make it so that usernames are always lower-case--which they are anyway (users do not create their usernames; they are created by the company support person). And I am not using that bundle. – user5670895 Jul 27 '15 at 21:31
  • Sorry if I sound rude, really. I don't mean to be. – user5670895 Jul 27 '15 at 21:40
  • It's not a problem. I clearly didn't read the whole question. What DB are you using? As far as I know MySQL searches are case-insensitive, although that wouldn't matter to you if you were using something else. – qooplmao Jul 28 '15 at 05:15
  • I am using Oracle. Though in any case, it is easy enough to always retrieve the lower-cased version using `lower(USERNAME)`. It's what the user types in that I can't figure out how to access. – user5670895 Jul 28 '15 at 13:46
  • If you are using the `entity` security provider you could [Use a Custom Query to Load the User](http://symfony.com/doc/current/cookbook/security/entity_provider.html#using-a-custom-query-to-load-the-user) and lower the case of the username in that before running the query. – qooplmao Jul 28 '15 at 14:29
  • I am not using the `entity` security provider. I am not using an Object Relational Mapper of any sort. I am using raw SQL (we have to build this on top of several existing poorly-designed databases with hundreds of tables each, plus simultaneously fetch and process thousands of records, something not possible with an ORM--so we decided to stick to using raw SQL). In my `UserProvider` I have already done exactly that. It's the **user input** at the login screen that I can't figure out how to lower the case of. – user5670895 Jul 28 '15 at 14:36
  • Just had a head-smack moment... all I needed to do was put a second `lower()` around the relevant part of the `where` clause. – user5670895 Jul 28 '15 at 14:41
  • Glad to have almost helped. :D – qooplmao Jul 28 '15 at 14:59
0

I feel like an idiot. All I had to do was add another lower() around the where clause in my SQL statement. Like this:

select lower(USERNAME) as USERNAME, PASSWORD, ROLES, ALL_CUSTOMER_ACCESS
from COMPANYNAME_USERS
where lower(USERNAME) = lower(:username)
user5670895
  • 1,488
  • 17
  • 29