Эх сурвалжийг харах

HUE-1992 [core] set username to lowercase when using RemoteUserDjangoBackend

Abraham Elmahrek 11 жил өмнө
parent
commit
5c09057

+ 8 - 0
desktop/conf.dist/hue.ini

@@ -129,6 +129,14 @@
     # Defaults to HTTP_REMOTE_USER
     ## remote_user_header=HTTP_REMOTE_USER
 
+    # Ignore the case of usernames when searching for existing users.
+    # Only supported in remoteUserDjangoBackend.
+    ## ignore_username_case=false
+
+    # Ignore the case of usernames when searching for existing users to authenticate with.
+    # Only supported in remoteUserDjangoBackend.
+    ## force_username_lowercase=false
+
   # Configuration options for connecting to LDAP and Active Directory
   # -------------------------------------------------------------------
   [[ldap]]

+ 8 - 0
desktop/conf/pseudo-distributed.ini.tmpl

@@ -138,6 +138,14 @@
     # Defaults to HTTP_REMOTE_USER
     ## remote_user_header=HTTP_REMOTE_USER
 
+    # Ignore the case of usernames when searching for existing users.
+    # Only supported in remoteUserDjangoBackend.
+    ## ignore_username_case=false
+
+    # Ignore the case of usernames when searching for existing users to authenticate with.
+    # Only supported in remoteUserDjangoBackend.
+    ## force_username_lowercase=false
+
   # Configuration options for connecting to LDAP and Active Directory
   # -------------------------------------------------------------------
   [[ldap]]

+ 5 - 1
desktop/core/src/desktop/auth/backend.py

