Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ For context: The connection of custom SSO providers is also described [here](../
## I want to connect AD and LDAP to OpenProject. Which attribute for authentication sources does OpenProject use?

You can freely define the attributes that are taken from LDAP sources [in the LDAP auth source configuration screen](../ldap-connections/).
For group synchronization, OpenProject supports the AD/LDAP standard for groups via "member / memberOf". The attribute cannot be configured at this time.
For group synchronization, OpenProject defaults to reverse lookup via the `memberOf` attribute on user entries (Active Directory, OpenLDAP with memberof overlay). If your LDAP server does not maintain `memberOf` on user entries, you can configure forward lookup by setting the **Group member attribute** on a synchronized filter (e.g. `uniqueMember` for `groupOfUniqueNames`, or `member` for `groupOfNames`). See [LDAP group synchronization](../ldap-connections/ldap-group-synchronization/) for details.

## Is there an option to mass-create users in OpenProject via the LDAP?

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ keywords: synchronize ldap groups

# Synchronize LDAP and OpenProject groups (Enterprise add-on)

Note: This feature is available for the Enterprise on-premises only, for OpenProject versions 7.4+. For more information and differences to Community edition, [see this page](https://www.openproject.org/enterprise-edition/).
This feature is available for the Enterprise edition only. For more information and differences to Community edition, [see the Enterprise edition overview](https://www.openproject.org/enterprise-edition/).

In OpenProject Enterprise on-premises, you can provision and periodically synchronize groups and their members from your existing LDAP or Active Directory. Group memberships using the [groupOfNames](https://tools.ietf.org/html/rfc4519#section-3.5) LDAP object class are supported for this. This guide assumes that you:
In OpenProject Enterprise edition, you can provision and periodically synchronize groups and their members from your existing LDAP or Active Directory. This guide assumes that you:

- have set up your LDAP authentication source (See the “[Manage LDAP authentication](../../ldap-connections/)” guide)
- have at least one LDAP entry with a *groupOfNames* object class and members of that group to contain the *`memberOf: <DN of the group>`* attribute to determine the members of a group entry. Right now we do not support LDAP instances that only have *member* attributes, but not the inverse *memberOf* property.
- have LDAP group entries whose members can be discovered either via the `memberOf` attribute on user entries (reverse lookup, default) or via a member list attribute on the group entry such as `uniqueMember` or `member` (forward lookup, configurable).

> [!NOTE]
> OpenProject does not support other attributes other than the `memberOf` property to define groups. Please make sure that user objects have the `memberOf` property for the synchronization to work.
> This feature is available for both Enterprise cloud and Enterprise on-premises. When using Enterprise cloud, your LDAP server must be reachable from the OpenProject cloud infrastructure, which typically means exposing it to the internet, which is not recommended. This is a network and security consideration for your organization to evaluate. Consider using [SAML](../../saml/) or [SCIM provisioning](../../scim/) as a more secure alternative.

For the sake of simplicity, we assume that in this guide, your LDAP structure looks like the following:

Expand All @@ -35,38 +35,37 @@ In order to get to the LDAP group sync administration pane, expand the LDAP auth

Synchronizing a single LDAP group allows you to connect an existing group in OpenProject with one from LDAP.

LDAP group synchronization extends the memberships defined by administrators in an existing OpenProject group. Important things to note are:
LDAP group synchronization extends the memberships defined by administrators in an existing OpenProject group. Important things to note are:

- You need to have created at least one manual group in the OpenProject administration before you continue.
- Group synchronization for this group is enabled by an administrator creating a *synchronized LDAP group* that ties the OpenProject group to an LDAP entry.
- Only synchronized memberships will be removed from the OpenProject group. If you want to add a user outside your LDAP authentication to an OpenProject group, you can do so without the membership being affected from the group synchronization.

### Single synchronized groups

To create a new synchronized group, use the button on the top right of the page. There, you will select your LDAP authentication source that contains the group, as well as the existing OpenProject group that members should be synchronized to. The following options can be set:
To create a new synchronized group, use the button on the top right of the page. There, you will select your LDAP authentication source that contains the group, as well as the existing OpenProject group that members should be synchronized to. The following options can be set:

- **LDAP connection:** Select the LDAP connection you want this synchronized group to use. Users created by group synchronization will be tied to that LDAP and may bind against it for authentication.
- **DN:** Enter the full distinguished name (DN) of the group you want to synchronize. For example: `cn=team1,ou=groups,dc=example,dc=com`.
- **Sync users:** Check this option if you want members of this group to be automatically created in OpenProject. When unchecked, only members of the group that also are existing users in OpenProject can be synchronized.
- **Group:** Select an OpenProject group you want the members of the LDAP group to synchronize to.

Click on *Create* to finish the creation of the synchronized group. The LDAP memberships of each user will be synchronized hourly through a background job on your packaged installation. Changes and output will be logged to */var/log/openproject/cron-hourly.log*.
Click on *Create* to finish the creation of the synchronized group. The LDAP memberships of each user will be synchronized hourly through a background job.

If you want to trigger the synchronization *manually* you can do so by running the respective rake task directly.
In the packaged installation, for instance, this would work like this:
To trigger a full synchronization manually (group discovery **and** member sync), run the following rake task in a [console](../../../../installation-and-operations/operation/control/):

```shell
sudo openproject run bundle exec rake ldap_groups:synchronize
bundle exec rake ldap_groups:synchronize
```

This method of creating synchronized groups is well-suited for a small number of groups, or a very individual set of groups that you need to synchronize. It is very flexible by allowing individual groups to synchronize users into OpenProject.
This method of creating synchronized groups is well-suited for a small number of groups, or a carefully selected set of groups that you need to synchronize. It is very flexible by allowing individual groups to synchronize users into OpenProject.

If you need to synchronize a large number of groups that follow a common pattern, consider using the following filter functionality.


## Automatic groups using synchronized filters

Instead of manually synchronizing groups from a given group DN in your LDAP, you can also create filter objects that will query the LDAP not only for group members, but the groups themselves.
Instead of manually synchronizing groups from a given group DN in your LDAP, you can also create filter objects that automatically discover matching groups in your LDAP and synchronize their members.

When the synchronization task is executed, the filter is being queried against the LDAP and resulting group objects will be created as synchronized groups *and* as OpenProject groups.

Expand All @@ -82,11 +81,23 @@ To create a new synchronized filter, use the button on the top right of the inde
- **LDAP connection:** Select the LDAP connection you want this synchronized filter to use. Users created by group synchronization will be tied to that LDAP and may bind against it for authentication.
- **Search base DN:** (optional) Enter the base DN of the LDAP subtree you want to perform the search in. If you leave this unset, the base DN of the LDAP connection will be used instead. The DN specified here must contain the base DN of the LDAP connection to be valid.
- **LDAP filter:** The LDAP filter string to be used for identifying LDAP group entries to be synchronized with OpenProject.
- **Group member attribute:** (optional) The attribute on group entries that lists member DNs, used for forward lookup. Leave empty (default) to use reverse lookup via the `memberOf` attribute on user entries. Set this to `uniqueMember` for `groupOfUniqueNames` schemas, or `member` for `groupOfNames` schemas. Use forward lookup when your LDAP server does not maintain `memberOf` on user entries.

Click on *Create* to finish the creation of the synchronized filter. This filter is being executed hourly as part of the background job before the actual group synchronization runs.
Click on *Create* to finish the creation of the synchronized filter. This filter is being executed hourly as part of the background job before the actual group synchronization runs.

Once the filter is created, the **Discover LDAP groups** button on the filter detail page runs the discovery immediately: it queries LDAP using the filter and creates the corresponding synchronized groups and OpenProject groups. It does **not** synchronize group members — member synchronization is handled by the hourly background job.

To trigger a full synchronization (group discovery **and** member sync) manually, run the following rake task in a [console](../../../../installation-and-operations/operation/control/):

```shell
bundle exec rake ldap_groups:synchronize
```

> [!NOTE]
> Forward lookup via the **Group member attribute** is only available for groups created through a synchronized filter. Manually-created synchronized groups always use reverse lookup (`memberOf`).

> [!NOTE]
> If you manually create a synchronized group that is also found by a filter, its properties (such as the *Sync users* setting) is being overridden by the filter setting.
> If you manually create a synchronized group that is also found by a filter, its properties (such as the *Sync users* setting) are overridden by the filter settings.

## FAQ

Expand All @@ -108,6 +119,7 @@ Please double check the DN of the groups and the LDAP connection. The base DN of
For users to be automatically synchronized, the following conditions need to be met:

1. The connection, or the LDAP group need to have "Sync users" checked. This setting overrides the LDAP connection's "Sync users" attribute for fine-grained control over which groups will have users synchronized.
2. The group needs to define their members using the `member` LDAP property, and users need to have the `memberOf` property or virtual property. OpenProject will look for users with the following filter: `(memberOf=<DN of the group>).` You can use `ldapsearch` to verify that this works as expected.
3. The users defined in the groups need to have all required attributes present to be created. These are at *login, email, first and last name*. If any of these attributes are missing, the user cannot be saved to the database.
4. If you enterprise license exceeds the user limit, new users can also not be synchronized through LDAP. A corresponding log entry will be logged.
2. OpenProject needs to be able to resolve group members. By default it uses **reverse lookup**: it searches for users with the filter `(memberOf=<DN of the group>)`, which requires `memberOf` to be present on user entries (Active Directory, OpenLDAP with memberof overlay). If your LDAP server does not support `memberOf`, configure **forward lookup** by setting the **Group member attribute** on the synchronized filter (e.g. `uniqueMember` or `member`). You can use `ldapsearch` to verify either approach works as expected.
3. The users defined in the groups need to have all required attributes present and non-empty to be created: *login, email, first and last name*. If any of these attributes are missing or empty, the user cannot be saved to the database.
4. The **Login** attribute configured on the LDAP connection must match an attribute that is actually present on your LDAP user entries. If the attribute name is wrong or absent, users will be found in the group but not matched or created in OpenProject. Verify the attribute name against your LDAP schema and update the LDAP connection configuration if needed.
5. If your enterprise license exceeds the user limit, new users cannot be synchronized through LDAP. On-premises users will find a corresponding entry in the application logs.
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ class SynchronizedFiltersController < ::ApplicationController
layout "admin"
menu_item :plugin_ldap_groups

def show; end

def new
@filter = SynchronizedFilter.new
end

def show; end

def end; end

def destroy_info
Expand Down Expand Up @@ -80,13 +80,17 @@ def synchronize
private

def find_filter
@filter = SynchronizedFilter.find(params[:ldap_filter_id])
@filter = SynchronizedFilter.find(params.expect(:ldap_filter_id))
end

def permitted_params
params
.require(:synchronized_filter)
.permit(:filter_string, :name, :ldap_auth_source_id, :group_name_attribute, :sync_users, :base_dn)
params.expect(synchronized_filter: %i[filter_string
name
ldap_auth_source_id
group_name_attribute
sync_users
base_dn
member_lookup_attribute])
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ def used_base_dn
base_dn.presence || ldap_auth_source.base_dn
end

def forward_member_lookup?
member_lookup_attribute.present?
end

def seeded_from_env?
return false if ldap_auth_source.nil?

Expand Down
Loading
Loading