docbrown/app/javascript/admin/controllers/config_controller.js
Michael Kohl 5406b0576e
Split Settings::Authentication from SiteConfig (#13095)
* Split Settings::Authentication from SiteConfig

* Move specs

* Sort fields

* Update settings usages

* Update recaptcha usages

* Add data update script

* Update spec

* Rename SiteConfigParams concern

* Fixes, new route, new controller

* Controller and service refactoring

* More controller and service updates

* Spec updates

* More spec fixes

* Move file

* Fix FeedbackMessagesController

* Update admin/configs_spec

* Fix remaining specs in admin/configs_spec

* Fix configs API

* Formatting

* Clean up old service object

* Various fixes

* Update DUS

* Add model argument to admin_config_label

* Fix key name

* Fix specs

* Add distinct request caches for settings classes

* Fix e2e tests

* Fix remaining system spec

* Make DUS idempotent

* Move routes block

* Cleanup

* Switch to ActiveSupport::CurrentAttributes

* Pinned rails-settings-cached

* Update e2e test

* Update lib/data_update_scripts/20210316091354_move_authentication_settings.rb

Co-authored-by: rhymes <rhymes@hey.com>

* Add guard to DUS

* Temporarily re-add two SiteConfig fields

* Fix config show view

Co-authored-by: rhymes <rhymes@hey.com>
2021-04-12 09:41:09 +02:00

373 lines
12 KiB
JavaScript

/* global jQuery */
import { Controller } from 'stimulus';
import { adminModal } from '../adminModal';
const recaptchaFields = document.getElementById('recaptchaContainer');
const emailRegistrationCheckbox = document.getElementById(
'email-registration-checkbox',
);
const emailAuthSettingsSection = document.getElementById(
'email-auth-settings-section',
);
const emailAuthModalTitle = 'Disable Email address registration';
// TODO: Remove the sentence "You must update site config to save this action!"
// once we build more robust flow for Admin/Config
const emailAuthModalBody = `
<p>If you disable Email address as a registration option, people cannot create an account with their email address.</p>
<p>However, people who have already created an account using their email address can continue to login.</p>
<p><strong>You must confirm and update site config to save below this action.</strong></p>`;
export default class ConfigController extends Controller {
static targets = [
'authenticationProviders',
'authSectionForm',
'collectiveNoun',
'configModalAnchor',
'emailAuthSettingsBtn',
'enabledIndicator',
'inviteOnlyMode',
'requireCaptchaForEmailPasswordRegistration',
];
// GENERAL FUNCTIONS START
// This is a bit of hack because we have to deal with Bootstrap used inline, jQuery and Stimulus :-/
// NOTE: it'd be best to rewrite this as a reusable "toggle" element in Stimulus without using jQuery + Bootstrap
toggleAccordionButtonLabel(event) {
const $target = jQuery(event.target);
const $container = $target.parent();
const text = $target.text();
if ($container) {
const show = $container.attr('aria-expanded') === 'true';
if (show) {
$target.text(text.replace(/Hide/i, 'Show'));
} else {
$target.text(text.replace(/Show/i, 'Hide'));
}
}
}
disableTargetField(event) {
const targetElementName = event.target.dataset.disableTarget;
const targetElement = this[`${targetElementName}Target`];
const newValue = event.target.checked;
targetElement.disabled = newValue;
// Disable the button generated by ERB for select tags
if (targetElement.nodeName === 'SELECT') {
const snakeCaseName = targetElementName.replace(
/[A-Z]/g,
(letter) => `_${letter.toLowerCase()}`,
);
document.querySelector(
`button[data-id=site_config_${snakeCaseName}]`,
).disabled = newValue;
}
}
closeAdminModal() {
// per forem/internalEngineering#336, need to short-circuit the
// "Update Site Configuration" button submit action; chose not to
// define Target on actual "Update" button (since it's a partial).
// The Target is defined on the Authentication form, and that section's
// "Update" button is queried.
const submitBtn = this.authSectionFormTarget.querySelector(
'input[type="submit"]',
);
this.configModalAnchorTarget.innerHTML = '';
document.body.style.height = 'inherit';
document.body.style.overflowY = 'inherit';
if (submitBtn.hasAttribute('disabled')) {
submitBtn.removeAttribute('disabled');
}
}
positionModalOnPage() {
if (document.getElementsByClassName('crayons-modal')[0]) {
document.body.style.height = '100vh';
document.body.style.overflowY = 'hidden';
}
}
// GENERAL FUNCTIONS END
// EMAIL AUTH FUNCTIONS START
toggleGoogleRecaptchaFields() {
if (this.requireCaptchaForEmailPasswordRegistrationTarget.checked) {
recaptchaFields.classList.remove('hidden');
} else {
recaptchaFields.classList.add('hidden');
}
}
enableOrEditEmailAuthSettings(event) {
event.preventDefault();
if (this.emailAuthSettingsBtnTarget.dataset.buttonText === 'enable') {
emailRegistrationCheckbox.checked = true;
this.emailAuthSettingsBtnTarget.setAttribute('data-button-text', 'edit');
this.enabledIndicatorTarget.classList.add('visible');
}
this.emailAuthSettingsBtnTarget.classList.add('hidden');
emailAuthSettingsSection.classList.remove('hidden');
}
hideEmailAuthSettings(event) {
event.preventDefault();
this.emailAuthSettingsBtnTarget.classList.remove('hidden');
emailAuthSettingsSection.classList.add('hidden');
}
activateEmailAuthModal(event) {
event.preventDefault();
this.configModalAnchorTarget.innerHTML = adminModal({
title: emailAuthModalTitle,
body: emailAuthModalBody,
leftBtnText: 'Confirm disable',
leftBtnAction: 'disableEmailAuthFromModal',
rightBtnText: 'Cancel',
rightBtnAction: 'closeAdminModal',
leftBtnClasses: 'crayons-btn--danger',
rightBtnClasses: 'crayons-btn--secondary',
});
this.positionModalOnPage();
}
disableEmailAuthFromModal(event) {
event.preventDefault();
this.disableEmailAuth(event);
this.closeAdminModal();
}
disableEmailAuth(event) {
event.preventDefault();
emailRegistrationCheckbox.checked = false;
this.emailAuthSettingsBtnTarget.innerHTML = 'Enable';
this.emailAuthSettingsBtnTarget.setAttribute('data-button-text', 'enable');
this.enabledIndicatorTarget.classList.remove('visible');
this.hideEmailAuthSettings(event);
}
// EMAIL AUTH FUNCTIONS END
// AUTH PROVIDERS FUNCTIONS START
enableOrEditAuthProvider(event) {
event.preventDefault();
const providerName = event.target.dataset.providerName;
const enabledIndicator = document.getElementById(
`${providerName}-enabled-indicator`,
);
document
.getElementById(`${providerName}-auth-settings`)
.classList.remove('hidden');
event.target.classList.add('hidden');
if (event.target.dataset.buttonText === 'enable') {
enabledIndicator.classList.add('visible');
event.target.setAttribute('data-enable-auth', 'true');
this.listAuthToBeEnabled();
}
}
disableAuthProvider(event) {
event.preventDefault();
const providerName = event.target.dataset.providerName;
const enabledIndicator = document.getElementById(
`${providerName}-enabled-indicator`,
);
const authEnableButton = document.getElementById(
`${providerName}-auth-btn`,
);
authEnableButton.setAttribute('data-enable-auth', 'false');
enabledIndicator.classList.remove('visible');
this.listAuthToBeEnabled();
this.hideAuthProviderSettings(event);
}
authProviderModalTitle(provider) {
return `Disable ${provider} login`;
}
authProviderModalBody(provider) {
return `<p>If you disable ${provider} as a login option, people cannot authenticate with ${provider}.</p><p><strong>You must update Site Config to save this action!</strong></p>`;
}
activateAuthProviderModal(event) {
event.preventDefault();
const providerName = event.target.dataset.providerName;
const providerOfficialName = event.target.dataset.providerOfficialName;
this.configModalAnchorTarget.innerHTML = adminModal({
title: this.authProviderModalTitle(providerOfficialName),
body: this.authProviderModalBody(providerOfficialName),
leftBtnText: 'Confirm disable',
leftBtnAction: 'disableAuthProviderFromModal',
rightBtnText: 'Cancel',
rightBtnAction: 'closeAdminModal',
leftBtnClasses: 'crayons-btn--danger',
rightBtnClasses: 'crayons-btn--secondary',
leftCustomDataAttr: `data-provider-name=${providerName}`,
});
this.positionModalOnPage();
}
disableAuthProviderFromModal(event) {
event.preventDefault();
const providerName = event.target.dataset.providerName;
const authEnableButton = document.getElementById(
`${providerName}-auth-btn`,
);
const enabledIndicator = document.getElementById(
`${providerName}-enabled-indicator`,
);
authEnableButton.setAttribute('data-enable-auth', 'false');
this.listAuthToBeEnabled(event);
this.checkForAndGuardSoleAuthProvider();
enabledIndicator.classList.remove('visible');
this.hideAuthProviderSettings(event);
this.closeAdminModal();
}
checkForAndGuardSoleAuthProvider() {
if (
document.querySelectorAll('[data-enable-auth="true"]').length === 1 &&
document
.getElementById('email-auth-enable-edit-btn')
.getAttribute('data-button-text') === 'enable'
) {
const targetAuthDisableBtn = document.querySelector(
'[data-enable-auth="true"]',
);
targetAuthDisableBtn.parentElement.classList.add('crayons-tooltip');
targetAuthDisableBtn.parentElement.setAttribute(
'data-tooltip',
'To edit this, you must first enable Email address as a registration option',
);
targetAuthDisableBtn.setAttribute('disabled', true);
}
}
hideAuthProviderSettings(event) {
event.preventDefault();
const providerName = event.target.dataset.providerName;
document
.getElementById(`${providerName}-auth-settings`)
.classList.add('hidden');
document
.getElementById(`${providerName}-auth-btn`)
.classList.remove('hidden');
}
listAuthToBeEnabled() {
const enabledProviderArray = [];
document
.querySelectorAll('[data-enable-auth="true"]')
.forEach((provider) => {
enabledProviderArray.push(provider.dataset.providerName);
});
document.getElementById(
'auth_providers_to_enable',
).value = enabledProviderArray;
}
adjustAuthenticationOptions() {
if (this.inviteOnlyModeTarget.checked) {
document.getElementById('auth_providers_to_enable').value = '';
emailRegistrationCheckbox.checked = false;
} else {
emailRegistrationCheckbox.checked = true;
}
}
// AUTH PROVIDERS FUNCTIONS END
enabledProvidersWithMissingKeys() {
const providersWithMissingKeys = [];
document
.querySelectorAll('[data-enable-auth="true"]')
.forEach((provider) => {
const { providerName } = provider.dataset;
if (
!document.getElementById(
`settings_authentication_${providerName}_key`,
).value ||
!document.getElementById(
`settings_authentication_${providerName}_secret`,
).value
) {
providersWithMissingKeys.push(providerName);
}
});
return providersWithMissingKeys;
}
generateProvidersList(providers) {
const list = providers.reduce((html, provider) => {
return `${html}<li class="capitalize">${provider}</li>`;
}, '');
return list;
}
missingAuthKeysModalBody(providers) {
return `
<p>You haven't filled out all of the required fields to enable the following authentication providers:</p>
<ul class="mb-0">${this.generateProvidersList(providers)}</ul>
<p class="mb-0">You may continue editing these authentication providers, or you may cancel.</p>
`;
}
submitForm() {
this.authSectionFormTarget.submit();
}
activateMissingKeysModal(providers) {
this.configModalAnchorTarget.innerHTML = adminModal({
title: 'Setup not complete',
body: this.missingAuthKeysModalBody(providers),
leftBtnText: 'Continue editing',
leftBtnAction: 'closeAdminModal',
rightBtnText: 'Cancel',
rightBtnAction: 'cancelAuthProviderEnable',
rightBtnClasses: 'crayons-btn--secondary',
});
}
configUpdatePrecheck(event) {
if (this.enabledProvidersWithMissingKeys().length > 0) {
event.preventDefault();
this.activateMissingKeysModal(this.enabledProvidersWithMissingKeys());
} else {
event.target.submit();
}
}
cancelAuthProviderEnable() {
const providers = this.enabledProvidersWithMissingKeys();
providers.forEach((provider) => {
const enabledIndicator = document.getElementById(
`${provider}-enabled-indicator`,
);
const authEnableButton = document.getElementById(`${provider}-auth-btn`);
authEnableButton.setAttribute('data-enable-auth', 'false');
enabledIndicator.classList.remove('visible');
this.listAuthToBeEnabled();
document
.getElementById(`${provider}-auth-settings`)
.classList.add('hidden');
document
.getElementById(`${provider}-auth-btn`)
.classList.remove('hidden');
this.closeAdminModal();
});
}
}