@@ -441,12 +441,16 @@ class RemoteUserDjangoBackend(django.contrib.auth.backends.RemoteUserBackend):
   """
   def authenticate(self, remote_user=None):
     username = self.clean_username(remote_user)
+    username = desktop.conf.AUTH.FORCE_USERNAME_LOWERCASE.get() and username.lower() or username
     is_super = False
     if User.objects.count() == 0:
       is_super = True
 
     try:
-      user = User.objects.get(username=username)
+      if desktop.conf.AUTH.IGNORE_USERNAME_CASE.get():
+        user = User.objects.get(username__iexact=username)
+      else:
+        user = User.objects.get(username=username)
     except User.DoesNotExist:
       user = find_or_create_user(username, None)
       if user is not None and user.is_active:

+ 93 - 0
desktop/core/src/desktop/auth/views_test.py

@@ -129,6 +129,99 @@ class TestLdapLogin(PseudoHdfsTestBase):
     # 'Could not create home directory.' won't show up because the messages are consumed before
 
 
+class TestRemoteUserLogin(object):
+  reset = []
+
+  def setUp(self):
+    self.backends = settings.AUTHENTICATION_BACKENDS
+    settings.AUTHENTICATION_BACKENDS = ('desktop.auth.backend.RemoteUserDjangoBackend',)
+    self.reset.append( conf.AUTH.BACKEND.set_for_testing('desktop.auth.backend.RemoteUserDjangoBackend') )
+    self.reset.append( conf.AUTH.REMOTE_USER_HEADER.set_for_testing('REMOTE_USER') )
+    self.c = Client()
+
+  def tearDown(self):
+    User.objects.all().delete()
+    settings.AUTHENTICATION_BACKENDS = self.backends
+    for finish in self.reset:
+      finish()
+
+  def test_normal(self):
+    response = self.c.get('/accounts/login/')
+    assert_equal(200, response.status_code, "Expected ok status.")
+    assert_false(response.context['first_login_ever'])
+
+    assert_equal(0, len(User.objects.all()))
+    response = self.c.post('/accounts/login/', {}, **{"REMOTE_USER": "foo3"})
+    assert_equal(200, response.status_code, "Expected ok status.")
+    assert_equal(1, len(User.objects.all()))
+    assert_equal('foo3', User.objects.all()[0].username)
+
+  def test_ignore_case(self):
+    self.reset.append( conf.AUTH.IGNORE_USERNAME_CASE.set_for_testing(True) )
+
+    response = self.c.get('/accounts/login/')
+    assert_equal(200, response.status_code, "Expected ok status.")
+    assert_false(response.context['first_login_ever'])
+
+    response = self.c.post('/accounts/login/', {}, **{"REMOTE_USER": "foo3"})
+    assert_equal(200, response.status_code, "Expected ok status.")
+    assert_equal(1, len(User.objects.all()))
+    assert_equal('foo3', User.objects.all()[0].username)
+
+    response = self.c.post('/accounts/login/', {}, **{"REMOTE_USER": "FOO3"})
+    assert_equal(200, response.status_code, "Expected ok status.")
+    assert_equal(1, len(User.objects.all()))
+    assert_equal('foo3', User.objects.all()[0].username)
+
+    response = self.c.post('/accounts/login/', {}, **{"REMOTE_USER": "FOO4"})
+    assert_equal(200, response.status_code, "Expected ok status.")
+    assert_equal(2, len(User.objects.all()))
+    assert_equal('FOO4', User.objects.all()[1].username)
+
+    response = self.c.post('/accounts/login/', {}, **{"REMOTE_USER": "foo4"})
+    assert_equal(200, response.status_code, "Expected ok status.")
+    assert_equal(2, len(User.objects.all()))
+    assert_equal('FOO4', User.objects.all()[1].username)
+
+  def test_force_lower_case(self):
+    self.reset.append( conf.AUTH.FORCE_USERNAME_LOWERCASE.set_for_testing(True) )
+
+    response = self.c.get('/accounts/login/')
+    assert_equal(200, response.status_code, "Expected ok status.")
+    assert_false(response.context['first_login_ever'])
+
+    response = self.c.post('/accounts/login/', {}, **{"REMOTE_USER": "foo3"})
+    assert_equal(200, response.status_code, "Expected ok status.")
+    assert_equal(1, len(User.objects.all()))
+    assert_equal('foo3', User.objects.all()[0].username)
+
+    response = self.c.post('/accounts/login/', {}, **{"REMOTE_USER": "FOO3"})
+    assert_equal(200, response.status_code, "Expected ok status.")
+    assert_equal(1, len(User.objects.all()))
+    assert_equal('foo3', User.objects.all()[0].username)
+
+  def test_ignore_case_and_force_lower_case(self):
+    response = self.c.post('/accounts/login/', {}, **{"REMOTE_USER": "FOO3"})
+    assert_equal(200, response.status_code, "Expected ok status.")
+    assert_equal(1, len(User.objects.all()))
+    assert_equal('FOO3', User.objects.all()[0].username)
+
+    self.reset.append( conf.AUTH.FORCE_USERNAME_LOWERCASE.set_for_testing(True) )
+    self.reset.append( conf.AUTH.IGNORE_USERNAME_CASE.set_for_testing(True) )
+
+    # Previously existing users should not be forced to lower case.
+    response = self.c.post('/accounts/login/', {}, **{"REMOTE_USER": "FOO3"})
+    assert_equal(200, response.status_code, "Expected ok status.")
+    assert_equal(1, len(User.objects.all()))
+    assert_equal('FOO3', User.objects.all()[0].username)
+
+    # New users should be forced to lowercase.
+    response = self.c.post('/accounts/login/', {}, **{"REMOTE_USER": "FOO4"})
+    assert_equal(200, response.status_code, "Expected ok status.")
+    assert_equal(2, len(User.objects.all()))
+    assert_equal('foo4', User.objects.all()[1].username)
+
+
 class TestLogin(object):
   def setUp(self):
     # Simulate first login ever

+ 9 - 1
desktop/core/src/desktop/conf.py

@@ -390,7 +390,15 @@ AUTH = ConfigSection(
                                "The HTTP header in the request is converted to a key by converting "
                                "all characters to uppercase, replacing any hyphens with underscores "
                                "and adding an HTTP_ prefix to the name. So, for example, if the header "
-                               "is called Remote-User that would be configured as HTTP_REMOTE_USER"))
+                               "is called Remote-User that would be configured as HTTP_REMOTE_USER")),
+    IGNORE_USERNAME_CASE = Config("ignore_username_case",
+                                  help=_("Ignore the case of usernames when searching for existing users in Hue."),
+                                  type=coerce_bool,
+                                  default=False),
+    FORCE_USERNAME_LOWERCASE = Config("force_username_lowercase",
+                                      help=_("Force usernames to lowercase when creating new users from LDAP."),
+                                      type=coerce_bool,
+                                      default=False)
 ))
 
 LDAP = ConfigSection(