mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +00:00 
			
		
		
		
	auth: Add support for Azure Active Directory authentication.
This takes advantage of all of our work on making the python-social-auth integration reusable for other authentication backends.
This commit is contained in:
		@@ -28,6 +28,7 @@ authenticate users with any of several single-sign-on (SSO)
 | 
				
			|||||||
authentication providers:
 | 
					authentication providers:
 | 
				
			||||||
* Google accounts, with `GoogleMobileOauth2Backend`
 | 
					* Google accounts, with `GoogleMobileOauth2Backend`
 | 
				
			||||||
* GitHub accounts, with `GitHubAuthBackend`
 | 
					* GitHub accounts, with `GitHubAuthBackend`
 | 
				
			||||||
 | 
					* Microsoft Azure Active Directory, with `AzureADAuthBackend`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Each of these requires one to a handful of lines of configuration in
 | 
					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
 | 
					`settings.py`, as well as a secret in `zulip-secrets.conf`.  Details
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -635,6 +635,16 @@ button.login-google-button {
 | 
				
			|||||||
    transform: translateX(15px) translateY(13px);
 | 
					    transform: translateX(15px) translateY(13px);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.azure-wrapper::before {
 | 
				
			||||||
 | 
					    content: "\f17a";
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    font-family: "FontAwesome";
 | 
				
			||||||
 | 
					    font-size: 2rem;
 | 
				
			||||||
 | 
					    color: hsl(0, 0%, 20%);
 | 
				
			||||||
 | 
					    transform: translateX(15px) translateY(13px);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.login-page-container .right-side .actions,
 | 
					.login-page-container .right-side .actions,
 | 
				
			||||||
.forgot-password-container .actions {
 | 
					.forgot-password-container .actions {
 | 
				
			||||||
    margin: 20px 0px 0px;
 | 
					    margin: 20px 0px 0px;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -154,6 +154,17 @@
 | 
				
			|||||||
                            </div>
 | 
					                            </div>
 | 
				
			||||||
                            {% endif %}
 | 
					                            {% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            {% if azuread_auth_enabled %}
 | 
				
			||||||
 | 
					                            <div class="login-social">
 | 
				
			||||||
 | 
					                                <form id='azure_login_form' class="form-inline azure-wrapper" action="{{ url('login-social', args=('azuread-oauth2',)) }}" method="get">
 | 
				
			||||||
 | 
					                                    <input type="hidden" name="next" value="{{ next }}">
 | 
				
			||||||
 | 
					                                    <button class="login-social-button">
 | 
				
			||||||
 | 
					                                        {{ _('Log in with %(identity_provider)s', identity_provider="Azure AD") }}
 | 
				
			||||||
 | 
					                                    </button>
 | 
				
			||||||
 | 
					                                </form>
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                            {% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            <div class="actions">
 | 
					                            <div class="actions">
 | 
				
			||||||
                                {% if email_auth_enabled %}
 | 
					                                {% if email_auth_enabled %}
 | 
				
			||||||
                                <a class="forgot-password" href="/accounts/password/reset/">{{ _('Forgot your password?') }}</a>
 | 
					                                <a class="forgot-password" href="/accounts/password/reset/">{{ _('Forgot your password?') }}</a>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										21
									
								
								zerver/migrations/0197_azure_active_directory_auth.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								zerver/migrations/0197_azure_active_directory_auth.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					# -*- coding: utf-8 -*-
 | 
				
			||||||
 | 
					# Generated by Django 1.11.14 on 2018-10-11 00:12
 | 
				
			||||||
 | 
					from __future__ import unicode_literals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import bitfield.models
 | 
				
			||||||
 | 
					from django.db import migrations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dependencies = [
 | 
				
			||||||
 | 
					        ('zerver', '0196_add_realm_logo_fields'),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    operations = [
 | 
				
			||||||
 | 
					        migrations.AlterField(
 | 
				
			||||||
 | 
					            model_name='realm',
 | 
				
			||||||
 | 
					            name='authentication_methods',
 | 
				
			||||||
 | 
					            field=bitfield.models.BitField(['Google', 'Email', 'GitHub', 'LDAP', 'Dev', 'RemoteUser', 'AzureAD'], default=2147483647),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
@@ -145,7 +145,7 @@ class Realm(models.Model):
 | 
				
			|||||||
    INVITES_STANDARD_REALM_DAILY_MAX = 3000
 | 
					    INVITES_STANDARD_REALM_DAILY_MAX = 3000
 | 
				
			||||||
    MESSAGE_VISIBILITY_LIMITED = 10000
 | 
					    MESSAGE_VISIBILITY_LIMITED = 10000
 | 
				
			||||||
    VIDEO_CHAT_PROVIDERS = [u"Jitsi", u"Google Hangouts"]
 | 
					    VIDEO_CHAT_PROVIDERS = [u"Jitsi", u"Google Hangouts"]
 | 
				
			||||||
    AUTHENTICATION_FLAGS = [u'Google', u'Email', u'GitHub', u'LDAP', u'Dev', u'RemoteUser']
 | 
					    AUTHENTICATION_FLAGS = [u'Google', u'Email', u'GitHub', u'LDAP', u'Dev', u'RemoteUser', u'AzureAD']
 | 
				
			||||||
    SUBDOMAIN_FOR_ROOT_DOMAIN = ''
 | 
					    SUBDOMAIN_FOR_ROOT_DOMAIN = ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # User-visible display name and description used on e.g. the organization homepage
 | 
					    # User-visible display name and description used on e.g. the organization homepage
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -319,6 +319,7 @@ def start_social_login(request: HttpRequest, backend: str) -> HttpResponse:
 | 
				
			|||||||
    if (backend == "github") and not (settings.SOCIAL_AUTH_GITHUB_KEY and
 | 
					    if (backend == "github") and not (settings.SOCIAL_AUTH_GITHUB_KEY and
 | 
				
			||||||
                                      settings.SOCIAL_AUTH_GITHUB_SECRET):
 | 
					                                      settings.SOCIAL_AUTH_GITHUB_SECRET):
 | 
				
			||||||
        return redirect_to_config_error("github")
 | 
					        return redirect_to_config_error("github")
 | 
				
			||||||
 | 
					    # TODO: Add a similar block of AzureAD.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return oauth_redirect_to_root(request, backend_url, 'social')
 | 
					    return oauth_redirect_to_root(request, backend_url, 'social')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,7 @@ from django.http import HttpResponse
 | 
				
			|||||||
from requests import HTTPError
 | 
					from requests import HTTPError
 | 
				
			||||||
from social_core.backends.github import GithubOAuth2, GithubOrganizationOAuth2, \
 | 
					from social_core.backends.github import GithubOAuth2, GithubOrganizationOAuth2, \
 | 
				
			||||||
    GithubTeamOAuth2
 | 
					    GithubTeamOAuth2
 | 
				
			||||||
 | 
					from social_core.backends.azuread import AzureADOAuth2
 | 
				
			||||||
from social_core.backends.base import BaseAuth
 | 
					from social_core.backends.base import BaseAuth
 | 
				
			||||||
from social_core.backends.oauth import BaseOAuth2
 | 
					from social_core.backends.oauth import BaseOAuth2
 | 
				
			||||||
from social_core.utils import handle_http_errors
 | 
					from social_core.utils import handle_http_errors
 | 
				
			||||||
@@ -66,6 +67,9 @@ def google_auth_enabled(realm: Optional[Realm]=None) -> bool:
 | 
				
			|||||||
def github_auth_enabled(realm: Optional[Realm]=None) -> bool:
 | 
					def github_auth_enabled(realm: Optional[Realm]=None) -> bool:
 | 
				
			||||||
    return auth_enabled_helper(['GitHub'], realm)
 | 
					    return auth_enabled_helper(['GitHub'], realm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def azuread_auth_enabled(realm: Optional[Realm]=None) -> bool:
 | 
				
			||||||
 | 
					    return auth_enabled_helper(['AzureAD'], realm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def remote_auth_enabled(realm: Optional[Realm]=None) -> bool:
 | 
					def remote_auth_enabled(realm: Optional[Realm]=None) -> bool:
 | 
				
			||||||
    return auth_enabled_helper(['RemoteUser'], realm)
 | 
					    return auth_enabled_helper(['RemoteUser'], realm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -645,6 +649,9 @@ class GitHubAuthBackend(SocialAuthMixin, GithubOAuth2):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        raise AssertionError("Invalid configuration")
 | 
					        raise AssertionError("Invalid configuration")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AzureADAuthBackend(SocialAuthMixin, AzureADOAuth2):
 | 
				
			||||||
 | 
					    auth_backend_name = "AzureAD"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AUTH_BACKEND_NAME_MAP = {
 | 
					AUTH_BACKEND_NAME_MAP = {
 | 
				
			||||||
    'Dev': DevAuthBackend,
 | 
					    'Dev': DevAuthBackend,
 | 
				
			||||||
    'Email': EmailAuthBackend,
 | 
					    'Email': EmailAuthBackend,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,6 +39,7 @@ AUTHENTICATION_BACKENDS = (
 | 
				
			|||||||
    'zproject.backends.EmailAuthBackend',
 | 
					    'zproject.backends.EmailAuthBackend',
 | 
				
			||||||
    'zproject.backends.GitHubAuthBackend',
 | 
					    'zproject.backends.GitHubAuthBackend',
 | 
				
			||||||
    'zproject.backends.GoogleMobileOauth2Backend',
 | 
					    'zproject.backends.GoogleMobileOauth2Backend',
 | 
				
			||||||
 | 
					    # 'zproject.backends.AzureADAuthBackend',
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EXTERNAL_URI_SCHEME = "http://"
 | 
					EXTERNAL_URI_SCHEME = "http://"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -114,6 +114,7 @@ AUTHENTICATION_BACKENDS = (
 | 
				
			|||||||
    'zproject.backends.EmailAuthBackend',  # Email and password; just requires SMTP setup
 | 
					    'zproject.backends.EmailAuthBackend',  # Email and password; just requires SMTP setup
 | 
				
			||||||
    # 'zproject.backends.GoogleMobileOauth2Backend',  # Google Apps, setup below
 | 
					    # 'zproject.backends.GoogleMobileOauth2Backend',  # Google Apps, setup below
 | 
				
			||||||
    # 'zproject.backends.GitHubAuthBackend',  # GitHub auth, setup below
 | 
					    # 'zproject.backends.GitHubAuthBackend',  # GitHub auth, setup below
 | 
				
			||||||
 | 
					    # 'zproject.backends.AzureADAuthBackend',  # Microsoft Azure Active Directory auth, setup below
 | 
				
			||||||
    # 'zproject.backends.ZulipLDAPAuthBackend',  # LDAP, setup below
 | 
					    # 'zproject.backends.ZulipLDAPAuthBackend',  # LDAP, setup below
 | 
				
			||||||
    # 'zproject.backends.ZulipRemoteUserBackend',  # Local SSO, setup docs on readthedocs
 | 
					    # 'zproject.backends.ZulipRemoteUserBackend',  # Local SSO, setup docs on readthedocs
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -179,6 +180,23 @@ AUTHENTICATION_BACKENDS = (
 | 
				
			|||||||
#
 | 
					#
 | 
				
			||||||
#SOCIAL_AUTH_SUBDOMAIN = 'auth'
 | 
					#SOCIAL_AUTH_SUBDOMAIN = 'auth'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					########
 | 
				
			||||||
 | 
					# Azure Active Directory OAuth.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# To set up Microsoft Azure AD authentication, you'll need to do the following:
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# (1) Register an OAuth2 application with Microsoft at:
 | 
				
			||||||
 | 
					# https://apps.dev.microsoft.com
 | 
				
			||||||
 | 
					# Generate a new password under Application Secrets
 | 
				
			||||||
 | 
					# Generate a new platform (web) under Platforms. For Redirect URL, enter:
 | 
				
			||||||
 | 
					#   https://zulip.example.com/complete/azuread-oauth2/
 | 
				
			||||||
 | 
					# Add User.Read permission under Microsoft Graph Permissions
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# (2) Enter the application ID for the app as SOCIAL_AUTH_AZUREAD_OAUTH2_KEY here
 | 
				
			||||||
 | 
					# (3) Put the application password in zulip-secrets.conf as 'azure_oauth2_secret'.
 | 
				
			||||||
 | 
					#SOCIAL_AUTH_AZUREAD_OAUTH2_KEY = ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
########
 | 
					########
 | 
				
			||||||
# SSO via REMOTE_USER.
 | 
					# SSO via REMOTE_USER.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -159,6 +159,7 @@ DEFAULT_SETTINGS = {
 | 
				
			|||||||
    'SOCIAL_AUTH_GITHUB_ORG_NAME': None,
 | 
					    'SOCIAL_AUTH_GITHUB_ORG_NAME': None,
 | 
				
			||||||
    'SOCIAL_AUTH_GITHUB_TEAM_ID': None,
 | 
					    'SOCIAL_AUTH_GITHUB_TEAM_ID': None,
 | 
				
			||||||
    'SOCIAL_AUTH_SUBDOMAIN': None,
 | 
					    'SOCIAL_AUTH_SUBDOMAIN': None,
 | 
				
			||||||
 | 
					    'SOCIAL_AUTH_AZUREAD_OAUTH2_SECRET': get_secret('azure_oauth2_secret'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Email gateway
 | 
					    # Email gateway
 | 
				
			||||||
    'EMAIL_GATEWAY_PATTERN': '',
 | 
					    'EMAIL_GATEWAY_PATTERN': '',
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user