1919)
2020
2121from pymongo .errors import DuplicateKeyError
22+ from pymongo .collation import Collation
2223
2324from .models import (
2425 UserCreate ,
@@ -65,6 +66,8 @@ def __init__(self, mdb, email, invites):
6566 self .invites = invites
6667 self .org_ops = None
6768
69+ self .email_collation = Collation ("en" , strength = 2 )
70+
6871 self .registration_enabled = is_bool (os .environ .get ("REGISTRATION_ENABLED" ))
6972
7073 # pylint: disable=attribute-defined-outside-init
@@ -78,6 +81,13 @@ async def init_index(self):
7881 """init lookup index"""
7982 await self .users .create_index ("id" , unique = True )
8083 await self .users .create_index ("email" , unique = True )
84+
85+ await self .users .create_index (
86+ "email" ,
87+ name = "case_insensitive_email_index" ,
88+ collation = self .email_collation ,
89+ )
90+
8191 # Expire failed logins object after one hour
8292 await self .failed_logins .create_index ("attempted" , expireAfterSeconds = 3600 )
8393
@@ -379,7 +389,9 @@ async def get_by_id(self, _id: uuid.UUID) -> Optional[User]:
379389
380390 async def get_by_email (self , email : str ) -> Optional [User ]:
381391 """get user by email"""
382- user = await self .users .find_one ({"email" : email })
392+ user = await self .users .find_one (
393+ {"email" : email }, collation = self .email_collation
394+ )
383395 if not user :
384396 return None
385397
@@ -535,7 +547,9 @@ async def _update_password(self, user: User, new_password: str) -> None:
535547
536548 async def reset_failed_logins (self , email : str ) -> None :
537549 """Reset consecutive failed login attempts by deleting FailedLogin object"""
538- await self .failed_logins .delete_one ({"email" : email })
550+ await self .failed_logins .delete_one (
551+ {"email" : email }, collation = self .email_collation
552+ )
539553
540554 async def inc_failed_logins (self , email : str ) -> None :
541555 """Inc consecutive failed login attempts for user by 1
@@ -552,11 +566,14 @@ async def inc_failed_logins(self, email: str) -> None:
552566 "$inc" : {"count" : 1 },
553567 },
554568 upsert = True ,
569+ collation = self .email_collation ,
555570 )
556571
557572 async def get_failed_logins_count (self , email : str ) -> int :
558573 """Get failed login attempts for user, falling back to 0"""
559- failed_login = await self .failed_logins .find_one ({"email" : email })
574+ failed_login = await self .failed_logins .find_one (
575+ {"email" : email }, collation = self .email_collation
576+ )
560577 if not failed_login :
561578 return 0
562579 return failed_login .get ("count" , 0 )
0 commit comments