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

OF-2923 (and friends): Use more than one LDAP server #2632

Merged
merged 14 commits into from
Dec 18, 2024

Conversation

guusdk
Copy link
Member

@guusdk guusdk commented Dec 12, 2024

Openfire offers various 'providers' to allow it to use users, groups and other data from external systems, such as LDAP/AD. Typically, one such external system is used, but Openfire also allows for more than one system to be used. This way, it can, for example, obtain part of the userbase from its own database, and another part from a directory service.

Up until now, it was not possible to configure Openfire to use two systems of the same type (eg: two different LDAP servers), as it was impossible to provide more than one set of configuration for the external system (configuring more than one of them would thus cause multiple connections to the same system).

In this change set, the providers have been refactored, to overcome this limitation. In essence, they a provider can now be configured with an opaque 'config' value (which can be used by implementations as a distinct source from where to obtain further configuration).

This concept has been applied to the various LDAP providers, making it possible for one Openfire server to now interact with more than one distinct LDAP server (that can each host a completely different data set).

Some smaller, related changes have also been added to this PR.

This moves mostly duplicated code from HybridUserProvider and MappedUserProvider in their common superclass, UserMultiProvider

Slight functional changes have been introduces, which intent to make behavior more consistent.
This moves mostly duplicated code from HybridUserPropertyProvider and MappedUserPropertyProvider in a new, common superclass, UserPropertyMultiProvider

Slight functional changes have been introduces, which intent to make behavior more consistent.
This moves mostly duplicated code from HybridAuthProvider and MappedAuthProvider in a new, common superclass, AuthMultiProvider

Slight functional changes have been introduces, which intent to make behavior more consistent.
As are available for Users, UserProperties and Auth, this commit now introduces a Hybrid and Mapped provider for Groups.

With these providers, groups can be obtained from more than one external system.

This change is a prerequisite for OF-2923. As both issues were developed at the same time, some concepts related to both issues are applied to this commit. This foreshadows more, similar changes related to OF-2923.
This change replaces the singleton LDAP Manager (where LDAP connectivity information is stored) with a solution in which multiple, named, LDAP Managers can exist.

Having more than one LDAP configuration allows Openfire (through Mapped or Hybrid providers) to connect to more than one LDAP service. This can be used to combine data from multiple services.

The various Hybrid and Mapped providers that pre-exist have received modifications that allow them to configure providers with an additional string (which is used in the LDAP manager to differentiate between the configuration of different LDAP servers).
This commit fulfills the promise expressed in documentation, by adding the configuration options that are defined for the cache used by LdapAuthProvider.
This exposes a pre-existing LDAP property in the setup wizard.
Where possible, execute operations on providers in parallel.
@guusdk
Copy link
Member Author

guusdk commented Dec 12, 2024

What's still lacking is updated documentation.

I've tested the LDAP setup with local OpenLDAP services that each provide different users and groups. It seems to work nicely for users, groups and authentication. I've been using this configuration:

<?xml version="1.0" encoding="UTF-8"?>

