Search code examples
djangosamldjango-authenticationhue

SAML authentication for HUE revoking admin access AFTER first login


I'm trying to set up SAML authentication for HUE deployed with AWS EMR, using Azure SSO as the IdP. I can actually get the handshake to work and the users are logging in to HUE, matching network login details to the usernames that are prepopulated in the HUE backend database. We create the users in HUE first and part of that setup includes setting some users with "is_superuser" to TRUE. The only attribute I explicitly look for to get from the IdP to HUE is the username/network-credential

The behaviour I'm trying to understand is that the first person to log into the HUE UI is getting authenticated via SAML and logging in, with the admin/superuser privileges intact. But anyone after that logging in who is set up as an admin is losing the flag to indicate an admin, i.e. logged in as a normal user. If I manually go in afterward and set the users to have admin access in HUE database and have the users log in again, the access will be granted to the admin permissions and the problem seems to disappear but I don't understand why every login after the first is removing these permissions?

I tried setting Django up in Debug mode to see if I can get any insights, but the only things I can find that could potentially explain this are:

  1. I found this line in the output of the runcpserver.log after the first login and not after any other ones, and when digging into the class there are definitely references to "is_superuser" in there.

backend INFO Augmenting users with class: <class 'desktop.auth.backend.DefaultUserAugmentor'>

However, I can't tell if this is SUPPOSED to be only called once. If it's supposed to be called every time, I've no idea how to force that from the from configuration available to me.

  1. The other thing I found in the logs that leads me towards suspicion is the following two lines I found, after the login attempts of every user EXCEPT the first:
mdstore      ERROR    Unsupported binding: urn:oasis:names:tc:SAML:2.0:bindings:SOAP (https://sts.windows.net/xxxxxxx-xxxx-xxxx-xxxxx-xxxxxxxxxxxx/)
mdstore      ERROR    Unsupported binding: urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST (https://sts.windows.net/xxxxxxx-xxxx-xxxx-xxxxx-xxxxxxxxxxxx/)

But I would have expected this to result in the user not getting access at all if there were issues with the bindings rather than allowing the user to login but altering the attributes of said user.

Would appreciate any thoughts or insights into this, as it's not an area I know particularly well


Solution

  • Finally found the issue after going through the code, and the issue I was hitting was here: https://github.com/cloudera/hue/blob/master/desktop/libs/libsaml/src/libsaml/backend.py#L91

            # If the user already exists, we shouldn't change its superuser
            # privileges. However, if there's a naming conflict with a non-external
            # user, we should do the safe thing and turn off superuser privs.
            existing_profile = get_profile(user)
            if existing_profile.creation_method == UserProfile.CreationMethod.EXTERNAL.name:
              is_super = user.is_superuser
    

    The if statement checks to see if the way the user was created matches an external source (i.e. LDAP) to ensure that there aren't duplicated entries in the Django database. Since all my users were prepopulated in the database, the first variable was set to "HUE" and the second one was set to "EXTERNAL" consistently, so the if statement was always false.

    This meant the user, even though I had set it as a superuser, wasn't inheriting this attribute when being created, UNLESS it was the first user as I was seeing because of this line of code: https://github.com/cloudera/hue/blob/master/desktop/libs/libsaml/src/libsaml/backend.py#L80

        if not UserProfile.objects.filter(creation_method=UserProfile.CreationMethod.EXTERNAL.name).exists():
          # If there are no LDAP users already in the system, the first one will
          # become a superuser
          is_super = True
    

    which was always executing because again, I wasn't using LDAP or anything, I was prepopulating the database in Django directly.

    So the fix for me will be removing the if statement in the first code block. I hope this saves someone the two weeks it took me to find :)