Banno / getsentry-ldap-auth

A Sentry extension to add an LDAP server as an authention source.
Apache License 2.0
163 stars 54 forks source link

Group role mapping #38

Open reynico opened 5 years ago

reynico commented 5 years ago

Hi! I have two groups:

# sentry_admin, groups, site.io
dn: cn=sentry_admin,ou=groups,dc=site,dc=io
objectClass: groupOfNames
cn: sentry_admin_user
member: uid=sentry_admin_user,ou=people,dc=site,dc=io
# sentry_owner, groups, site.io
dn: cn=sentry_owner,ou=groups,dc=site,dc=io
objectClass: groupOfNames
cn: sentry_owner_user
member: uid=sentry_owner_user,ou=people,dc=site,dc=io

My current ldap configuration is as follows

AUTH_LDAP_USER_SEARCH = LDAPSearch(
    "ou=people,dc=site,dc=io", 
    ldap.SCOPE_SUBTREE, 
    "(uid=%(user)s)"
)

AUTH_LDAP_GROUP_SEARCH = LDAPSearch(
    "ou=groups,dc=site,dc=io", ldap.SCOPE_SUBTREE, "(objectClass=groupOfNames)"
)

AUTH_LDAP_SENTRY_USERNAME_FIELD = 'uid'
AUTH_LDAP_GROUP_TYPE = GroupOfUniqueNamesType()
AUTH_LDAP_REQUIRE_GROUP = None
AUTH_LDAP_DENY_GROUP = None

AUTH_LDAP_USER_ATTR_MAP = {
    'name': 'cn',
    'email': 'mail'
}

AUTH_LDAP_SENTRY_GROUP_ROLE_MAPPING = {
    'owner': ['cn=sentry_owner,ou=groups,dc=site,dc=io'],
    'admin': ['cn=sentry_admin,ou=groups,dc=site,dc=io']
}

AUTH_LDAP_FIND_GROUP_PERMS   = False
AUTH_LDAP_CACHE_GROUPS = True
AUTH_LDAP_GROUP_CACHE_TIMEOUT = 3600

AUTH_LDAP_DEFAULT_SENTRY_ORGANIZATION = u'Sentry'
# AUTH_LDAP_SENTRY_ORGANIZATION_ROLE_TYPE = 'member'
AUTH_LDAP_SENTRY_ORGANIZATION_GLOBAL_ACCESS = True
AUTH_LDAP_SENTRY_SUBSCRIBE_BY_DEFAULT = True

User login works perfect, but always as "Member". Is there any way to approach a user management by its group? Not really sure if I'm using

AUTH_LDAP_SENTRY_GROUP_ROLE_MAPPING = {
    'owner': ['cn=sentry_owner,ou=groups,dc=site,dc=io'],
    'admin': ['cn=sentry_admin,ou=groups,dc=site,dc=io']
}

correctly.

anitabee commented 5 years ago

@reynico which version of package do you use? I had the same issue using latest published version (2.7) but then I realised that this version doesn't support functionality which you are mentioning above... Not sure if that works for you, but installing directly from master solved the problem for me.

I have similar setup as you do, only difference is that in AUTH_LDAP_SENTRY_GROUP_ROLE_MAPPING I don't specify full path of group but only value of common name e.g.:

AUTH_LDAP_SENTRY_GROUP_ROLE_MAPPING = {
    'owner': ['sentry_owner'],
    'admin': ['sentry_admin'],
    ...
}
reynico commented 5 years ago

@anitabee hi! I'm using the latest one as you do. I'll try with the master branch

ntimo commented 5 years ago

I am also trying to get it two work, but for some reason the user that is added to the admin group does not have the admin permissions. I already tried to delete the user multiple times but it does not work.

anitabee commented 5 years ago

@ntimo you mean user that is in LDAP admin group does not get authenticated as admin in Sentry? If you go to <your_host>/settings/sentry/members/ how do you see your user listed as member or? Referring to my previous comment, I see that changes from master which support role mapping based on LDAP group are still not published so if you are running on 2.7 version of this package and using AUTH_LDAP_SENTRY_GROUP_ROLE_MAPPING setting to detect admin group it won't work.

ntimo commented 5 years ago

@anitabee I am actually building the plugin my self using GitLab CI from the latest master version from GitHub. When I go the <my_host>/manage/users/<id>/ the user does not have the Administrator checkbox checked, and when loggin in with said user the user also can't change organisation settings.

And when I go to https://<my_host>/settings/<my_org>/members/<id>/ the member only has the role member and not admin.

This is my sentry config:

import ldap
from django_auth_ldap.config import LDAPSearch, GroupOfUniqueNamesType

SENTRY_MANAGED_USER_FIELDS = ('email', 'first_name', 'last_name', 'password', )

