2016-08-09 22:21:19 +02:00
|
|
|
# Authentication methods
|
2016-07-12 23:32:12 +02:00
|
|
|
|
2018-01-19 02:43:23 +01:00
|
|
|
Zulip supports a wide variety of authentication methods. Some of them
|
|
|
|
require configuration to set up.
|
2016-07-30 00:02:32 +02:00
|
|
|
|
2018-01-19 02:43:23 +01:00
|
|
|
To configure or disable authentication methods on your Zulip server,
|
|
|
|
edit the `AUTHENTICATION_BACKENDS` setting in
|
|
|
|
`/etc/zulip/settings.py`, as well as any additional configuration your
|
|
|
|
chosen authentication methods require; then restart the Zulip server.
|
|
|
|
|
|
|
|
Details on each method below.
|
|
|
|
|
|
|
|
## Email and password
|
|
|
|
|
|
|
|
The `EmailAuthBackend` method is the one method enabled by default,
|
|
|
|
and it requires no additional configuration.
|
|
|
|
|
|
|
|
Users set a password with the Zulip server, and log in with their
|
|
|
|
email and password.
|
|
|
|
|
|
|
|
When first setting up your Zulip server, this method must be used for
|
|
|
|
creating the initial realm and user. You can disable it after that.
|
|
|
|
|
2020-02-11 23:13:09 +01:00
|
|
|
## Plug-and-play SSO (Google, GitHub, GitLab)
|
2018-01-19 02:43:23 +01:00
|
|
|
|
|
|
|
With just a few lines of configuration, your Zulip server can
|
|
|
|
authenticate users with any of several single-sign-on (SSO)
|
|
|
|
authentication providers:
|
2019-02-02 16:51:26 +01:00
|
|
|
* Google accounts, with `GoogleAuthBackend`
|
2018-01-19 02:43:23 +01:00
|
|
|
* GitHub accounts, with `GitHubAuthBackend`
|
2020-03-16 01:55:08 +01:00
|
|
|
* GitLab accounts, with `GitLabAuthBackend`
|
2018-10-05 14:32:02 +02:00
|
|
|
* Microsoft Azure Active Directory, with `AzureADAuthBackend`
|
2018-01-19 02:43:23 +01:00
|
|
|
|
|
|
|
Each of these requires one to a handful of lines of configuration in
|
|
|
|
`settings.py`, as well as a secret in `zulip-secrets.conf`. Details
|
|
|
|
are documented in your `settings.py`.
|
|
|
|
|
2019-09-29 06:32:56 +02:00
|
|
|
## SAML
|
|
|
|
|
2019-10-30 00:23:07 +01:00
|
|
|
Zulip 2.1 and later supports SAML authentication, used by Okta,
|
|
|
|
OneLogin, and many other IdPs (identity providers). You can configure
|
|
|
|
it as follows:
|
2019-09-29 06:32:56 +02:00
|
|
|
|
2020-04-30 15:15:38 +02:00
|
|
|
1. These instructions assume you have an installed Zulip server; if
|
|
|
|
you're using Zulip Cloud, see [this article][saml-help-center],
|
|
|
|
which also has IdP-side configuration advice for common IdPs.
|
|
|
|
|
|
|
|
You can have created a Zulip organization already using the default
|
|
|
|
EmailAuthBackend, or plan to create the organization using SAML
|
|
|
|
authentication.
|
2019-09-29 06:32:56 +02:00
|
|
|
|
|
|
|
1. Tell your IdP how to find your Zulip server:
|
|
|
|
|
|
|
|
* **SP Entity ID**: `https://yourzulipdomain.example.com`.
|
2019-12-12 01:32:26 +01:00
|
|
|
|
|
|
|
The `Entity ID` should match the value of
|
|
|
|
`SOCIAL_AUTH_SAML_SP_ENTITY_ID` computed in the Zulip settings.
|
|
|
|
You can get the correct value by running the following:
|
2020-01-03 07:03:13 +01:00
|
|
|
`/home/zulip/deployments/current/scripts/get-django-setting
|
2019-12-12 01:32:26 +01:00
|
|
|
SOCIAL_AUTH_SAML_SP_ENTITY_ID`.
|
|
|
|
|
2019-09-29 06:32:56 +02:00
|
|
|
* **SSO URL**:
|
|
|
|
`https://yourzulipdomain.example.com/complete/saml/`. This is
|
2019-12-12 01:32:26 +01:00
|
|
|
the "SAML ACS url" in SAML terminology.
|
|
|
|
|
|
|
|
If you're
|
|
|
|
[hosting multiple organizations](../production/multiple-organizations.html#authentication),
|
2019-12-11 21:39:22 +01:00
|
|
|
you need to use `SOCIAL_AUTH_SUBDOMAIN`. For example,
|
|
|
|
if `SOCIAL_AUTH_SUBDOMAIN="auth"` and `EXTERNAL_HOST=zulip.example.com`,
|
|
|
|
this should be `https://auth.zulip.example.com/complete/saml/`.
|
2019-09-29 06:32:56 +02:00
|
|
|
|
2019-10-28 23:21:49 +01:00
|
|
|
2. Tell Zulip how to connect to your SAML provider(s) by filling
|
2019-09-29 06:32:56 +02:00
|
|
|
out the section of `/etc/zulip/settings.py` on your Zulip server
|
|
|
|
with the heading "SAML Authentication".
|
|
|
|
* You will need to update `SOCIAL_AUTH_SAML_ORG_INFO` with your
|
2019-10-28 23:21:49 +01:00
|
|
|
organization name (`displayname` may appear in the IdP's
|
2019-09-29 06:32:56 +02:00
|
|
|
authentication flow; `name` won't be displayed to humans).
|
|
|
|
* Fill out `SOCIAL_AUTH_SAML_ENABLED_IDPS` with data provided by
|
|
|
|
your identity provider. You may find [the python-social-auth
|
|
|
|
SAML
|
2020-03-20 13:30:21 +01:00
|
|
|
docs](https://python-social-auth.readthedocs.io/en/latest/backends/saml.html)
|
2019-09-29 06:32:56 +02:00
|
|
|
helpful. You'll need to obtain several values from your IdP's
|
|
|
|
metadata and enter them on the right-hand side of this
|
|
|
|
Python dictionary:
|
|
|
|
1. Set the outer `idp_name` key to be an identifier for your IdP,
|
2019-10-28 23:21:49 +01:00
|
|
|
e.g. `testshib` or `okta`. This field appears in URLs for
|
|
|
|
parts of your Zulip server's SAML authentication flow.
|
2019-09-29 06:32:56 +02:00
|
|
|
2. The IdP should provide the `url` and `entity_id` values.
|
|
|
|
3. Save the `x509cert` value to a file; you'll use it in the
|
|
|
|
instructions below.
|
|
|
|
4. The values needed in the `attr_` fields are often configurable
|
|
|
|
in your IdP's interface when setting up SAML authentication
|
|
|
|
(referred to as "Attribute Statements" with Okta, or
|
|
|
|
"Attribute Mapping" with GSuite). You'll want to connect
|
|
|
|
these so that Zulip gets the email address (used as a unique
|
|
|
|
user ID) and name for the user.
|
2019-11-04 00:08:29 +01:00
|
|
|
5. The `display_name` and `display_icon` fields are used to
|
2019-10-28 23:21:49 +01:00
|
|
|
display the login/registration buttons for the IdP.
|
2019-09-29 06:32:56 +02:00
|
|
|
|
|
|
|
3. Install the certificate(s) required for SAML authentication. You
|
|
|
|
will definitely need the public certificate of your IdP. Some IdP
|
|
|
|
providers also support the Zulip server (Service Provider) having
|
|
|
|
a certificate used for encryption and signing. We detail these
|
|
|
|
steps as optional below, because they aren't required for basic
|
|
|
|
setup, and some IdPs like Okta don't fully support Service
|
|
|
|
Provider certificates. You should install them as follows:
|
|
|
|
|
|
|
|
1. On your Zulip server, `mkdir -p /etc/zulip/saml/idps/`
|
|
|
|
2. Put the IDP public certificate in `/etc/zulip/saml/idps/{idp_name}.crt`
|
|
|
|
3. (Optional) Put the Zulip server public certificate in `/etc/zulip/saml/zulip-cert.crt`
|
|
|
|
4. (Optional) Put the Zulip server private key in `/etc/zulip/saml/zulip-private-key.key`
|
|
|
|
5. Set the proper permissions on these files and directories:
|
|
|
|
|
|
|
|
```
|
|
|
|
chown -R zulip.zulip /etc/zulip/saml/
|
|
|
|
find /etc/zulip/saml/ -type f -exec chmod 644 -- {} +
|
|
|
|
chmod 640 /etc/zulip/saml/zulip-private-key.key
|
|
|
|
```
|
|
|
|
|
|
|
|
4. (Optional) If you configured the optional public and private server
|
|
|
|
certificates above, you can enable the additional setting
|
|
|
|
`"authnRequestsSigned": True` in `SOCIAL_AUTH_SAML_SECURITY_CONFIG`
|
|
|
|
to have the SAMLRequests the server will be issuing to the IdP
|
|
|
|
signed using those certificates. Additionally, if the IdP supports
|
|
|
|
it, you can upload the public certificate to enable encryption of
|
|
|
|
assertions in the SAMLResponses the IdP will send about
|
|
|
|
authenticated users.
|
|
|
|
|
|
|
|
5. Enable the `zproject.backends.SAMLAuthBackend` auth backend, in
|
|
|
|
`AUTHENTICATION_BACKENDS` in `/etc/zulip/settings.py`.
|
|
|
|
|
2019-10-23 22:03:51 +02:00
|
|
|
6. [Restart the Zulip server](../production/settings.md) to ensure
|
|
|
|
your settings changes take effect. The Zulip login page should now
|
2020-08-11 02:20:10 +02:00
|
|
|
have a button for SAML authentication that you can use to log in or
|
2019-10-23 22:03:51 +02:00
|
|
|
create an account (including when creating a new organization).
|
2019-09-29 06:32:56 +02:00
|
|
|
|
|
|
|
7. If the configuration was successful, the server's metadata can be
|
|
|
|
found at `https://yourzulipdomain.example.com/saml/metadata.xml`. You
|
|
|
|
can use this for verifying your configuration or provide it to your
|
|
|
|
IdP.
|
|
|
|
|
2020-04-30 15:15:38 +02:00
|
|
|
[saml-help-center]: https://zulip.com/help/saml-authentication
|
|
|
|
|
2020-05-23 15:21:19 +02:00
|
|
|
### IdP-initiated SSO
|
|
|
|
|
|
|
|
The above configuration is sufficient for Service Provider initialized
|
|
|
|
SSO, i.e. you can visit the Zulip webapp and click "Sign in with
|
|
|
|
{IdP}" and it'll correctly start the authentication flow. If you are
|
2020-06-25 01:25:14 +02:00
|
|
|
not hosting multiple organizations, with Zulip 3.0+, the above
|
2020-05-23 15:21:19 +02:00
|
|
|
configuration is also sufficient for Identity Provider initiated SSO,
|
|
|
|
i.e. clicking a "Sign in to Zulip" button on the IdP's website can
|
|
|
|
correctly authenticate the user to Zulip.
|
|
|
|
|
|
|
|
If you're hosting multiple organizations and thus using the
|
|
|
|
`SOCIAL_AUTH_SUBDOMAIN` setting, you'll need to configure a custom
|
|
|
|
`RelayState` in your IdP of the form `{"subdomain":
|
|
|
|
"yourzuliporganization"}` to let Zulip know which organization to
|
|
|
|
authenticate the user to when they visit your SSO URL from the IdP.
|
|
|
|
(If the organization is on the root domain, use the empty string:
|
|
|
|
`{"subdomain": ""}`.).
|
|
|
|
|
2018-09-25 23:29:03 +02:00
|
|
|
```eval_rst
|
|
|
|
.. _ldap:
|
|
|
|
```
|
2020-06-19 21:44:29 +02:00
|
|
|
|
|
|
|
### Restricting access to specific organizations
|
|
|
|
|
|
|
|
If you're hosting multiple Zulip organizations, you can restrict which
|
|
|
|
organizations can use a given IdP by setting `limit_to_subdomains`.
|
|
|
|
For example, `limit_to_subdomains = ["", "engineering"]` would
|
|
|
|
restrict an IdP the root domain and the `engineering` subdomain.
|
|
|
|
|
|
|
|
You can achieve the same goal with a SAML attribute; just declare
|
|
|
|
which attribute using `attr_org_membership` in the IdP configuration.
|
|
|
|
For the root subdomain, `www` in the list will work, or any other of
|
|
|
|
`settings.ROOT_SUBDOMAIN_ALIASES`.
|
|
|
|
|
|
|
|
For example, with `attr_org_membership` set to `member`, a user with
|
|
|
|
the following attribute in their `AttributeStatement` will have access
|
|
|
|
to the root and `engineering` subdomains:
|
|
|
|
|
|
|
|
```
|
|
|
|
<saml2:Attribute Name="member" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
|
|
|
|
<saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">
|
|
|
|
www
|
|
|
|
</saml2:AttributeValue>
|
|
|
|
<saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">
|
|
|
|
engineering
|
|
|
|
</saml2:AttributeValue>
|
|
|
|
</saml2:Attribute>
|
|
|
|
```
|
|
|
|
|
2018-09-25 23:29:03 +02:00
|
|
|
## LDAP (including Active Directory)
|
|
|
|
|
|
|
|
Zulip supports retrieving information about users via LDAP, and
|
|
|
|
optionally using LDAP as an authentication mechanism.
|
|
|
|
|
|
|
|
In either configuration, you will need to do the following:
|
|
|
|
|
2019-10-29 01:55:37 +01:00
|
|
|
1. These instructions assume you have an installed Zulip server and
|
|
|
|
are logged into a shell there. You can have created an
|
|
|
|
organization already using EmailAuthBackend, or plan to create the
|
|
|
|
organization using LDAP authentication.
|
2018-09-27 22:37:14 +02:00
|
|
|
|
2019-10-29 01:55:37 +01:00
|
|
|
1. Tell Zulip how to connect to your LDAP server:
|
2018-09-25 23:29:03 +02:00
|
|
|
* Fill out the section of your `/etc/zulip/settings.py` headed "LDAP
|
|
|
|
integration, part 1: Connecting to the LDAP server".
|
|
|
|
* If a password is required, put it in
|
|
|
|
`/etc/zulip/zulip-secrets.conf` by setting
|
|
|
|
`auth_ldap_bind_password`. For example: `auth_ldap_bind_password
|
|
|
|
= abcd1234`.
|
|
|
|
|
2019-10-29 01:55:37 +01:00
|
|
|
1. Decide how you want to map the information in your LDAP database to
|
2018-09-27 22:37:14 +02:00
|
|
|
users' account data in Zulip. For each Zulip user, two closely
|
2018-09-26 00:55:51 +02:00
|
|
|
related concepts are:
|
|
|
|
* their **email address**. Zulip needs this in order to send, for
|
|
|
|
example, a notification when they're offline and another user
|
|
|
|
sends a PM.
|
|
|
|
* their **Zulip username**. This means the name the user types into the
|
|
|
|
Zulip login form. You might choose for this to be the user's
|
|
|
|
email address (`sam@example.com`), or look like a traditional
|
|
|
|
"username" (`sam`), or be something else entirely, depending on
|
|
|
|
your environment.
|
|
|
|
|
|
|
|
Either or both of these might be an attribute of the user records
|
|
|
|
in your LDAP database.
|
|
|
|
|
2019-10-29 01:55:37 +01:00
|
|
|
1. Tell Zulip how to map the user information in your LDAP database to
|
2018-12-12 19:46:37 +01:00
|
|
|
the form it needs for authentication. There are three supported
|
|
|
|
ways to set up the username and/or email mapping:
|
2018-09-26 00:55:51 +02:00
|
|
|
|
2019-12-13 21:22:58 +01:00
|
|
|
(A) Using email addresses as Zulip usernames, if LDAP has each
|
|
|
|
user's email address:
|
|
|
|
* Make `AUTH_LDAP_USER_SEARCH` a query by email address.
|
|
|
|
* Set `AUTH_LDAP_REVERSE_EMAIL_SEARCH` to the same query with
|
|
|
|
`%(email)s` rather than `%(user)s` as the search parameter.
|
|
|
|
* Set `AUTH_LDAP_USERNAME_ATTR` to the name of the LDAP
|
|
|
|
attribute for the user's LDAP username in the search result
|
|
|
|
for `AUTH_LDAP_REVERSE_EMAIL_SEARCH`.
|
2018-09-26 00:55:51 +02:00
|
|
|
|
|
|
|
(B) Using LDAP usernames as Zulip usernames, with email addresses
|
2019-12-13 21:22:58 +01:00
|
|
|
formed consistently like `sam` -> `sam@example.com`:
|
|
|
|
* Set `AUTH_LDAP_USER_SEARCH` to query by LDAP username
|
|
|
|
* Set `LDAP_APPEND_DOMAIN = "example.com"`.
|
2018-09-26 00:55:51 +02:00
|
|
|
|
|
|
|
(C) Using LDAP usernames as Zulip usernames, with email addresses
|
2019-12-13 21:22:58 +01:00
|
|
|
taken from some other attribute in LDAP (for example, `mail`):
|
|
|
|
* Set `AUTH_LDAP_USER_SEARCH` to query by LDAP username
|
|
|
|
* Set `LDAP_EMAIL_ATTR = "mail"`.
|
|
|
|
* Set `AUTH_LDAP_REVERSE_EMAIL_SEARCH` to a query that will find
|
|
|
|
an LDAP user given their email address (i.e. a search by
|
|
|
|
`LDAP_EMAIL_ATTR`). For example:
|
|
|
|
```
|
|
|
|
AUTH_LDAP_REVERSE_EMAIL_SEARCH = LDAPSearch("ou=users,dc=example,dc=com",
|
|
|
|
ldap.SCOPE_SUBTREE, "(mail=%(email)s)")
|
|
|
|
```
|
|
|
|
* Set `AUTH_LDAP_USERNAME_ATTR` to the name of the LDAP
|
|
|
|
attribute for the user's LDAP username in that search result.
|
2019-10-05 01:02:46 +02:00
|
|
|
|
2018-09-25 23:29:03 +02:00
|
|
|
You can quickly test whether your configuration works by running:
|
2018-09-27 22:37:14 +02:00
|
|
|
|
2018-09-25 23:29:03 +02:00
|
|
|
```
|
2018-12-13 23:58:26 +01:00
|
|
|
/home/zulip/deployments/current/manage.py query_ldap username
|
2018-09-25 23:29:03 +02:00
|
|
|
```
|
2018-09-27 22:37:14 +02:00
|
|
|
|
|
|
|
from the root of your Zulip installation. If your configuration is
|
|
|
|
working, that will output the full name for your user (and that user's
|
|
|
|
email address, if it isn't the same as the "Zulip username").
|
2018-09-25 23:29:03 +02:00
|
|
|
|
2019-12-13 21:22:58 +01:00
|
|
|
**Active Directory**: Most Active Directory installations will use one
|
|
|
|
of the following configurations:
|
2019-01-09 01:28:51 +01:00
|
|
|
|
|
|
|
* To access by Active Directory username:
|
|
|
|
```
|
|
|
|
AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=users,dc=example,dc=com",
|
|
|
|
ldap.SCOPE_SUBTREE, "(sAMAccountName=%(user)s)")
|
2019-12-13 21:22:58 +01:00
|
|
|
AUTH_LDAP_REVERSE_EMAIL_SEARCH = LDAPSearch("ou=users,dc=example,dc=com",
|
|
|
|
ldap.SCOPE_SUBTREE, "(mail=%(email)s)")
|
2020-01-03 03:33:46 +01:00
|
|
|
AUTH_LDAP_USERNAME_ATTR = "sAMAccountName"
|
2019-01-09 01:28:51 +01:00
|
|
|
```
|
2019-12-13 21:22:58 +01:00
|
|
|
|
2019-01-09 01:28:51 +01:00
|
|
|
* To access by Active Directory email address:
|
|
|
|
```
|
|
|
|
AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=users,dc=example,dc=com",
|
|
|
|
ldap.SCOPE_SUBTREE, "(mail=%(user)s)")
|
2019-12-13 21:22:58 +01:00
|
|
|
AUTH_LDAP_REVERSE_EMAIL_SEARCH = LDAPSearch("ou=users,dc=example,dc=com",
|
|
|
|
ldap.SCOPE_SUBTREE, "(mail=%(email)s)")
|
2020-01-03 03:33:46 +01:00
|
|
|
AUTH_LDAP_USERNAME_ATTR = "mail"
|
2019-01-09 01:28:51 +01:00
|
|
|
```
|
|
|
|
|
2018-09-26 00:05:58 +02:00
|
|
|
**If you are using LDAP for authentication**: you will need to enable
|
|
|
|
the `zproject.backends.ZulipLDAPAuthBackend` auth backend, in
|
2019-12-13 21:22:58 +01:00
|
|
|
`AUTHENTICATION_BACKENDS` in `/etc/zulip/settings.py`. After doing so
|
|
|
|
(and as always [restarting the Zulip server](settings.md) to ensure
|
2018-09-26 00:05:58 +02:00
|
|
|
your settings changes take effect), you should be able to log into
|
|
|
|
Zulip by entering your email address and LDAP password on the Zulip
|
|
|
|
login form.
|
2018-09-25 23:29:03 +02:00
|
|
|
|
2020-01-26 08:38:47 +01:00
|
|
|
You may also want to configure Zulip's settings for [inviting new
|
2020-06-08 23:04:39 +02:00
|
|
|
users](https://zulip.com/help/invite-new-users). If LDAP is the
|
2020-01-26 08:38:47 +01:00
|
|
|
only enabled authentication method, the main use case for Zulip's
|
|
|
|
invitation feature is selecting the initial streams for invited users
|
|
|
|
(invited users will still need to use their LDAP password to create an
|
|
|
|
account).
|
|
|
|
|
2018-12-13 23:58:26 +01:00
|
|
|
### Synchronizing data
|
|
|
|
|
|
|
|
Zulip can automatically synchronize data declared in
|
|
|
|
`AUTH_LDAP_USER_ATTR_MAP` from LDAP into Zulip, via the following
|
|
|
|
management command:
|
|
|
|
|
2018-09-25 23:29:03 +02:00
|
|
|
```
|
2018-12-13 23:58:26 +01:00
|
|
|
/home/zulip/deployments/current/manage.py sync_ldap_user_data
|
2018-09-25 23:29:03 +02:00
|
|
|
```
|
|
|
|
|
2018-12-13 23:58:26 +01:00
|
|
|
This will sync the fields declared in `AUTH_LDAP_USER_ATTR_MAP` for
|
2019-05-28 10:53:49 +02:00
|
|
|
all of your users.
|
2018-12-13 23:58:26 +01:00
|
|
|
|
|
|
|
We recommend running this command in a **regular cron job**, to pick
|
2019-05-28 10:53:49 +02:00
|
|
|
up changes made on your LDAP server.
|
2018-12-13 23:58:26 +01:00
|
|
|
|
2019-02-05 19:37:04 +01:00
|
|
|
All of these data synchronization options have the same model:
|
|
|
|
* New users will be populated automatically with the
|
|
|
|
name/avatar/etc. from LDAP (as configured) on account creation.
|
|
|
|
* The `manage.py sync_ldap_user_data` cron job will automatically
|
|
|
|
update existing users with any changes that were made in LDAP.
|
|
|
|
* You can easily test your configuration using `manage.py query_ldap`.
|
|
|
|
Once you're happy with the configuration, remember to restart the
|
|
|
|
Zulip server with
|
|
|
|
`/home/zulip/deployments/current/scripts/restart-server` so that
|
|
|
|
your configuration changes take effect.
|
|
|
|
|
2018-12-13 23:58:26 +01:00
|
|
|
When using this feature, you may also want to
|
|
|
|
[prevent users from changing their display name in the Zulip UI][restrict-name-changes],
|
|
|
|
since any such changes would be automatically overwritten on the sync
|
|
|
|
run of `manage.py sync_ldap_user_data`.
|
|
|
|
|
2020-06-08 23:04:39 +02:00
|
|
|
[restrict-name-changes]: https://zulip.com/help/restrict-name-and-email-changes
|
2018-12-13 23:58:26 +01:00
|
|
|
|
|
|
|
#### Synchronizing avatars
|
2018-12-12 19:46:37 +01:00
|
|
|
|
|
|
|
Starting with Zulip 2.0, Zulip supports syncing LDAP / Active
|
|
|
|
Directory profile pictures (usually available in the `thumbnailPhoto`
|
|
|
|
or `jpegPhoto` attribute in LDAP) by configuring the `avatar` key in
|
2019-02-05 19:37:04 +01:00
|
|
|
`AUTH_LDAP_USER_ATTR_MAP`.
|
2018-12-12 19:46:37 +01:00
|
|
|
|
2019-02-05 18:14:07 +01:00
|
|
|
#### Synchronizing custom profile fields
|
|
|
|
|
2019-02-05 19:37:04 +01:00
|
|
|
Starting with Zulip 2.0, Zulip supports syncing
|
|
|
|
[custom profile fields][custom-profile-fields] from LDAP / Active
|
|
|
|
Directory. To configure this, you first need to
|
|
|
|
[configure some custom profile fields][custom-profile-fields] for your
|
|
|
|
Zulip organization. Then, define a mapping from the fields you'd like
|
|
|
|
to sync from LDAP to the corresponding LDAP attributes. For example,
|
2019-02-05 18:14:07 +01:00
|
|
|
if you have a custom profile field `LinkedIn Profile` and the
|
2019-02-05 19:37:04 +01:00
|
|
|
corresponding LDAP attribute is `linkedinProfile` then you just need
|
|
|
|
to add `'custom_profile_field__linkedin_profile': 'linkedinProfile'`
|
|
|
|
to the `AUTH_LDAP_USER_ATTR_MAP`.
|
|
|
|
|
2020-06-08 23:04:39 +02:00
|
|
|
[custom-profile-fields]: https://zulip.com/help/add-custom-profile-fields
|
2019-02-05 18:14:07 +01:00
|
|
|
|
2018-12-13 23:58:26 +01:00
|
|
|
#### Automatically deactivating users with Active Directory
|
|
|
|
|
|
|
|
Starting with Zulip 2.0, Zulip supports synchronizing the
|
|
|
|
disabled/deactivated status of users from Active Directory. You can
|
|
|
|
configure this by uncommenting the sample line `"userAccountControl":
|
|
|
|
"userAccountControl",` in `AUTH_LDAP_USER_ATTR_MAP` (and restarting
|
|
|
|
the Zulip server). Zulip will then treat users that are disabled via
|
|
|
|
the "Disable Account" feature in Active Directory as deactivated in
|
|
|
|
Zulip.
|
|
|
|
|
2020-08-11 02:20:10 +02:00
|
|
|
Users disabled in active directory will be immediately unable to log in
|
2018-12-13 23:58:26 +01:00
|
|
|
to Zulip, since Zulip queries the LDAP/Active Directory server on
|
|
|
|
every login attempt. The user will be fully deactivated the next time
|
|
|
|
your `manage.py sync_ldap_user_data` cron job runs (at which point
|
|
|
|
they will be forcefully logged out from all active browser sessions,
|
|
|
|
appear as deactivated in the Zulip UI, etc.).
|
|
|
|
|
|
|
|
This feature works by checking for the `ACCOUNTDISABLE` flag on the
|
|
|
|
`userAccountControl` field in Active Directory. See
|
|
|
|
[this handy resource](https://jackstromberg.com/2013/01/useraccountcontrol-attributeflag-values/)
|
|
|
|
for details on the various `userAccountControl` flags.
|
|
|
|
|
2019-01-13 13:53:52 +01:00
|
|
|
#### Deactivating non-matching users
|
|
|
|
|
|
|
|
Starting with Zulip 2.0, Zulip supports automatically deactivating
|
|
|
|
users if they are not found by the `AUTH_LDAP_USER_SEARCH` query
|
|
|
|
(either because the user is no longer in LDAP/Active Directory, or
|
|
|
|
because the user no longer matches the query). This feature is
|
|
|
|
enabled by default if LDAP is the only authentication backend
|
|
|
|
configured on the Zulip server. Otherwise, you can enable this
|
|
|
|
feature by setting `LDAP_DEACTIVATE_NON_MATCHING_USERS` to `True` in
|
|
|
|
`/etc/zulip/settings.py`. Nonmatching users will be fully deactivated
|
|
|
|
the next time your `manage.py sync_ldap_user_data` cron job runs.
|
|
|
|
|
2018-12-14 01:04:48 +01:00
|
|
|
#### Other fields
|
|
|
|
|
|
|
|
Other fields you may want to sync from LDAP include:
|
|
|
|
|
|
|
|
* Boolean flags; `is_realm_admin` (the organization's administrator
|
|
|
|
permission) is the main one. You can use the
|
|
|
|
[AUTH_LDAP_USER_FLAGS_BY_GROUP][django-auth-booleans] feature of
|
|
|
|
`django-auth-ldap` to configure a group to get this permissions.
|
|
|
|
(We don't recommend using this flags feature for managing
|
2019-06-01 12:35:34 +02:00
|
|
|
`is_active` because deactivating a user this way would not disable
|
2018-12-14 01:04:48 +01:00
|
|
|
any active sessions the user might have; see the above discussion of
|
|
|
|
automatic deactivation for how to do that properly).
|
|
|
|
* String fields like `default_language` (e.g. `en`) or `timezone`, if
|
|
|
|
you have that data in the right format in your LDAP database.
|
|
|
|
* [Coming soon][custom-profile-fields-ldap]: Support for syncing
|
2020-06-08 23:04:39 +02:00
|
|
|
[custom profile fields](https://zulip.com/help/add-custom-profile-fields)
|
2018-12-14 01:04:48 +01:00
|
|
|
from your LDAP database.
|
|
|
|
|
|
|
|
You can look at the [full list of fields][models-py] in the Zulip user
|
|
|
|
model; search for `class UserProfile`, but the above should cover all
|
|
|
|
the fields that would be useful to sync from your LDAP databases.
|
|
|
|
|
|
|
|
[models-py]: https://github.com/zulip/zulip/blob/master/zerver/models.py
|
|
|
|
[django-auth-booleans]: https://django-auth-ldap.readthedocs.io/en/latest/users.html#easy-attributes
|
|
|
|
[custom-profile-fields-ldap]: https://github.com/zulip/zulip/issues/10976
|
|
|
|
|
2018-09-26 00:05:58 +02:00
|
|
|
### Multiple LDAP searches
|
|
|
|
|
|
|
|
To do the union of multiple LDAP searches, use `LDAPSearchUnion`. For example:
|
|
|
|
```
|
|
|
|
AUTH_LDAP_USER_SEARCH = LDAPSearchUnion(
|
|
|
|
LDAPSearch("ou=users,dc=example,dc=com", ldap.SCOPE_SUBTREE, "(uid=%(user)s)"),
|
|
|
|
LDAPSearch("ou=otherusers,dc=example,dc=com", ldap.SCOPE_SUBTREE, "(uid=%(user)s)"),
|
|
|
|
)
|
|
|
|
```
|
|
|
|
|
2019-02-11 20:40:57 +01:00
|
|
|
### Restricting access to an LDAP group
|
|
|
|
|
|
|
|
You can restrict access to your Zulip server to a set of LDAP groups
|
|
|
|
using the `AUTH_LDAP_REQUIRE_GROUP` and `AUTH_LDAP_DENY_GROUP`
|
|
|
|
settings in `/etc/zulip/settings.py`. See the
|
|
|
|
[upstream django-auth-ldap documentation][upstream-ldap-groups] for
|
|
|
|
details.
|
|
|
|
|
|
|
|
[upstream-ldap-groups]: https://django-auth-ldap.readthedocs.io/en/latest/groups.html#limiting-access
|
|
|
|
|
2020-01-03 05:57:47 +01:00
|
|
|
### Troubleshooting
|
|
|
|
|
|
|
|
Most issues with LDAP authentication are caused by misconfigurations of
|
|
|
|
the user and email search settings. Some things you can try to get to
|
|
|
|
the bottom of the problem:
|
|
|
|
|
|
|
|
* Review the instructions for the LDAP configuration type you're
|
|
|
|
using: (A), (B) or (C) (described above), and that you have
|
|
|
|
configured all of the required settings documented in the
|
|
|
|
instructions for that configuration type.
|
|
|
|
* Use the `manage.py query_ldap` tool to verify your configuration.
|
|
|
|
The output of the command will usually indicate the cause of any
|
|
|
|
configuration problem. For the LDAP integration to work, this
|
|
|
|
command should be able to successfully fetch a complete, correct set
|
|
|
|
of data for the queried user.
|
|
|
|
* You can find LDAP-specific logs in `/var/log/zulip/ldap.log`. If
|
|
|
|
you're asking for help with your setup, please provide logs from
|
|
|
|
this file (feel free to anonymize any email addresses to
|
|
|
|
`username@example.com`) in your report.
|
|
|
|
|
2018-01-19 02:43:23 +01:00
|
|
|
## Apache-based SSO with `REMOTE_USER`
|
|
|
|
|
|
|
|
If you have any existing SSO solution where a preferred way to deploy
|
|
|
|
it (a) runs inside Apache, and (b) sets the `REMOTE_USER` environment
|
|
|
|
variable, then the `ZulipRemoteUserBackend` method provides you with a
|
|
|
|
straightforward way to deploy that SSO solution with Zulip.
|
|
|
|
|
|
|
|
### Setup instructions for Apache-based SSO
|
2016-07-13 01:05:04 +02:00
|
|
|
|
2018-01-19 02:43:23 +01:00
|
|
|
1. In `/etc/zulip/settings.py`, configure two settings:
|
2016-07-13 01:05:04 +02:00
|
|
|
|
2018-01-19 02:43:23 +01:00
|
|
|
* `AUTHENTICATION_BACKENDS`: `'zproject.backends.ZulipRemoteUserBackend'`,
|
|
|
|
and no other entries.
|
2016-07-12 23:32:12 +02:00
|
|
|
|
2018-01-19 02:43:23 +01:00
|
|
|
* `SSO_APPEND_DOMAIN`: see documentation in `settings.py`.
|
2016-07-12 23:32:12 +02:00
|
|
|
|
2018-01-19 02:43:23 +01:00
|
|
|
Make sure that you've restarted the Zulip server since making this
|
|
|
|
configuration change.
|
2016-07-12 23:32:12 +02:00
|
|
|
|
2018-01-19 02:43:23 +01:00
|
|
|
2. Edit `/etc/zulip/zulip.conf` and change the `puppet_classes` line to read:
|
2016-07-12 23:32:12 +02:00
|
|
|
|
2018-01-19 02:43:23 +01:00
|
|
|
```
|
2020-10-20 02:49:54 +02:00
|
|
|
puppet_classes = zulip::profile::voyager, zulip::apache_sso
|
2018-01-19 02:43:23 +01:00
|
|
|
```
|
2016-07-12 23:32:12 +02:00
|
|
|
|
2018-01-19 02:43:23 +01:00
|
|
|
3. As root, run `/home/zulip/deployments/current/scripts/zulip-puppet-apply`
|
|
|
|
to install our SSO integration.
|
2016-07-12 23:32:12 +02:00
|
|
|
|
2018-01-19 02:43:23 +01:00
|
|
|
4. To configure our SSO integration, edit a copy of
|
|
|
|
`/etc/apache2/sites-available/zulip-sso.example`, saving the result
|
|
|
|
as `/etc/apache2/sites-available/zulip-sso.conf`. The example sets
|
|
|
|
up HTTP basic auth, with an `htpasswd` file; you'll want to replace
|
|
|
|
that with configuration for your SSO solution to authenticate the
|
|
|
|
user and set `REMOTE_USER`.
|
2016-07-12 23:32:12 +02:00
|
|
|
|
2018-01-19 02:43:23 +01:00
|
|
|
For testing, you may want to move ahead with the rest of the setup
|
|
|
|
using the `htpasswd` example configuration and demonstrate that
|
|
|
|
working end-to-end, before returning later to configure your SSO
|
|
|
|
solution. You can do that with the following steps:
|
|
|
|
```
|
|
|
|
/home/zulip/deployments/current/scripts/restart-server
|
|
|
|
cd /etc/apache2/sites-available/
|
|
|
|
cp zulip-sso.example zulip-sso.conf
|
|
|
|
htpasswd -c /home/zulip/zpasswd username@example.com # prompts for a password
|
|
|
|
```
|
2016-07-12 23:32:12 +02:00
|
|
|
|
2018-01-19 02:43:23 +01:00
|
|
|
5. Run `a2ensite zulip-sso` to enable the SSO integration within Apache.
|
2016-07-12 23:32:12 +02:00
|
|
|
|
2018-01-19 02:43:23 +01:00
|
|
|
6. Run `service apache2 reload` to use your new configuration. If
|
|
|
|
Apache isn't already running, you may need to run `service apache2
|
|
|
|
start` instead.
|
2016-07-12 23:32:12 +02:00
|
|
|
|
2018-01-19 02:43:23 +01:00
|
|
|
Now you should be able to visit your Zulip server in a browser (e.g.,
|
|
|
|
at `https://zulip.example.com/`) and log in via the SSO solution.
|
2016-07-12 23:32:12 +02:00
|
|
|
|
2018-01-19 02:43:23 +01:00
|
|
|
### Troubleshooting Apache-based SSO
|
2016-07-12 23:32:12 +02:00
|
|
|
|
2018-01-19 02:43:23 +01:00
|
|
|
Most issues with this setup tend to be subtle issues with the
|
|
|
|
hostname/DNS side of the configuration. Suggestions for how to
|
|
|
|
improve this SSO setup documentation are very welcome!
|
|
|
|
|
|
|
|
* For example, common issues have to do with `/etc/hosts` not mapping
|
|
|
|
`settings.EXTERNAL_HOST` to the Apache listening on
|
|
|
|
`127.0.0.1`/`localhost`.
|
2016-07-12 23:32:12 +02:00
|
|
|
|
2018-01-19 02:43:23 +01:00
|
|
|
* While debugging, it can often help to temporarily change the Apache
|
|
|
|
config in `/etc/apache2/sites-available/zulip-sso` to listen on all
|
|
|
|
interfaces rather than just `127.0.0.1`.
|
2016-07-12 23:32:12 +02:00
|
|
|
|
2018-01-19 02:43:23 +01:00
|
|
|
* While debugging, it can also be helpful to change `proxy_pass` in
|
|
|
|
`/etc/nginx/zulip-include/app.d/external-sso.conf` to point to a
|
|
|
|
more explicit URL, possibly not over HTTPS.
|
2016-07-12 23:32:12 +02:00
|
|
|
|
2018-01-19 02:43:23 +01:00
|
|
|
* The following log files can be helpful when debugging this setup:
|
2016-07-12 23:32:12 +02:00
|
|
|
|
2018-01-19 02:43:23 +01:00
|
|
|
* `/var/log/zulip/{errors.log,server.log}` (the usual places)
|
|
|
|
* `/var/log/nginx/access.log` (nginx access logs)
|
|
|
|
* `/var/log/apache2/zulip_auth_access.log` (from the
|
|
|
|
`zulip-sso.conf` Apache config file; you may want to change
|
|
|
|
`LogLevel` in that file to "debug" to make this more verbose)
|
2016-07-12 23:32:12 +02:00
|
|
|
|
2018-01-19 02:43:23 +01:00
|
|
|
### Life of an Apache-based SSO login attempt
|
|
|
|
|
|
|
|
Here's a summary of how the Apache `REMOTE_USER` SSO system works,
|
|
|
|
assuming you're using the example configuration with HTTP basic auth.
|
|
|
|
This summary should help with understanding what's going on as you try
|
|
|
|
to debug.
|
2016-07-12 23:32:12 +02:00
|
|
|
|
2016-10-23 02:49:43 +02:00
|
|
|
* Since you've configured `/etc/zulip/settings.py` to only define the
|
2020-08-19 21:55:28 +02:00
|
|
|
`zproject.backends.ZulipRemoteUserBackend`,
|
|
|
|
`zproject/computed_settings.py` configures `/accounts/login/sso/` as
|
|
|
|
`HOME_NOT_LOGGED_IN`. This makes `https://zulip.example.com/`
|
|
|
|
(a.k.a. the homepage for the main Zulip Django app running behind
|
|
|
|
nginx) redirect to `/accounts/login/sso/` for a user that isn't
|
|
|
|
logged in.
|
2016-07-12 23:32:12 +02:00
|
|
|
|
2018-01-19 02:43:23 +01:00
|
|
|
* nginx proxies requests to `/accounts/login/sso/` to an Apache
|
|
|
|
instance listening on `localhost:8888`, via the config in
|
|
|
|
`/etc/nginx/zulip-include/app.d/external-sso.conf` (using the
|
|
|
|
upstream `localhost_sso`, defined in `/etc/nginx/zulip-include/upstreams`).
|
2016-07-12 23:32:12 +02:00
|
|
|
|
2016-10-23 02:49:43 +02:00
|
|
|
* The Apache `zulip-sso` site which you've enabled listens on
|
2018-01-19 02:43:23 +01:00
|
|
|
`localhost:8888` and (in the example config) presents the `htpasswd`
|
|
|
|
dialogue. (In a real configuration, it takes the user through
|
|
|
|
whatever more complex interaction your SSO solution performs.) The
|
|
|
|
user provides correct login information, and the request reaches a
|
|
|
|
second Zulip Django app instance, running behind Apache, with
|
2016-10-23 02:49:43 +02:00
|
|
|
`REMOTE_USER` set. That request is served by
|
|
|
|
`zerver.views.remote_user_sso`, which just checks the `REMOTE_USER`
|
2018-01-19 02:43:23 +01:00
|
|
|
variable and either logs the user in or, if they don't have an
|
|
|
|
account already, registers them. The login sets a cookie.
|
2016-07-12 23:32:12 +02:00
|
|
|
|
2018-01-19 02:43:23 +01:00
|
|
|
* After succeeding, that redirects the user back to `/` on port 443.
|
|
|
|
This request is sent by nginx to the main Zulip Django app, which
|
|
|
|
sees the cookie, treats them as logged in, and proceeds to serve
|
|
|
|
them the main app page normally.
|
2016-07-12 23:32:12 +02:00
|
|
|
|
2020-06-09 12:04:21 +02:00
|
|
|
## Sign in with Apple
|
|
|
|
|
|
|
|
Zulip supports using the web flow for Sign in with Apple on
|
|
|
|
self-hosted servers. To do so, you'll need to do the following:
|
|
|
|
|
|
|
|
1. Visit [the Apple Developer site][apple-developer] and [Create a
|
|
|
|
Services ID.][apple-create-services-id]. When prompted for a "Return
|
|
|
|
URL", enter `https://zulip.example.com/complete/apple/` (using the
|
|
|
|
domain for your server).
|
|
|
|
|
|
|
|
1. Create a [Sign in with Apple private key][apple-create-private-key].
|
|
|
|
|
|
|
|
1. Store the resulting private key at
|
2020-06-25 13:26:17 +02:00
|
|
|
`/etc/zulip/apple-auth-key.p8`. Be sure to set
|
2020-06-09 12:04:21 +02:00
|
|
|
permissions correctly:
|
|
|
|
|
|
|
|
```
|
2020-06-25 13:26:17 +02:00
|
|
|
chown zulip:zulip /etc/zulip/apple-auth-key.p8
|
|
|
|
chmod 640 /etc/zulip/apple-auth-key.p8
|
2020-06-09 12:04:21 +02:00
|
|
|
```
|
|
|
|
|
2020-06-24 03:38:08 +02:00
|
|
|
1. Configure Apple authentication in `/etc/zulip/settings.py`:
|
|
|
|
* `SOCIAL_AUTH_APPLE_TEAM`: Your Team ID from Apple, which is a
|
|
|
|
string like "A1B2C3D4E5".
|
|
|
|
* `SOCIAL_AUTH_APPLE_SERVICES_ID`: The Services ID you created in
|
|
|
|
step 1, which might look like "com.example.services".
|
2020-06-26 12:22:48 +02:00
|
|
|
* `SOCIAL_AUTH_APPLE_APP_ID`: The App ID, or Bundle ID, of your
|
2020-06-24 03:38:08 +02:00
|
|
|
app that you used in step 1 to configure your Services ID.
|
|
|
|
This might look like "com.example.app".
|
|
|
|
* `SOCIAL_AUTH_APPLE_KEY`: Despite the name this is not a key, but
|
|
|
|
rather the Key ID of the key you created in step 2. This looks
|
|
|
|
like "F6G7H8I9J0".
|
|
|
|
* `AUTHENTICATION_BACKENDS`: Uncomment (or add) a line like
|
|
|
|
`'zproject.backends.AppleAuthBackend',` to enable Apple auth
|
|
|
|
using the created configuration.
|
2020-06-09 12:04:21 +02:00
|
|
|
|
2020-06-24 04:01:16 +02:00
|
|
|
1. Register with Apple the email addresses or domains your Zulip
|
|
|
|
server sends email to users from. For instructions and background,
|
|
|
|
see the "Email Relay Service" subsection of
|
|
|
|
[this page][apple-get-started]. For details on what email
|
|
|
|
addresses Zulip sends from, see our
|
|
|
|
[outgoing email documentation][outgoing-email].
|
2020-06-09 12:04:21 +02:00
|
|
|
|
|
|
|
[apple-create-services-id]: https://help.apple.com/developer-account/?lang=en#/dev1c0e25352
|
|
|
|
[apple-developer]: https://developer.apple.com/account/resources/
|
|
|
|
[apple-create-private-key]: https://help.apple.com/developer-account/?lang=en#/dev77c875b7e
|
|
|
|
[apple-get-started]: https://developer.apple.com/sign-in-with-apple/get-started/
|
|
|
|
[outgoing-email]: ../production/email.md
|
|
|
|
|
2018-01-19 02:43:23 +01:00
|
|
|
## Adding more authentication backends
|
|
|
|
|
2018-12-19 01:45:57 +01:00
|
|
|
Adding an integration with any of the more than 100 authentication
|
|
|
|
providers supported by [python-social-auth][python-social-auth] (e.g.,
|
2018-01-19 02:43:23 +01:00
|
|
|
Facebook, Twitter, etc.) is easy to do if you're willing to write a
|
|
|
|
bit of code, and pull requests to add new backends are welcome.
|
|
|
|
|
2018-12-19 01:45:57 +01:00
|
|
|
For example, the
|
|
|
|
[Azure Active Directory integration](https://github.com/zulip/zulip/commit/49dbd85a8985b12666087f9ea36acb6f7da0aa4f)
|
|
|
|
was about 30 lines of code, plus some documentation and an
|
|
|
|
[automatically generated migration][schema-migrations]. We also have
|
|
|
|
helpful developer documentation on
|
2019-11-07 18:29:05 +01:00
|
|
|
[testing auth backends](../development/authentication.md).
|
2018-01-19 02:43:23 +01:00
|
|
|
|
2019-09-30 19:37:56 +02:00
|
|
|
[schema-migrations]: ../subsystems/schema-migrations.md
|
2018-01-19 02:43:23 +01:00
|
|
|
[python-social-auth]: https://python-social-auth.readthedocs.io/en/latest/
|
|
|
|
|
|
|
|
## Development only
|
|
|
|
|
|
|
|
The `DevAuthBackend` method is used only in development, to allow
|
|
|
|
passwordless login as any user in a development environment. It's
|
|
|
|
mentioned on this page only for completeness.
|