mirror of https://github.com/zulip/zulip.git
auth: Add support for GitLab authentication.
With some tweaks by tabbott to the documentation and comments. Fixes #13694.
This commit is contained in:
parent
4cb94bf483
commit
4304d5f8db
|
@ -72,6 +72,17 @@ details worth understanding:
|
|||
`social_auth_github_key` to the client ID and `social_auth_github_secret`
|
||||
to the client secret.
|
||||
|
||||
### GitLab
|
||||
|
||||
* Register an OAuth application with GitLab at
|
||||
https://gitlab.com/oauth/applications.
|
||||
Specify `http://zulipdev.com:9991/complete/gitlab` as the callback URL.
|
||||
|
||||
* You should get a page containing the Application ID and Secret for
|
||||
your new application. In `dev-secrets.conf`, enter the Application
|
||||
ID as `social_auth_gitlab_key` and the Secret as
|
||||
`social_auth_gitlab_secret`.
|
||||
|
||||
### SAML
|
||||
|
||||
* Sign up for a [developer Okta account](https://developer.okta.com/).
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 8.7 KiB |
|
@ -74,7 +74,20 @@
|
|||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if google_error or github_error %}
|
||||
{% if gitlab_error %}
|
||||
{% if development_environment %}
|
||||
{{ render_markdown_path('zerver/gitlab-error.md', {"root_domain_uri": root_domain_uri, "settings_path": secrets_path, "secrets_path": secrets_path, "client_id_key_name": "social_auth_gitlab_key"}) }}
|
||||
<p>
|
||||
For more information, have a look at
|
||||
the <a href="https://zulip.readthedocs.io/en/latest/development/authentication.html#gitlab">authentication
|
||||
setup guide</a> for the development environment.
|
||||
</p>
|
||||
{% else %}
|
||||
{{ render_markdown_path('zerver/gitlab-error.md', {"root_domain_uri": root_domain_uri, "settings_path": settings_path, "secrets_path": secrets_path, "client_id_key_name": "SOCIAL_AUTH_GITLAB_KEY"}) }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if google_error or github_error or gitlab_error %}
|
||||
{% if not development_environment %}
|
||||
<p>
|
||||
For more information, have a look at
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
You are using the **GitLab auth backend**, but it is not properly
|
||||
configured. Please check the following:
|
||||
|
||||
* You have added `{{ root_domain_uri }}/complete/gitlab/` as the callback
|
||||
URL in the OAuth application in GitLab. You can register OAuth apps at
|
||||
[GitLab Applications](https://gitlab.com/profile/applications).
|
||||
|
||||
* You have set `{{ client_id_key_name }}` in `{{ settings_path }}` and
|
||||
`social_auth_gitlab_secret` in `{{ secrets_path }}` with the values
|
||||
from your OAuth application.
|
||||
|
||||
* Navigate back to the login page and attempt the GitLab auth flow again.
|
|
@ -0,0 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.26 on 2020-01-29 17:30
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import bitfield.models
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('zerver', '0268_add_userpresence_realm_timestamp_index'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='realm',
|
||||
name='authentication_methods',
|
||||
field=bitfield.models.BitField(['Google', 'Email', 'GitHub', 'LDAP', 'Dev', 'RemoteUser', 'AzureAD', 'SAML', 'GitLab'], default=2147483647),
|
||||
),
|
||||
]
|
|
@ -136,7 +136,7 @@ class Realm(models.Model):
|
|||
INVITES_STANDARD_REALM_DAILY_MAX = 3000
|
||||
MESSAGE_VISIBILITY_LIMITED = 10000
|
||||
AUTHENTICATION_FLAGS = [u'Google', u'Email', u'GitHub', u'LDAP', u'Dev',
|
||||
u'RemoteUser', u'AzureAD', u'SAML']
|
||||
u'RemoteUser', u'AzureAD', u'SAML', u'GitLab']
|
||||
SUBDOMAIN_FOR_ROOT_DOMAIN = ''
|
||||
|
||||
# User-visible display name and description used on e.g. the organization homepage
|
||||
|
|
|
@ -58,9 +58,9 @@ from confirmation.models import Confirmation, create_confirmation_link
|
|||
|
||||
from zproject.backends import ZulipDummyBackend, EmailAuthBackend, \
|
||||
GoogleAuthBackend, ZulipRemoteUserBackend, ZulipLDAPAuthBackend, \
|
||||
ZulipLDAPUserPopulator, DevAuthBackend, GitHubAuthBackend, ZulipAuthMixin, \
|
||||
dev_auth_enabled, password_auth_enabled, github_auth_enabled, google_auth_enabled, \
|
||||
require_email_format_usernames, AUTH_BACKEND_NAME_MAP, \
|
||||
ZulipLDAPUserPopulator, DevAuthBackend, GitHubAuthBackend, GitLabAuthBackend, ZulipAuthMixin, \
|
||||
dev_auth_enabled, password_auth_enabled, github_auth_enabled, gitlab_auth_enabled, \
|
||||
google_auth_enabled, require_email_format_usernames, AUTH_BACKEND_NAME_MAP, \
|
||||
ZulipLDAPConfigurationError, ZulipLDAPExceptionNoMatchingLDAPUser, ZulipLDAPExceptionOutsideDomain, \
|
||||
ZulipLDAPException, query_ldap, sync_user_from_ldap, SocialAuthMixin, \
|
||||
PopulateUserLDAPError, SAMLAuthBackend, saml_auth_enabled, email_belongs_to_ldap, \
|
||||
|
@ -1695,6 +1695,26 @@ class GitHubAuthBackendTest(SocialAuthBase):
|
|||
mock_warning.assert_called_once_with("Social auth (GitHub) failed because user has no verified"
|
||||
" emails associated with the account")
|
||||
|
||||
class GitLabAuthBackendTest(SocialAuthBase):
|
||||
__unittest_skip__ = False
|
||||
|
||||
BACKEND_CLASS = GitLabAuthBackend
|
||||
CLIENT_KEY_SETTING = "SOCIAL_AUTH_GITLAB_KEY"
|
||||
LOGIN_URL = "/accounts/login/social/gitlab"
|
||||
SIGNUP_URL = "/accounts/register/social/gitlab"
|
||||
AUTHORIZATION_URL = "https://gitlab.com/oauth/authorize"
|
||||
ACCESS_TOKEN_URL = "https://gitlab.com/oauth/token"
|
||||
USER_INFO_URL = "https://gitlab.com/api/v4/user"
|
||||
AUTH_FINISH_URL = "/complete/gitlab/"
|
||||
CONFIG_ERROR_URL = "/config-error/gitlab"
|
||||
|
||||
def test_gitlab_auth_enabled(self) -> None:
|
||||
with self.settings(AUTHENTICATION_BACKENDS=('zproject.backends.GitLabAuthBackend',)):
|
||||
self.assertTrue(gitlab_auth_enabled())
|
||||
|
||||
def get_account_data_dict(self, email: str, name: str) -> Dict[str, Any]:
|
||||
return dict(email=email, name=name, email_verified=True)
|
||||
|
||||
class GoogleAuthBackendTest(SocialAuthBase):
|
||||
__unittest_skip__ = False
|
||||
|
||||
|
|
|
@ -413,6 +413,36 @@ class ConfigErrorTest(ZulipTestCase):
|
|||
self.assert_not_in_success_response(["zproject/dev_settings.py"], result)
|
||||
self.assert_not_in_success_response(["zproject/dev-secrets.conf"], result)
|
||||
|
||||
@override_settings(SOCIAL_AUTH_GITLAB_KEY=None)
|
||||
def test_gitlab(self) -> None:
|
||||
result = self.client_get("/accounts/login/social/gitlab")
|
||||
self.assertEqual(result.status_code, 302)
|
||||
self.assertEqual(result.url, '/config-error/gitlab')
|
||||
result = self.client_get(result.url)
|
||||
self.assert_in_success_response(["social_auth_gitlab_key"], result)
|
||||
self.assert_in_success_response(["social_auth_gitlab_secret"], result)
|
||||
self.assert_in_success_response(["zproject/dev-secrets.conf"], result)
|
||||
self.assert_not_in_success_response(["SOCIAL_AUTH_GITLAB_KEY"], result)
|
||||
self.assert_not_in_success_response(["zproject/dev_settings.py"], result)
|
||||
self.assert_not_in_success_response(["/etc/zulip/settings.py"], result)
|
||||
self.assert_not_in_success_response(["/etc/zulip/zulip-secrets.conf"], result)
|
||||
|
||||
@override_settings(SOCIAL_AUTH_GITLAB_KEY=None)
|
||||
@override_settings(DEVELOPMENT=False)
|
||||
def test_gitlab_production_error(self) -> None:
|
||||
"""Test the !DEVELOPMENT code path of config-error."""
|
||||
result = self.client_get("/accounts/login/social/gitlab")
|
||||
self.assertEqual(result.status_code, 302)
|
||||
self.assertEqual(result.url, '/config-error/gitlab')
|
||||
result = self.client_get(result.url)
|
||||
self.assert_in_success_response(["SOCIAL_AUTH_GITLAB_KEY"], result)
|
||||
self.assert_in_success_response(["/etc/zulip/settings.py"], result)
|
||||
self.assert_in_success_response(["social_auth_gitlab_secret"], result)
|
||||
self.assert_in_success_response(["/etc/zulip/zulip-secrets.conf"], result)
|
||||
self.assert_not_in_success_response(["social_auth_gitlab_key"], result)
|
||||
self.assert_not_in_success_response(["zproject/dev_settings.py"], result)
|
||||
self.assert_not_in_success_response(["zproject/dev-secrets.conf"], result)
|
||||
|
||||
@override_settings(SOCIAL_AUTH_SAML_ENABLED_IDPS=None)
|
||||
def test_saml_error(self) -> None:
|
||||
result = self.client_get("/accounts/login/social/saml")
|
||||
|
|
|
@ -429,6 +429,9 @@ def start_social_login(request: HttpRequest, backend: str, extra_arg: Optional[s
|
|||
if (backend == "google") and not (settings.SOCIAL_AUTH_GOOGLE_KEY and
|
||||
settings.SOCIAL_AUTH_GOOGLE_SECRET):
|
||||
return redirect_to_config_error("google")
|
||||
if (backend == "gitlab") and not (settings.SOCIAL_AUTH_GITLAB_KEY and
|
||||
settings.SOCIAL_AUTH_GITLAB_SECRET):
|
||||
return redirect_to_config_error("gitlab")
|
||||
# TODO: Add a similar block for AzureAD.
|
||||
|
||||
return oauth_redirect_to_root(request, backend_url, 'social', extra_url_params=extra_url_params)
|
||||
|
|
|
@ -39,6 +39,7 @@ from onelogin.saml2.errors import OneLogin_Saml2_Error
|
|||
from social_core.backends.github import GithubOAuth2, GithubOrganizationOAuth2, \
|
||||
GithubTeamOAuth2
|
||||
from social_core.backends.azuread import AzureADOAuth2
|
||||
from social_core.backends.gitlab import GitLabOAuth2
|
||||
from social_core.backends.base import BaseAuth
|
||||
from social_core.backends.google import GoogleOAuth2
|
||||
from social_core.backends.saml import SAMLAuth
|
||||
|
@ -110,6 +111,9 @@ def google_auth_enabled(realm: Optional[Realm]=None) -> bool:
|
|||
def github_auth_enabled(realm: Optional[Realm]=None) -> bool:
|
||||
return auth_enabled_helper(['GitHub'], realm)
|
||||
|
||||
def gitlab_auth_enabled(realm: Optional[Realm]=None) -> bool:
|
||||
return auth_enabled_helper(['GitLab'], realm)
|
||||
|
||||
def saml_auth_enabled(realm: Optional[Realm]=None) -> bool:
|
||||
return auth_enabled_helper(['SAML'], realm)
|
||||
|
||||
|
@ -1324,6 +1328,21 @@ class AzureADAuthBackend(SocialAuthMixin, AzureADOAuth2):
|
|||
auth_backend_name = "AzureAD"
|
||||
display_icon = "/static/images/landing-page/logos/azuread-icon.png"
|
||||
|
||||
@external_auth_method
|
||||
class GitLabAuthBackend(SocialAuthMixin, GitLabOAuth2):
|
||||
sort_order = 75
|
||||
name = "gitlab"
|
||||
auth_backend_name = "GitLab"
|
||||
display_icon = "/static/images/landing-page/logos/gitlab-icon.png"
|
||||
|
||||
# Note: GitLab as of early 2020 supports having multiple email
|
||||
# addresses connected with a GitLab account, and we could access
|
||||
# those emails, but its APIs don't indicate which of those email
|
||||
# addresses were verified, so we cannot use them for
|
||||
# authentication like we do for the GitHub integration. Instead,
|
||||
# we just use the primary email address, which is always verified.
|
||||
# (No code is required to do so, as that's the default behavior).
|
||||
|
||||
@external_auth_method
|
||||
class GoogleAuthBackend(SocialAuthMixin, GoogleOAuth2):
|
||||
sort_order = 150
|
||||
|
|
|
@ -50,6 +50,7 @@ FAKE_LDAP_NUM_USERS = 8
|
|||
SOCIAL_AUTH_GITHUB_KEY = get_secret('social_auth_github_key', development_only=True)
|
||||
SOCIAL_AUTH_GITHUB_ORG_NAME = None # type: Optional[str]
|
||||
SOCIAL_AUTH_GITHUB_TEAM_ID = None # type: Optional[str]
|
||||
SOCIAL_AUTH_GITLAB_KEY = get_secret('social_auth_gitlab_key')
|
||||
SOCIAL_AUTH_SUBDOMAIN = None # type: Optional[str]
|
||||
SOCIAL_AUTH_AZUREAD_OAUTH2_SECRET = get_secret('azure_oauth2_secret')
|
||||
SOCIAL_AUTH_GOOGLE_KEY = get_secret('social_auth_google_key', development_only=True)
|
||||
|
|
|
@ -47,6 +47,7 @@ AUTHENTICATION_BACKENDS = (
|
|||
'zproject.backends.GoogleAuthBackend',
|
||||
'zproject.backends.SAMLAuthBackend',
|
||||
# 'zproject.backends.AzureADAuthBackend',
|
||||
'zproject.backends.GitLabAuthBackend',
|
||||
)
|
||||
|
||||
EXTERNAL_URI_SCHEME = "http://"
|
||||
|
|
|
@ -119,6 +119,7 @@ AUTHENTICATION_BACKENDS = (
|
|||
'zproject.backends.EmailAuthBackend', # Email and password; just requires SMTP setup
|
||||
# 'zproject.backends.GoogleAuthBackend', # Google auth, setup below
|
||||
# 'zproject.backends.GitHubAuthBackend', # GitHub auth, setup below
|
||||
# 'zproject.backends.GitLabAuthBackend', # GitLab auth, setup below
|
||||
# 'zproject.backends.AzureADAuthBackend', # Microsoft Azure Active Directory auth, setup below
|
||||
# 'zproject.backends.SAMLAuthBackend', # SAML, setup below
|
||||
# 'zproject.backends.ZulipLDAPAuthBackend', # LDAP, setup below
|
||||
|
@ -148,6 +149,27 @@ AUTHENTICATION_BACKENDS = (
|
|||
# client secret in zulip-secrets.conf as `social_auth_google_secret`.
|
||||
#SOCIAL_AUTH_GOOGLE_KEY = <your client ID from Google>
|
||||
|
||||
#######
|
||||
# GitLab OAuth.
|
||||
#
|
||||
# To set up GitLab authentication, you'll need to do the following:
|
||||
#
|
||||
# (1) Register an OAuth application with GitLab at
|
||||
# https://gitlab.com/oauth/applications
|
||||
# Or the equivalent URL on a self-hosted GitLab server.
|
||||
# (2) Fill in the "Redirect URI" with a value like
|
||||
# http://zulip.example.com/complete/gitlab/
|
||||
# based on your value for EXTERNAL_HOST.
|
||||
# (3) For "scopes", select only "read_user", and create the application.
|
||||
# (4) You'll end up on a page with the Application ID and Secret for
|
||||
# your new GitLab Application. Use the Application ID as
|
||||
# `SOCIAL_AUTH_GITLAB_KEY` here, and put the Secret in
|
||||
# zulip-secrets.conf as `social_auth_gitlab_secret`.
|
||||
# (5) If you are self-hosting GitLab, provide the URL of the
|
||||
# GitLab server as SOCIAL_AUTH_GITLAB_API_URL here.
|
||||
#SOCIAL_AUTH_GITLAB_KEY = <your Application ID from GitLab>
|
||||
#SOCIAL_AUTH_GITLAB_API_URL = https://gitlab.example.com
|
||||
|
||||
########
|
||||
# GitHub OAuth.
|
||||
#
|
||||
|
|
|
@ -982,6 +982,7 @@ SOCIAL_AUTH_FIELDS_STORED_IN_SESSION = ['subdomain', 'is_signup', 'mobile_flow_o
|
|||
SOCIAL_AUTH_LOGIN_ERROR_URL = '/login/'
|
||||
|
||||
SOCIAL_AUTH_GITHUB_SECRET = get_secret('social_auth_github_secret')
|
||||
SOCIAL_AUTH_GITLAB_SECRET = get_secret('social_auth_gitlab_secret')
|
||||
SOCIAL_AUTH_GITHUB_SCOPE = ['user:email']
|
||||
SOCIAL_AUTH_GITHUB_ORG_KEY = SOCIAL_AUTH_GITHUB_KEY
|
||||
SOCIAL_AUTH_GITHUB_ORG_SECRET = SOCIAL_AUTH_GITHUB_SECRET
|
||||
|
|
|
@ -164,6 +164,8 @@ GOOGLE_OAUTH2_CLIENT_SECRET = "secret"
|
|||
|
||||
SOCIAL_AUTH_GITHUB_KEY = "key"
|
||||
SOCIAL_AUTH_GITHUB_SECRET = "secret"
|
||||
SOCIAL_AUTH_GITLAB_KEY = "key"
|
||||
SOCIAL_AUTH_GITLAB_SECRET = "secret"
|
||||
SOCIAL_AUTH_GOOGLE_KEY = "key"
|
||||
SOCIAL_AUTH_GOOGLE_SECRET = "secret"
|
||||
SOCIAL_AUTH_SUBDOMAIN = 'www'
|
||||
|
|
|
@ -572,6 +572,9 @@ i18n_urls = [
|
|||
url(r'^config-error/github$', TemplateView.as_view(
|
||||
template_name='zerver/config_error.html',),
|
||||
{'github_error': True},),
|
||||
url(r'^config-error/gitlab$', TemplateView.as_view(
|
||||
template_name='zerver/config_error.html',),
|
||||
{'gitlab_error': True},),
|
||||
url(r'^config-error/smtp$', TemplateView.as_view(
|
||||
template_name='zerver/config_error.html',),
|
||||
{'smtp_error': True},),
|
||||
|
|
Loading…
Reference in New Issue