AUTH_LDAP_USER_SEARCH = LDAPSearch(
    'OU=Users,DC=testdomain,DC=firm',
    ldap.SCOPE_SUBTREE,
    '(&(userPrincipalName=%(user)s)(memberOf:1.2.840.113556.1.4.1941:=CN=sentry-users,OU=Sentry,OU=Security,DC=testdomain,DC=firm))',
)

AUTH_LDAP_GROUP_SEARCH = LDAPSearch(
    'OU=Sentry,OU=Security,DC=testdomain,DC=firm',
    ldap.SCOPE_SUBTREE,
    '(objectClass=groupOfNames)'
)

AUTH_LDAP_GROUP_TYPE = GroupOfUniqueNamesType()
AUTH_LDAP_REQUIRE_GROUP = None
AUTH_LDAP_DENY_GROUP = None

AUTH_LDAP_USER_ATTR_MAP = {
    'name': 'cn',
    'email': 'mail'
}

AUTH_LDAP_FIND_GROUP_PERMS = False
AUTH_LDAP_CACHE_GROUPS = True
AUTH_LDAP_GROUP_CACHE_TIMEOUT = 3600

AUTH_LDAP_DEFAULT_SENTRY_ORGANIZATION = u'SentryOrg'
AUTH_LDAP_SENTRY_ORGANIZATION_ROLE_TYPE = 'member'
AUTH_LDAP_SENTRY_GROUP_ROLE_MAPPING = {
    'owner': ['sentry-owners'],
    'admin': ['sentry-admins'],
    'member': ['sentry-developers']
}

AUTH_LDAP_SENTRY_ORGANIZATION_GLOBAL_ACCESS = True
AUTH_LDAP_SENTRY_SUBSCRIBE_BY_DEFAULT = False
AUTH_LDAP_SENTRY_USERNAME_FIELD = 'userPrincipalName'

AUTHENTICATION_BACKENDS = AUTHENTICATION_BACKENDS + (
    'sentry_ldap_auth.backend.SentryLdapBackend',
)
anitabee commented 5 years ago

I see, one thing I noticed though, when searching for groups you use groupOfNames:

AUTH_LDAP_GROUP_SEARCH = LDAPSearch(
    'OU=Sentry,OU=Security,DC=testdomain,DC=firm',
    ldap.SCOPE_SUBTREE,
    '(objectClass=groupOfNames)'
)

But you set AUTH_LDAP_GROUP_TYPE to GroupOfUniqueNamesType() if your group objectClass in LDAP is groupOfNames I think you need to use: AUTH_LDAP_GROUP_TYPE = GroupOfNamesType()

ntimo commented 5 years ago

@anitabee When I set AUTH_LDAP_GROUP_TYPE = GroupOfNamesType() I get the following error in the log when starting sentry !! Configuration error: NameError: name 'GroupOfNamesType' is not defined

anitabee commented 5 years ago

Sounds like import error? Did you imported it from django_auth_ldap.config import LDAPSearch, GroupOfNamesType?

ntimo commented 5 years ago

I indeed used the wrong import. Now Sentry starts properly. But this still does not fix the issue that the user is still just a Member of the org and not the Admin of the Org.

anitabee commented 5 years ago

Huh I'm out of ideas :(, but I would validate that group queries are proper towards LDAP. Did you tried to run Sentry in debug mode? If so what are results from group query towards LDAP? For me I get something like this (when user first time logins):

08:28:21 [DEBUG] django_auth_ldap: search_s('ou=Users,dc=example,dc=org', 2, '(cn=%(user)s)') returned 1 objects: cn=john,ou=users,dc=example,dc=org
08:28:21 [DEBUG] django_auth_ldap: search_s('ou=Users,dc=example,dc=org', 2, '(cn=%(user)s)') returned 1 objects: cn=john,ou=users,dc=example,dc=org
08:28:21 [DEBUG] django_auth_ldap: search_s('ou=Groups,dc=example,dc=org', 2, '(&(objectClass=groupOfUniqueNames)(uniqueMember=cn=john,ou=users,dc=example,dc=org))') returned 1 objects: cn=sentry_admin,ou=groups,dc=example,dc=org
08:28:21 [DEBUG] django_auth_ldap: search_s('ou=Groups,dc=example,dc=org', 2, '(&(objectClass=groupOfUniqueNames)(uniqueMember=cn=john,ou=users,dc=example,dc=org))') returned 1 objects: cn=sentry_admin,ou=groups,dc=example,dc=org
08:28:21 [DEBUG] django_auth_ldap: Created Django user john
08:28:21 [DEBUG] django_auth_ldap: Created Django user john
08:28:21 [DEBUG] django_auth_ldap: Populating Django user john
08:28:21 [DEBUG] django_auth_ldap: Populating Django user john