<jive>
    <adminConsole>
        <port>9090</port>
        <securePort>9091</securePort>
    </adminConsole>
    <connectionProvider>
        <className>org.jivesoftware.database.EmbeddedConnectionProvider</className>
    </connectionProvider>
    <setup>true</setup>
    <locale>en</locale>
    <fqdn>example.org</fqdn>
    <provider>
        <auth>
            <className>org.jivesoftware.openfire.auth.HybridAuthProvider</className>
        </auth>
        <group>
            <className>org.jivesoftware.openfire.group.HybridGroupProvider</className>
        </group>
        <user>
            <className>org.jivesoftware.openfire.user.HybridUserProvider</className>
        </user>
    </provider>

    <hybridAuthProvider>
        <primaryProvider>
            <className>org.jivesoftware.openfire.ldap.LdapAuthProvider</className>
            <config>ldapPrimary</config>
        </primaryProvider>
        <secondaryProvider>
            <className>org.jivesoftware.openfire.ldap.LdapAuthProvider</className>
            <config>ldapSecondary</config>
        </secondaryProvider>
    </hybridAuthProvider>

    <hybridGroupProvider>
        <primaryProvider>
            <className>org.jivesoftware.openfire.ldap.LdapGroupProvider</className>
            <config>ldapPrimary</config>
        </primaryProvider>
        <secondaryProvider>
            <className>org.jivesoftware.openfire.ldap.LdapGroupProvider</className>
            <config>ldapSecondary</config>
        </secondaryProvider>
    </hybridGroupProvider>

    <hybridUserProvider>
        <primaryProvider>
            <className>org.jivesoftware.openfire.ldap.LdapUserProvider</className>
            <config>ldapPrimary</config>
        </primaryProvider>
        <secondaryProvider>
            <className>org.jivesoftware.openfire.ldap.LdapUserProvider</className>
            <config>ldapSecondary</config>
        </secondaryProvider>
    </hybridUserProvider>

    <ldapPrimary>
        <host>localhost</host>
        <port>20389</port>
        <baseDN>dc=springfield,dc=com</baseDN>
        <adminDN>cn=admin,dc=springfield,dc=com</adminDN>
        <adminPassword>Anytown</adminPassword>
        <startTlsEnabled>false</startTlsEnabled>
        <sslEnabled>false</sslEnabled>
        <groupSearchFilter>(objectClass=Group)</groupSearchFilter>
    </ldapPrimary>
    <ldapSecondary>
        <host>localhost</host>
        <port>10389</port>
        <baseDN>ou=people,dc=planetexpress,dc=com</baseDN>
        <adminDN>cn=admin,dc=planetexpress,dc=com</adminDN>
        <adminPassword>GoodNewsEveryone</adminPassword>
        <startTlsEnabled>false</startTlsEnabled>
        <sslEnabled>false</sslEnabled>
        <groupSearchFilter>(objectClass=Group)</groupSearchFilter>
    </ldapSecondary>

    <admin>
        <authorizedUsernames>leela</authorizedUsernames>
    </admin>
</jive>

This adds unit tests for most of the 'Hybrid' and 'Mapped' providers for User, UserProperty, Auth and Group.

One bigger issue introduced by the recent (as of yet unreleased) refactoring was identified through these tests, and was fixed in `HybridUserPropertyProvider`

Various smaller issues (mostly with throwing one type of exception while another was expected) have also been addressed by this.
The `ldap.host` property can be used to define hot standby hosts. The existing documentation hints at that, but isn't overly clear. This commit adds more explicit wording.
This adds documentation for the functionality introduced in OF-2923.
Copy link
Member

@Fishbowler Fishbowler left a comment

Choose a reason for hiding this comment

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

Nothing show-stopping, mostly nitpicking.

documentation/multi-providers.html Outdated Show resolved Hide resolved
documentation/multi-providers.html Outdated Show resolved Hide resolved
documentation/separating-admin-users-guide.html Outdated Show resolved Hide resolved
/**
* Returns an unmodifiable Collection of all shared groups in the system for a given user.
*
* Implementations should use the bare JID representation of the JID passed as an argument to this method.
Copy link
Member

Choose a reason for hiding this comment

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

If implementations should use the bare JID, should there be protection or warning around a non-bare JID?

Copy link
Member Author

Choose a reason for hiding this comment

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

Possibly, although I wonder if strict validation breaks some edge case that we're not thinking of now. The description 'should' leaves a bit of wiggle room. I'd like to keep this out of scope for this change (this description is copied from the preexisting interface definition), and should maybe be enforced by the providers that is being delegated to, rather than this 'multi' provider (that does the delegation). I had started writing a ticket for this, but given the 'should' text, I'm not sure if Openfire should enforce this as if it was a MUST.

@guusdk
Copy link
Member Author

guusdk commented Dec 18, 2024

I'm merging as-is (as that makes creating a build that I need a bit easer), pending some feedback from @Fishbowler. Happy to add additional changes in a new commit.

@guusdk guusdk merged commit c311ae0 into igniterealtime:main Dec 18, 2024
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants