Configuration for Google Apps.

This commit is contained in:
Arik Fraimovich 2018-02-27 23:10:10 +02:00
parent 0eefc7b592
commit 9ddf3745b8
6 changed files with 96 additions and 24 deletions

View File

@ -16,38 +16,64 @@
<h3>Authentication</h3> <h3>Authentication</h3>
<p> <p>
<label> <label>
<input name="input" type="checkbox" ng-model="$ctrl.settings.auth_password_login_enabled" <input name="input" type="checkbox" ng-model="$ctrl.settings.auth_password_login_enabled" ng-change="$ctrl.update('auth_password_login_enabled')"
ng-change="$ctrl.update('auth_password_login_enabled')" accesskey="tab"> accesskey="tab" ng-disabled="$ctrl.disablePasswordLoginToggle()"> Password Login Enabled
Password Login Enabled <span uib-popover="Password login can be disabled only if another login method is enabled." popover-trigger="'mouseenter'"
ng-if="$ctrl.disablePasswordLoginToggle()">
<i class="fa fa-question-circle"></i>
</span>
</label> </label>
<div class="callout callout-warning" ng-if="!$ctrl.settings.auth_password_login_enabled">
Password based login is currently disabled and users will be able to login only with the enabled SSO options.
</div>
</p> </p>
<div ng-if="$ctrl.googleLoginEnabled">
<h4>Google Login</h4>
<label>
Allowed Google Apps Domains
</label>
<ui-select multiple tagging tagging-label="false" ng-model="$ctrl.settings.auth_google_apps_domains" tagging-tokens="SPACE|,"
title="Google Apps Domain(s)"
ng-change="$ctrl.update('auth_google_apps_domains')">
<ui-select-match placeholder="Google Apps Domain(s)">{{$item}}</ui-select-match>
<!-- the ui-select-choices is here just to make ui-select work -->
<ui-select-choices repeat="domain in $ctrl.domains">
{{domain}}
</ui-select-choices>
</ui-select>
<div class="callout callout-info m-t-5" ng-if="$ctrl.settings.auth_google_apps_domains | notEmpty">
Any user registered with a <strong>{{$ctrl.settings.auth_google_apps_domains | join}}</strong> 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 <strong>Default</strong> group.
</div>
</div>
<h4>SAML</h4> <h4>SAML</h4>
<p> <p>
<label> <label>
<input name="input" type="checkbox" ng-model="$ctrl.settings.auth_saml_enabled" <input name="input" type="checkbox" ng-model="$ctrl.settings.auth_saml_enabled" ng-change="$ctrl.update('auth_saml_enabled')"
ng-change="$ctrl.update('auth_saml_enabled')" accesskey="tab"> accesskey="tab"> SAML Enabled
SAML Enabled
</label> </label>
<div ng-show="$ctrl.settings.auth_saml_enabled"> <div ng-show="$ctrl.settings.auth_saml_enabled">
<div class="form-group"> <div class="form-group">
<label>SAML Metadata URL</label> <label>SAML Metadata URL</label>
<input name="input" type="string" class="form-control" ng-model="$ctrl.settings.auth_saml_metadata_url" accesskey="tab" <input name="input" type="string" class="form-control" ng-model="$ctrl.settings.auth_saml_metadata_url" accesskey="tab" ng-change="$ctrl.update('auth_saml_metadata_url')"
ng-change="$ctrl.update('auth_saml_metadata_url')" ng-model-options="{ debounce: 200 }"> ng-model-options="{ debounce: 200 }">
</div> </div>
<div class="form-group"> <div class="form-group">
<label>SAML Entity ID</label> <label>SAML Entity ID</label>
<input name="input" type="string" class="form-control" ng-model="$ctrl.settings.auth_saml_entity_id" accesskey="tab" <input name="input" type="string" class="form-control" ng-model="$ctrl.settings.auth_saml_entity_id" accesskey="tab" ng-change="$ctrl.update('auth_saml_entity_id')"
ng-change="$ctrl.update('auth_saml_entity_id')" ng-model-options="{ debounce: 200 }"> ng-model-options="{ debounce: 200 }">
</div> </div>
<div class="form-group"> <div class="form-group">
<label>SAML NameID Format</label> <label>SAML NameID Format</label>
<input name="input" type="string" class="form-control" ng-model="$ctrl.settings.auth_saml_nameid_format" accesskey="tab" <input name="input" type="string" class="form-control" ng-model="$ctrl.settings.auth_saml_nameid_format" accesskey="tab"
ng-change="$ctrl.update('auth_saml_nameid_format')" ng-model-options="{ debounce: 200 }"> ng-change="$ctrl.update('auth_saml_nameid_format')" ng-model-options="{ debounce: 200 }">
</div> </div>
</div> </div>
</p> </p>
</div> </div>
</div> </div>
</settings-screen> </settings-screen>

View File

@ -1,7 +1,7 @@
import settingsMenu from '@/lib/settings-menu'; import settingsMenu from '@/lib/settings-menu';
import template from './organization.html'; import template from './organization.html';
function OrganizationSettingsCtrl($http, toastr, Events) { function OrganizationSettingsCtrl($http, toastr, clientConfig, Events) {
Events.record('view', 'page', 'org_settings'); Events.record('view', 'page', 'org_settings');
this.settings = {}; this.settings = {};
@ -13,10 +13,20 @@ function OrganizationSettingsCtrl($http, toastr, Events) {
$http.post('api/settings/organization', { [key]: this.settings[key] }).then((response) => { $http.post('api/settings/organization', { [key]: this.settings[key] }).then((response) => {
this.settings = response.data.settings; this.settings = response.data.settings;
toastr.success('Settings changes saved.'); 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(() => { }).catch(() => {
toastr.error('Failed saving changes.'); toastr.error('Failed saving changes.');
}); });
}; };
this.googleLoginEnabled = clientConfig.googleLoginEnabled;
this.disablePasswordLoginToggle = () =>
(clientConfig.googleLoginEnabled || this.settings.auth_saml_enabled) === false;
} }
export default function init(ngModule) { export default function init(ngModule) {

View File

@ -172,7 +172,8 @@ def client_config():
'dateFormat': date_format, 'dateFormat': date_format,
'dateTimeFormat': "{0} HH:mm".format(date_format), 'dateTimeFormat': "{0} HH:mm".format(date_format),
'mailSettingsMissing': settings.MAIL_DEFAULT_SENDER is None, '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) client_config.update(defaults)

View File

@ -1,12 +1,13 @@
from flask import request from flask import request
from redash.models import db from redash.models import db, Organization
from redash.handlers.base import BaseResource from redash.handlers.base import BaseResource, record_event
from redash.permissions import require_admin from redash.permissions import require_admin
from redash.settings.organization import settings as org_settings 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 = {} settings = {}
for setting, default_value in defaults.iteritems(): for setting, default_value in defaults.iteritems():
@ -19,14 +20,15 @@ def get_settings_with_defaults(defaults, values):
else: else:
settings[setting] = current_value settings[setting] = current_value
settings['auth_google_apps_domains'] = org.google_apps_domains
return settings return settings
class OrganizationSettings(BaseResource): class OrganizationSettings(BaseResource):
@require_admin @require_admin
def get(self): def get(self):
current_values = self.current_org.settings.get('settings', {}) settings = get_settings_with_defaults(org_settings, self.current_org)
settings = get_settings_with_defaults(org_settings, current_values)
return { return {
"settings": settings "settings": settings
@ -39,13 +41,27 @@ class OrganizationSettings(BaseResource):
if self.current_org.settings.get('settings') is None: if self.current_org.settings.get('settings') is None:
self.current_org.settings['settings'] = {} self.current_org.settings['settings'] = {}
previous_values = {}
for k, v in new_values.iteritems(): 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.add(self.current_org)
db.session.commit() 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 { return {
"settings": settings "settings": settings

View File

@ -341,14 +341,17 @@ class Organization(TimestampMixin, db.Model):
self.settings['settings'][key] = value self.settings['settings'][key] = value
flag_modified(self, 'settings') 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', {}): if key in self.settings.get('settings', {}):
return self.settings['settings'][key] return self.settings['settings'][key]
if key in org_settings: if key in org_settings:
return org_settings[key] return org_settings[key]
raise KeyError(key) if raise_on_missing:
raise KeyError(key)
return None
@property @property
def admin_group(self): def admin_group(self):

View File

@ -12,4 +12,20 @@ class TestOrganizationSettings(BaseTestCase):
rv = self.make_request('post', '/api/settings/organization', data={'auth_password_login_enabled': True}, user=admin) 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) updated_org = Organization.get_by_slug(self.factory.org.slug)
self.assertEqual(rv.json['settings']['auth_password_login_enabled'], True) self.assertEqual(rv.json['settings']['auth_password_login_enabled'], True)
self.assertEqual(updated_org.settings['settings']['auth_password_login_enabled'], True) 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)