Not sure if it helps you, but I can share development setup that works for me. This is my dev Groups LDIF:

✘  ~/ve/docker-sentry-ldap/9.1   master ●  ldapsearch -H ldap://localhost:389 -x -D "cn=my_super_powerfull_user,dc=example,dc=org" -b "ou=Groups,dc=example,dc=org" -W
# extended LDIF
#
# LDAPv3
# base <ou=Groups,dc=example,dc=org> with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#

# Groups, example.org
dn: ou=Groups,dc=example,dc=org
ou: Groups
objectClass: organizationalUnit
objectClass: top

# sentry_admin, Groups, example.org
dn: cn=sentry_admin,ou=Groups,dc=example,dc=org
uniqueMember: cn=john,ou=Users,dc=example,dc=org
cn: sentry_admin
objectClass: groupOfUniqueNames
objectClass: top

# sentry_owner, Groups, example.org
dn: cn=sentry_owner,ou=Groups,dc=example,dc=org
uniqueMember: cn=anita,ou=Users,dc=example,dc=org
cn: sentry_owner
objectClass: groupOfUniqueNames
objectClass: top

Conf for package:

import ldap
from django_auth_ldap.config import LDAPSearch, GroupOfUniqueNamesType

AUTH_LDAP_SERVER_URI = 'ldap://my-ldap:389'
AUTH_LDAP_BIND_DN = 'cn=my_super_powerfull_user,dc=example,dc=org'
AUTH_LDAP_BIND_PASSWORD = '*********'

AUTH_LDAP_USER_SEARCH = LDAPSearch(
    'ou=Users,dc=example,dc=org',
    ldap.SCOPE_SUBTREE,
    '(cn=%(user)s)'
)

AUTH_LDAP_GROUP_SEARCH = LDAPSearch(
   'ou=Groups,dc=example,dc=org',
    ldap.SCOPE_SUBTREE,
   '(objectClass=groupOfUniqueNames)'
)

AUTH_LDAP_GROUP_TYPE = GroupOfUniqueNamesType()
AUTH_LDAP_USER_ATTR_MAP = {'username': 'mail', 'name': 'cn', 'email': 'mail'}

AUTH_LDAP_DEFAULT_SENTRY_ORGANIZATION = 'Sentry'
AUTH_LDAP_SENTRY_SUBSCRIBE_BY_DEFAULT = False
AUTH_LDAP_SENTRY_ORGANIZATION_GLOBAL_ACCESS = False
AUTH_LDAP_SENTRY_ORGANIZATION_ROLE_TYPE = 'member'

AUTH_LDAP_SENTRY_GROUP_ROLE_MAPPING = {
    'owner': ['sentry_owner'],
    'admin': ['sentry_admin'],
}

AUTH_LDAP_FIND_GROUP_PERMS = True
AUTH_LDAP_CACHE_GROUPS = True
AUTH_LDAP_GROUP_CACHE_TIMEOUT = 3600

AUTHENTICATION_BACKENDS = AUTHENTICATION_BACKENDS + (
    'sentry_ldap_auth.backend.SentryLdapBackend',
)

requirements.txt

https://github.com/Banno/getsentry-ldap-auth/archive/master.zip
django-auth-ldap <=1.2.17
SimonGolms commented 4 years ago

Is it possible that AUTH_LDAP_SENTRY_GROUP_ROLE_MAPPING has not worked at all?

Starting from the source code, the attribute group_names is used. https://github.com/Banno/getsentry-ldap-auth/blob/a756d2e9ab013fe1d127b0b21f6edae67174ad2f/sentry_ldap_auth/backend.py#L87

But this attribute can only be used when AUTH_LDAP_MIRROR_GROUPS is set to True.

https://django-auth-ldap.readthedocs.io/en/latest/users.html#direct-attribute-access group_names: The set of groups that this user belongs to, as simple names. These are the names that will be used if AUTH_LDAP_MIRROR_GROUPS is used.

https://django-auth-ldap.readthedocs.io/en/latest/reference.html#auth-ldap-mirror-groups AUTH_LDAP_MIRROR_GROUPS If True, LDAPBackend will mirror a user’s LDAP group membership in the Django database. Any time a user authenticates, we will create all of their LDAP groups as Django groups and update their Django group membership to exactly match their LDAP group membership. If the LDAP server has nested groups, the Django database will end up with a flattened representation.

If AUTH_LDAP_MIRROR_GROUPS = True in sentry.config.py it will lead to a crash during the login Sentry.


I have the feeling to use group_dns would be the better option

https://django-auth-ldap.readthedocs.io/en/latest/users.html#direct-attribute-access group_dns: The set of groups that this user belongs to, as DNs.

member_role = _get_effective_sentry_role(ldap_user.group_dns) 

At least the following group mapping should then be possible?

AUTH_LDAP_SENTRY_GROUP_ROLE_MAPPING = {
'owner': ['cn=sentry_owner,ou=groups,dc=site,dc=io'],
'admin': ['cn=sentry_admin,ou=groups,dc=site,dc=io']
}
agendartobias commented 4 years ago

Did anyone get this working? I have role mapped but every user that login for first time the default "member" role is assinged to him. So role mapping dont work for me :(

guilh22 commented 1 year ago
#############
# LDAP AUTH #
#############
import ldap
from django_auth_ldap.config import LDAPSearch, GroupOfNamesType, NestedGroupOfNamesType

# Server URI
AUTH_LDAP_SERVER_URI = os.getenv("AUTH_LDAP_SERVER_URI", "")

# Define ldap_config template variables (Not natively used by django-auth-ldap)
AUTH_LDAP_BASE  = os.getenv("AUTH_LDAP_BASE", "")
AUTH_LDAP_USER_BASE = 'cn=users,' + AUTH_LDAP_BASE
AUTH_LDAP_GROUP_BASE = "cn=groups," + AUTH_LDAP_BASE

# Instead of a BIND Search Account, using authenticated user to search
AUTH_LDAP_BIND_AS_AUTHENTICATING_USER = True

# If a user's DN is producible from their username, we don't need to search.
AUTH_LDAP_USER_DN_TEMPLATE = "uid=%(user)s," + AUTH_LDAP_USER_BASE

AUTH_LDAP_USER_ATTR_MAP = {
    'username': 'uid',
    'name': 'cn',
    'email': 'mail'
}

AUTH_LDAP_MAIL_VERIFIED = True

AUTH_LDAP_GROUP_FILTER = "(objectClass=groupOfNames)"
AUTH_LDAP_GROUP_SEARCH = LDAPSearch(AUTH_LDAP_GROUP_BASE,ldap.SCOPE_SUBTREE, AUTH_LDAP_GROUP_FILTER)
AUTH_LDAP_GROUP_TYPE = GroupOfNamesType(name_attr="cn")

AUTH_LDAP_REQUIRE_GROUP = "cn=sentry-access," + AUTH_LDAP_GROUP_BASE
AUTH_LDAP_DENY_GROUP = None
AUTH_LDAP_FIND_GROUP_PERMS = True
AUTH_LDAP_CACHE_GROUPS = True
AUTH_LDAP_ALWAYS_UPDATE_USER = True
AUTH_LDAP_GROUP_CACHE_TIMEOUT = 3600

AUTH_LDAP_SENTRY_DEFAULT_ORGANIZATION = os.getenv("AUTH_LDAP_DEFAULT_ORGANIZATION", "")
AUTH_LDAP_DEFAULT_SENTRY_ORGANIZATION = u'Sentry'
AUTH_LDAP_SENTRY_ORGANIZATION_ROLE_TYPE = 'member'

AUTH_LDAP_USER_FLAGS_BY_GROUP = {
    'owner': "cn=sentry-owner," + AUTH_LDAP_GROUP_BASE,
    'admin': "cn=sentry-admin," + AUTH_LDAP_GROUP_BASE,
    'manager': "cn=sentry-manager," + AUTH_LDAP_GROUP_BASE,
    'member': "cn=sentry-member," + AUTH_LDAP_GROUP_BASE
}

AUTH_LDAP_SENTRY_GROUP_ROLE_MAPPING = {
    'owner':  ['sentry-owner'],
    'admin': ['sentry-admin'],
    'manager': ['sentry-manager'],
    'member': ['sentry-member']
}

AUTH_LDAP_SENTRY_ORGANIZATION_GLOBAL_ACCESS = True

AUTHENTICATION_BACKENDS = AUTHENTICATION_BACKENDS + (
    'sentry_auth_ldap.backend.SentryLdapBackend',
)

# Optional logging for diagnostics.
LOGGING['disable_existing_loggers'] = False
import logging
logger = logging.getLogger('django_auth_ldap')
logger.setLevel(logging.DEBUG)

This is how I successfully make it work on my side.

I might have some extra stuff not really needed but at least I got able to have my permission from LDAP.

This is use with getsentry helm chart and it is working with LDAP on FreeIPA.

Make sure to set up these variable in your dockerfile:

      env:
        - name: AUTH_LDAP_DEFAULT_ORGANIZATION
          value: sentry
        - name: AUTH_LDAP_SERVER_URI
          value: ldaps://<url>
        - name: AUTH_LDAP_BASE
          value: cn=accounts,dc=domain,dc=com