diff --git a/client/app/pages/settings/organization.html b/client/app/pages/settings/organization.html index f2698f16..b1f32ef1 100644 --- a/client/app/pages/settings/organization.html +++ b/client/app/pages/settings/organization.html @@ -16,38 +16,64 @@

Authentication

+ +

+ Password based login is currently disabled and users will be able to login only with the enabled SSO options. +

+
+

Google Login

+ + + {{$item}} + + + {{domain}} + + + +
+ Any user registered with a {{$ctrl.settings.auth_google_apps_domains | join}} Google Apps account will be able to login. If they don't have an existing user, a new user will be created and join the Default group. +
+
+

SAML

- +
- +
+ ng-change="$ctrl.update('auth_saml_nameid_format')" ng-model-options="{ debounce: 200 }">

- + \ No newline at end of file diff --git a/client/app/pages/settings/organization.js b/client/app/pages/settings/organization.js index 3d744f00..cc177355 100644 --- a/client/app/pages/settings/organization.js +++ b/client/app/pages/settings/organization.js @@ -1,7 +1,7 @@ import settingsMenu from '@/lib/settings-menu'; import template from './organization.html'; -function OrganizationSettingsCtrl($http, toastr, Events) { +function OrganizationSettingsCtrl($http, toastr, clientConfig, Events) { Events.record('view', 'page', 'org_settings'); this.settings = {}; @@ -13,10 +13,20 @@ function OrganizationSettingsCtrl($http, toastr, Events) { $http.post('api/settings/organization', { [key]: this.settings[key] }).then((response) => { this.settings = response.data.settings; toastr.success('Settings changes saved.'); + + if (this.disablePasswordLoginToggle() && this.settings.auth_password_login_enabled === false) { + this.settings.auth_password_login_enabled = true; + this.update('auth_password_login_enabled'); + } }).catch(() => { toastr.error('Failed saving changes.'); }); }; + + this.googleLoginEnabled = clientConfig.googleLoginEnabled; + + this.disablePasswordLoginToggle = () => + (clientConfig.googleLoginEnabled || this.settings.auth_saml_enabled) === false; } export default function init(ngModule) { diff --git a/redash/handlers/authentication.py b/redash/handlers/authentication.py index df45a6a3..d0e5cace 100644 --- a/redash/handlers/authentication.py +++ b/redash/handlers/authentication.py @@ -172,7 +172,8 @@ def client_config(): 'dateFormat': date_format, 'dateTimeFormat': "{0} HH:mm".format(date_format), 'mailSettingsMissing': settings.MAIL_DEFAULT_SENDER is None, - 'dashboardRefreshIntervals': settings.DASHBOARD_REFRESH_INTERVALS + 'dashboardRefreshIntervals': settings.DASHBOARD_REFRESH_INTERVALS, + 'googleLoginEnabled': settings.GOOGLE_OAUTH_ENABLED } client_config.update(defaults) diff --git a/redash/handlers/settings.py b/redash/handlers/settings.py index 5f3bc1fc..77dab014 100644 --- a/redash/handlers/settings.py +++ b/redash/handlers/settings.py @@ -1,12 +1,13 @@ from flask import request -from redash.models import db -from redash.handlers.base import BaseResource +from redash.models import db, Organization +from redash.handlers.base import BaseResource, record_event from redash.permissions import require_admin from redash.settings.organization import settings as org_settings -def get_settings_with_defaults(defaults, values): +def get_settings_with_defaults(defaults, org): + values = org.settings.get('settings', {}) settings = {} for setting, default_value in defaults.iteritems(): @@ -19,14 +20,15 @@ def get_settings_with_defaults(defaults, values): else: settings[setting] = current_value + settings['auth_google_apps_domains'] = org.google_apps_domains + return settings class OrganizationSettings(BaseResource): @require_admin def get(self): - current_values = self.current_org.settings.get('settings', {}) - settings = get_settings_with_defaults(org_settings, current_values) + settings = get_settings_with_defaults(org_settings, self.current_org) return { "settings": settings @@ -39,13 +41,27 @@ class OrganizationSettings(BaseResource): if self.current_org.settings.get('settings') is None: self.current_org.settings['settings'] = {} + previous_values = {} for k, v in new_values.iteritems(): - self.current_org.set_setting(k, v) + if k == 'auth_google_apps_domains': + previous_values[k] = self.current_org.google_apps_domains + self.current_org.settings[Organization.SETTING_GOOGLE_APPS_DOMAINS] = v + else: + previous_values[k] = self.current_org.get_setting(k, raise_on_missing=False) + self.current_org.set_setting(k, v) db.session.add(self.current_org) db.session.commit() - settings = get_settings_with_defaults(org_settings, self.current_org.settings['settings']) + self.record_event({ + 'action': 'edit', + 'object_id': self.current_org.id, + 'object_type': 'settings', + 'new_values': new_values, + 'previous_values': previous_values + }) + + settings = get_settings_with_defaults(org_settings, self.current_org) return { "settings": settings diff --git a/redash/models.py b/redash/models.py index 2566cd42..0944bd9d 100644 --- a/redash/models.py +++ b/redash/models.py @@ -341,14 +341,17 @@ class Organization(TimestampMixin, db.Model): self.settings['settings'][key] = value flag_modified(self, 'settings') - def get_setting(self, key): + def get_setting(self, key, raise_on_missing=True): if key in self.settings.get('settings', {}): return self.settings['settings'][key] if key in org_settings: return org_settings[key] - raise KeyError(key) + if raise_on_missing: + raise KeyError(key) + + return None @property def admin_group(self): diff --git a/tests/handlers/test_settings.py b/tests/handlers/test_settings.py index 937a6a74..4c73b1d2 100644 --- a/tests/handlers/test_settings.py +++ b/tests/handlers/test_settings.py @@ -12,4 +12,20 @@ class TestOrganizationSettings(BaseTestCase): rv = self.make_request('post', '/api/settings/organization', data={'auth_password_login_enabled': True}, user=admin) updated_org = Organization.get_by_slug(self.factory.org.slug) self.assertEqual(rv.json['settings']['auth_password_login_enabled'], True) - self.assertEqual(updated_org.settings['settings']['auth_password_login_enabled'], True) \ No newline at end of file + self.assertEqual(updated_org.settings['settings']['auth_password_login_enabled'], True) + + def test_updates_google_apps_domains(self): + admin = self.factory.create_admin() + domains = ['example.com'] + rv = self.make_request('post', '/api/settings/organization', data={'auth_google_apps_domains': domains}, user=admin) + updated_org = Organization.get_by_slug(self.factory.org.slug) + self.assertEqual(updated_org.google_apps_domains, domains) + + def test_get_returns_google_appas_domains(self): + admin = self.factory.create_admin() + domains = ['example.com'] + admin.org.settings[Organization.SETTING_GOOGLE_APPS_DOMAINS] = domains + + rv = self.make_request('get', '/api/settings/organization', user=admin) + self.assertEqual(rv.json['settings']['auth_google_apps_domains'], domains) +