Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add foundation for LDAP in core repository #2923

Merged
merged 4 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions api/app/settings/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -942,3 +942,81 @@
FLAGSMITH_ON_FLAGSMITH_SERVER_API_URL = env(
"FLAGSMITH_ON_FLAGSMITH_SERVER_API_URL", default=FLAGSMITH_ON_FLAGSMITH_API_URL
)

# LDAP setting
LDAP_INSTALLED = importlib.util.find_spec("ldap")
if LDAP_INSTALLED:
# The URL of the LDAP server.
LDAP_AUTH_URL = env.str("LDAP_AUTH_URL", None)
if LDAP_AUTH_URL:
AUTHENTICATION_BACKENDS.insert(0, "django_python3_ldap.auth.LDAPBackend")
Comment on lines +951 to +952
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this throw an exception if a URL is not set?

Copy link
Member

@gagantrivedi gagantrivedi Nov 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's because we will include LDAP in every private cloud image... but it's up to the client to enable it (by setting LDAP_AUTH_URL). Is that correct @matthewelwell

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's correct.


# Initiate TLS on connection.
LDAP_AUTH_USE_TLS = env.bool("LDAP_AUTH_USE_TLS", False)

# The LDAP search base for looking up users.
LDAP_AUTH_SEARCH_BASE = env.str(
"LDAP_AUTH_SEARCH_BASE", "ou=people,dc=example,dc=com"
)

# The LDAP class that represents a user.
LDAP_AUTH_OBJECT_CLASS = env.str("LDAP_AUTH_OBJECT_CLASS", "inetOrgPerson")

# User model fields mapped to the LDAP
# attributes that represent them.
LDAP_AUTH_USER_FIELDS = env.dict(
"LDAP_AUTH_USER_FIELDS",
{
"username": "uid",
"first_name": "givenName",
"last_name": "sn",
"email": "mail",
},
)
# Sets the login domain for Active Directory users.
LDAP_AUTH_ACTIVE_DIRECTORY_DOMAIN = env.str(
"LDAP_AUTH_ACTIVE_DIRECTORY_DOMAIN", None
)

# Path to a callable that takes a dict of {model_field_name: value}, and returns
# a string of the username to bind to the LDAP server.
# Use this to support different types of LDAP server.
LDAP_AUTH_FORMAT_USERNAME = env.str(
"LDAP_AUTH_FORMAT_USERNAME",
"django_python3_ldap.utils.format_username_openldap",
)

# Set connection/receive timeouts (in seconds) on the underlying `ldap3` library.
LDAP_AUTH_CONNECT_TIMEOUT = env.int("LDAP_AUTH_CONNECT_TIMEOUT", None)
LDAP_AUTH_RECEIVE_TIMEOUT = env.int("LDAP_AUTH_RECEIVE_TIMEOUT", None)

# Set this to add newly created users to an organisation
LDAP_DEFAULT_FLAGSMITH_ORGANISATION_ID = env.int(
"LDAP_DEFAULT_FLAGSMITH_ORGANISATION_ID", None
)

# Path to a callable that takes a user model, a dict of {ldap_field_name: [value]}
# a LDAP connection object (to allow further lookups), and saves any additional
# user relationships based on the LDAP data.
LDAP_AUTH_SYNC_USER_RELATIONS = env.str(
"LDAP_AUTH_SYNC_USER_RELATIONS", "django_python3_ldap.utils.sync_user_relations"
)

# Path to a callable that takes a dict of {ldap_field_name: value},
# returning a list of [ldap_search_filter]. The search filters will then be AND'd
# together when creating the final search filter.
LDAP_AUTH_FORMAT_SEARCH_FILTERS = env.str(
"LDAP_AUTH_FORMAT_SEARCH_FILTERS",
default="django_python3_ldap.utils.format_search_filters",
)

# List of LDAP group DN's that needs to be synced.
LDAP_SYNCED_GROUPS = env.list("LDAP_SYNCED_GROUPS", default=[], delimiter=":")

# DN of the LDAP group that is allowed to login
# If None no group check will be performed.
LDAP_LOGIN_GROUP = env.str("LDAP_LOGIN_GROUP", None)

# The LDAP user username and password used by `sync_ldap_users_and_groups` command
LDAP_SYNC_USER_USERNAME = env.str("LDAP_SYNC_USER_USERNAME", None)
LDAP_SYNC_USER_PASSWORD = env.str("LDAP_SYNC_USER_PASSWORD", None)
18 changes: 18 additions & 0 deletions api/users/migrations/0035_add_ldap_dn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.2.20 on 2023-11-01 15:10

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('users', '0034_add_user_permission_group_membership_through_model'),
]

operations = [
migrations.AddField(
model_name='userpermissiongroup',
name='ldap_dn',
field=models.CharField(blank=True, max_length=255, null=True, unique=True),
),
]
1 change: 1 addition & 0 deletions api/users/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,7 @@ class UserPermissionGroup(models.Model):
organisation = models.ForeignKey(
Organisation, on_delete=models.CASCADE, related_name="permission_groups"
)
ldap_dn = models.CharField(blank=True, null=True, unique=True, max_length=255)
is_default = models.BooleanField(
default=False,
help_text="If set to true, all new users will be added to this group",
Expand Down