[Team Email Login] Refactor Authentication Provider Enable/Disable (#11185)

* Starting out

* Building away...

* Hooking buttons up

* Hook Auth Provider buttons to Array Field

* trying to fix NoNameError

* Remains InviteOnlyMode disable and tests

* Smashing remaining tasks

* Last of tasks

* add tests

* Complete specs and tests 😅

* Fix bug

* Additional guard

* pass event to functions

* Position Email Auth first

* Fix bug in Email Auth Modal

* Fix spacing issue

* Update docs for adminModal.js

* Show/hide Enabled Indicator with Enable/Undo buttons

* Complete Auth Providers functionality

* Update app/javascript/admin/controllers/config_controller.js

Co-authored-by: Vaidehi Joshi <vaidehi.sj@gmail.com>

* Update app/javascript/admin/controllers/config_controller.js

Co-authored-by: Vaidehi Joshi <vaidehi.sj@gmail.com>

* Update app/controllers/admin/configs_controller.rb

Co-authored-by: Vaidehi Joshi <vaidehi.sj@gmail.com>

* Update helper names

* better implementation of EnabledIndicator show/hide

* Small copy changes

* Update spec/helpers/authentication_helper_spec.rb

Co-authored-by: Vaidehi Joshi <vaidehi.sj@gmail.com>

* Update app/views/admin/configs/show.html.erb

Co-authored-by: Vaidehi Joshi <vaidehi.sj@gmail.com>

* Update app/helpers/authentication_helper.rb

Co-authored-by: Vaidehi Joshi <vaidehi.sj@gmail.com>

Co-authored-by: Vaidehi Joshi <vaidehi.sj@gmail.com>
This commit is contained in:
Arit Amana 2020-11-06 18:19:34 -05:00 committed by GitHub
parent 397a1c2389
commit 08796b5cee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 446 additions and 205 deletions

View file

@ -5,8 +5,8 @@
* @param {string} title The title/heading text to be displayed
* @param {string} text The body text to be displayed
* @param {string} confirm_text Text of the confirmation button
*
* @example
*
* @example
* showUserAlertModal('Warning', 'You must wait', 'OK', '/faq/why-must-i-wait', 'Why must I wait?');
*/
function showUserAlertModal(title, text, confirm_text) {
@ -14,44 +14,54 @@ function showUserAlertModal(title, text, confirm_text) {
toggleUserAlertModal();
}
/**
* Displays a user rate limit alert modal letting the user know what they did that exceeded a rate limit,
* Displays a user rate limit alert modal letting the user know what they did that exceeded a rate limit,
* and gives them links to explain why they must wait
*
* @function showUserAlertModal
* @param {string} action_text Description of the action taken by the user
* @param {string} next_action_text Description of the next action that can be taken
*
* @example
*
* @example
* showRateLimitModal('Made a comment', 'comment again');
*/
function showRateLimitModal(action_text, next_action_text) {
let rateLimitText = buildRateLimitText(action_text, next_action_text);
let rateLimitLink = "/faq";
showUserAlertModal('Wait a Moment...', rateLimitText, 'Got it', rateLimitLink, "Why do I have to wait?")
let rateLimitLink = '/faq';
showUserAlertModal(
'Wait a Moment...',
rateLimitText,
'Got it',
rateLimitLink,
'Why do I have to wait?',
);
}
/**
* HTML ID for modal DOM node
*
*
* @private
* @constant modalId *
* @constant modalId *
* @type {string}
*/
const modalId = 'user-alert-modal';
/**
* HTML template for modal
*
*
* @private
* @function getModalHtml
*
*
* @param {string} title The title/heading text to be displayed
* @param {string} text The body text to be displayed
* @param {string} confirm_text Text of the confirmation button
*
*
* @returns {string} HTML for the modal
*/
const getModalHtml = (title, text, confirm_text) => `<div id="${modalId}" data-testid="modal-container" class="crayons-modal hidden">
const getModalHtml = (
title,
text,
confirm_text,
) => `<div id="${modalId}" data-testid="modal-container" class="crayons-modal hidden">
<div role="dialog" aria-modal="true" class="crayons-modal__box">
<div class="crayons-modal__box__header">
<h2>${title}</h2>
@ -78,25 +88,25 @@ const getModalHtml = (title, text, confirm_text) => `<div id="${modalId}" data-t
/**
* Constructs wording for rate limit modals
*
*
* @private
* @function buildRateLimitText
*
*
* @param {string} action_text Description of the action taken by the user
* @param {string} next_action_text Description of the next action that can be taken
*
*
* @returns {string} Formatted body text for a rate limit modal
*/
function buildRateLimitText(action_text, next_action_text) {
return `Since you recently ${action_text}, youll need to wait a moment before ${next_action_text}.`
return `Since you recently ${action_text}, youll need to wait a moment before ${next_action_text}.`;
}
/**
* Shows or hides the user alert modal by toggling it's 'hidden' class
*
*
* @private
* @function toggleUserAlertModal
*
*
*/
function toggleUserAlertModal() {
let modalDiv = document.getElementById(modalId);
@ -107,14 +117,14 @@ function toggleUserAlertModal() {
/**
* Checks for the alert modal, and if it's not present builds and inserts it in the DOM
*
*
* @private
* @function buildModalDiv
*
*
* @param {string} title The title/heading text to be displayed
* @param {string} text The body text to be displayed
* @param {string} confirm_text Text of the confirmation button
*
*
* @returns {Element} DOM node of the inserted alert modal
*/
function buildModalDiv(title, text, confirm_text) {
@ -128,18 +138,18 @@ function buildModalDiv(title, text, confirm_text) {
/**
* Takes template HTML for a modal and creates a DOM node based on supplied arguments
*
*
* @private
* @function getModal
*
*
* @param {string} title The title/heading text to be displayed
* @param {string} text The body text to be displayed
* @param {string} confirm_text Text of the confirmation button
*
*
* @returns {Element} DOM node of alert modal with formatted text
*/
function getModal(title, text, confirm_text) {
let wrapper = document.createElement('div');
wrapper.innerHTML= getModalHtml(title, text, confirm_text);
wrapper.innerHTML = getModalHtml(title, text, confirm_text);
return wrapper.firstChild;
}

View file

@ -128,6 +128,17 @@ label {
height: 40px;
width: 40px;
}
&--label {
.enabled-indicator-hidden {
display: none;
}
.enabled-indicator-visible {
display: flex;
align-items: center;
}
}
}
.config-authentication-form {

View file

@ -89,11 +89,6 @@ module Admin
display_jobs_banner
].freeze
ALLOWED_EMPTY_ENUMERABLES =
%i[
authentication_providers
].freeze
ALLOWED_PARAMS =
%i[
ga_tracking_id
@ -117,6 +112,7 @@ module Admin
github_secret
facebook_key
facebook_secret
auth_providers_to_enable
invite_only_mode
allow_both_email_signup_and_login
require_captcha_for_email_password_registration
@ -143,7 +139,9 @@ module Admin
clean_up_params
config_params.each do |key, value|
if value.is_a?(Array)
if key == "auth_providers_to_enable"
update_enabled_auth_providers(value) unless value.class.name != "String"
elsif value.is_a?(Array)
SiteConfig.public_send("#{key}=", value.reject(&:blank?)) unless value.empty?
elsif value.respond_to?(:to_h)
SiteConfig.public_send("#{key}=", value.to_h) unless value.empty?
@ -223,6 +221,23 @@ module Admin
end
end
def update_enabled_auth_providers(value)
enabled_providers = []
value.split(",").each do |entry|
enabled_providers.push(entry) unless invalid_provider_entry(entry)
end
SiteConfig.public_send("authentication_providers=", enabled_providers) unless
prevent_all_auth_provider_disable?(enabled_providers)
end
def invalid_provider_entry(entry)
entry.blank? || helpers.available_providers_array.exclude?(entry)
end
def prevent_all_auth_provider_disable?(value)
value.empty? && !SiteConfig.allow_email_password_login
end
# Validations
def brand_contrast_too_low
hex = params.dig(:site_config, :primary_brand_color_hex)

View file

@ -19,6 +19,10 @@ module AuthenticationHelper
Authentication::Providers.enabled_for_user(user)
end
def available_providers_array
Authentication::Providers.available.map(&:to_s)
end
def recaptcha_configured_and_enabled?
SiteConfig.recaptcha_secret_key.present? &&
SiteConfig.recaptcha_site_key.present? &&
@ -33,23 +37,45 @@ module AuthenticationHelper
SiteConfig.waiting_on_first_user
end
def disable_email_tooltip_class
SiteConfig.invite_only_mode || authentication_enabled_providers.none? ? "crayons-tooltip" : ""
def invite_only_mode_or_no_enabled_providers
SiteConfig.invite_only_mode || authentication_enabled_providers.none?
end
def disable_email_tooltip_content
SiteConfig.invite_only_mode || authentication_enabled_providers.none? ? disable_email_auth_tooltip_text : ""
def email_login_disabled_and_one_auth_provider_enabled
!SiteConfig.allow_email_password_login && authentication_enabled_providers.count == 1
end
def disable_button_class
SiteConfig.invite_only_mode || authentication_enabled_providers.none? ? "disabled" : ""
def tooltip_class_on_email_auth_disablebtn
invite_only_mode_or_no_enabled_providers ? "crayons-tooltip" : ""
end
def disable_email_auth_tooltip_text
def tooltip_class_on_auth_provider_enablebtn
SiteConfig.invite_only_mode ? "crayons-tooltip" : ""
end
def tooltip_class_on_auth_provider_disablebtn
email_login_disabled_and_one_auth_provider_enabled ? "crayons-tooltip" : ""
end
def disabled_attr_on_email_auth_disablebtn
invite_only_mode_or_no_enabled_providers ? "disabled" : ""
end
def disabled_attr_on_auth_provider_enablebtn
SiteConfig.invite_only_mode ? "disabled" : ""
end
def disabled_attr_on_auth_rpovider_disablebtn
email_login_disabled_and_one_auth_provider_enabled ? "disabled" : ""
end
def tooltip_text_email_or_auth_provider_btns
if SiteConfig.invite_only_mode
"You cannot do this until you disable Invite Only Mode"
elsif authentication_enabled_providers.none?
elsif authentication_enabled_providers.none? || email_login_disabled_and_one_auth_provider_enabled
"You cannot do this until you enable at least one other registration option"
else
""
end
end
end

View file

@ -8,6 +8,8 @@
* @param {string} confirmBtnAction The function that fires when "Confirm" button is clicked.
* @param {string} cancelBtnText The text for the modal's "Cancel" button.
* @param {string} cancelBtnAction The function that fires when "Cancel" button is clicked.
* @param {string} customAttr A custom data attribute name. Will be apprended to the "data-" part.
* @param {string} customAttrValue The value of the custom attribute "customAttr".
*/
const adminModal = (
title,
@ -16,6 +18,8 @@ const adminModal = (
confirmBtnAction,
cancelBtnText,
cancelBtnAction,
customAttr = null,
customAttrValue = null,
) => `
<div class="crayons-modal crayons-modal--s">
<div class="crayons-modal__box">
@ -32,7 +36,8 @@ const adminModal = (
<div class="flex gap-2">
<button
class="crayons-btn crayons-btn--danger"
data-action="click->config#${confirmBtnAction}">
data-action="click->config#${confirmBtnAction}"
data-${customAttr}="${customAttrValue}">
${confirmBtnText}
</button>
<button

View file

@ -8,11 +8,11 @@ const emailSigninAndLoginCheckbox = document.querySelector(
const emailAuthSettingsSection = document.querySelector(
'#email-auth-settings-section',
);
const emailAuthModalTitle = 'Disable email address registration';
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>Please update site config to save this action.</strong></p>';
'<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>Please update site config to save this action.</strong></p>';
export default class ConfigController extends Controller {
static targets = [
@ -25,6 +25,8 @@ export default class ConfigController extends Controller {
'requireCaptchaForEmailPasswordRegistration',
];
// GENERAL FUNCTIONS START
disableTargetField(event) {
const targetElementName = event.target.dataset.disableTarget;
const targetElement = this[`${targetElementName}Target`];
@ -43,6 +45,23 @@ export default class ConfigController extends Controller {
}
}
closeAdminConfigModal() {
this.configModalAnchorTarget.innerHTML = '';
document.body.style.height = 'inherit';
document.body.style.overflowY = 'inherit';
}
positionModalOnPage() {
if (document.querySelector('.crayons-modal')) {
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');
@ -51,18 +70,18 @@ export default class ConfigController extends Controller {
}
}
enableOrEditEmailAuthSettings() {
enableOrEditEmailAuthSettings(event) {
event.preventDefault();
if (this.emailAuthSettingsBtnTarget.dataset.buttonText === 'enable') {
emailSigninAndLoginCheckbox.checked = true;
this.enabledIndicatorTarget.classList.add('flex', 'items-center');
this.enabledIndicatorTarget.classList.remove('hidden');
this.emailAuthSettingsBtnTarget.setAttribute('data-button-text', 'edit');
this.enabledIndicatorTarget.classList.toggle('enabled-indicator-visible');
}
this.emailAuthSettingsBtnTarget.classList.add('hidden');
emailAuthSettingsSection.classList.remove('hidden');
}
hideEmailAuthSettings() {
hideEmailAuthSettings(event) {
event.preventDefault();
this.emailAuthSettingsBtnTarget.classList.remove('hidden');
emailAuthSettingsSection.classList.add('hidden');
@ -78,30 +97,144 @@ export default class ConfigController extends Controller {
'Cancel',
'closeAdminConfigModal',
);
if (document.querySelector('.crayons-modal')) {
document.body.style.height = '100vh';
document.body.style.overflowY = 'hidden';
}
this.positionModalOnPage();
}
closeAdminConfigModal() {
this.configModalAnchorTarget.innerHTML = '';
document.body.style.height = 'inherit';
document.body.style.overflowY = 'inherit';
}
disableEmailAuthFromModal() {
disableEmailAuthFromModal(event) {
event.preventDefault();
this.disableEmailAuth();
this.closeAdminConfigModal();
this.disableEmailAuth(event);
this.closeAdminConfigModal(event);
}
disableEmailAuth() {
disableEmailAuth(event) {
event.preventDefault();
emailSigninAndLoginCheckbox.checked = false;
this.emailAuthSettingsBtnTarget.innerHTML = 'Enable';
this.enabledIndicatorTarget.classList.remove('flex', 'items-center');
this.enabledIndicatorTarget.classList.add('hidden');
this.hideEmailAuthSettings();
this.emailAuthSettingsBtnTarget.setAttribute('data-button-text', 'enable');
this.enabledIndicatorTarget.classList.toggle('enabled-indicator-visible');
this.hideEmailAuthSettings(event);
}
// EMAIL AUTH FUNCTIONS END
// AUTH PROVIDERS FUNCTIONS START
enableOrEditAuthProvider(event) {
event.preventDefault();
const provider = event.target.dataset.authProviderEnable;
const enabledIndicator = document.querySelector(
`#${provider}-enabled-indicator`,
);
if (event.target.dataset.buttonText === 'enable') {
enabledIndicator.classList.add('enabled-indicator-visible');
event.target.setAttribute('data-enable-auth', 'true');
this.listAuthToBeEnabled();
}
document
.querySelector(`#${provider}-auth-settings`)
.classList.remove('hidden');
event.target.classList.add('hidden');
}
disableAuthProvider(event) {
event.preventDefault();
const provider = event.target.dataset.authProvider;
const enabledIndicator = document.querySelector(
`#${provider}-enabled-indicator`,
);
const authEnableButton = document.querySelector(
`[data-auth-provider-enable="${provider}"]`,
);
authEnableButton.setAttribute('data-enable-auth', 'false');
enabledIndicator.classList.remove('enabled-indicator-visible');
this.listAuthToBeEnabled(event);
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 provider = event.target.dataset.authProvider;
const official_provider = event.target.dataset.authProviderOfficial;
this.configModalAnchorTarget.innerHTML = adminModal(
this.authProviderModalTitle(official_provider),
this.authProviderModalBody(official_provider),
'Confirm disable',
'disableAuthProviderFromModal',
'Cancel',
'closeAdminConfigModal',
'auth-provider',
provider,
);
this.positionModalOnPage();
}
disableAuthProviderFromModal(event) {
event.preventDefault();
const provider = event.target.dataset.authProvider;
const authEnableButton = document.querySelector(
`[data-auth-provider-enable="${provider}"]`,
);
const enabledIndicator = document.querySelector(
`#${provider}-enabled-indicator`,
);
authEnableButton.setAttribute('data-enable-auth', 'false');
this.listAuthToBeEnabled(event);
this.checkForAndGuardSoleAuthProvider();
enabledIndicator.classList.remove('enabled-indicator-visible');
this.hideAuthProviderSettings(event);
this.closeAdminConfigModal(event);
}
checkForAndGuardSoleAuthProvider() {
if (
document.querySelectorAll('[data-enable-auth="true"]').length === 1 &&
document
.querySelector('#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 provider = event.target.dataset.authProvider;
document
.querySelector(`#${provider}-auth-settings`)
.classList.add('hidden');
document.querySelector(`#${provider}-auth-btn`).classList.remove('hidden');
}
listAuthToBeEnabled() {
const enabledProviderArray = [];
document
.querySelectorAll('[data-enable-auth="true"]')
.forEach((provider) => {
enabledProviderArray.push(provider.dataset.authProviderEnable);
});
document.querySelector(
'#auth_providers_to_enable',
).value = enabledProviderArray;
}
disableAuthenticationOptions() {
document.querySelector('#auth_providers_to_enable').value = '';
}
// AUTH PROVIDERS FUNCTIONS END
}

View file

@ -0,0 +1,54 @@
<div class="config-authentication__item">
<div class="config-authentication__item--container">
<div class="config-authentication__item--body">
<fieldset class="config-authentication-form">
<div class="crayons-field">
<%= admin_config_label :"#{provider.provider_name}_key" %>
<p class="crayons-field__description">
<%= Constants::SiteConfig::DETAILS[:"#{provider.provider_name}_key"][:description] %>
</p>
<%= f.text_field :"#{provider.provider_name}_key",
class: "crayons-textfield",
value: SiteConfig.public_send("#{provider.provider_name}_key"),
placeholder: Constants::SiteConfig::DETAILS[:"#{provider.provider_name}_key"][:placeholder] %>
</div>
<div class="crayons-field">
<%= admin_config_label :"#{provider.provider_name}_secret" %>
<p class="crayons-field__description">
<%= Constants::SiteConfig::DETAILS[:"#{provider.provider_name}_secret"][:description] %>
<p>
<%= f.text_field :"#{provider.provider_name}_secret",
class: "crayons-textfield",
value: SiteConfig.public_send("#{provider.provider_name}_secret"),
placeholder: Constants::SiteConfig::DETAILS[:"#{provider.provider_name}_secret"][:placeholder] %>
</div>
<div class="mt-6">
<% if authentication_enabled_providers.include?(provider) %>
<button
class="crayons-btn crayons-btn--danger <%= tooltip_class_on_auth_provider_disablebtn %>"
data-action="click->config#activateAuthProviderModal"
data-tooltip="<%= tooltip_text_email_or_auth_provider_btns %>"
data-auth-provider="<%= provider.provider_name %>"
data-auth-provider-official="<%= provider.official_name %>"
<%= disabled_attr_on_auth_rpovider_disablebtn %>>
Disable
</button>
<button
class="crayons-btn crayons-btn--secondary"
data-auth-provider="<%= provider.provider_name %>"
data-action="click->config#hideAuthProviderSettings">
Close
</button>
<% else %>
<button
class="crayons-btn crayons-btn--secondary"
data-auth-provider="<%= provider.provider_name %>"
data-action="click->config#disableAuthProvider">
Undo
</button>
<% end %>
</div>
</fieldset>
</div>
</div>
</div>

View file

@ -162,7 +162,7 @@
</p>
</header>
<section class="config-authentication__item">
<section class="config-authentication__item mb-0">
<figure class="config-authentication__item--icon">
<%= inline_svg_tag("email.svg", class: "crayons-icon", aria: true, title: "Email icon") %>
</figure>
@ -173,7 +173,9 @@
<p class="crayons-field__label <%= SiteConfig.allow_both_email_signup_and_login ? "pb-0" : "" %>">
Email address
</p>
<div class="<%= SiteConfig.allow_both_email_signup_and_login ? "flex items-center" : "hidden" %>" data-target="config.enabledIndicator">
<div
class="enabled-indicator-hidden <%= SiteConfig.allow_both_email_signup_and_login ? "enabled-indicator-visible" : "" %>"
data-target="config.enabledIndicator">
<%= inline_svg_tag("checkmark.svg", aria: true, class: "crayons-icon admin-config-checkmark", title: "Checkmark") %>
<small class="crayons-field__description ml-1">Enabled</small>
</div>
@ -181,6 +183,7 @@
<button
class="crayons-btn crayons-btn--secondary"
id="email-auth-enable-edit-btn"
data-button-text="<%= SiteConfig.allow_both_email_signup_and_login ? "edit" : "enable" %>"
data-target="config.emailAuthSettingsBtn"
data-action="click->config#enableOrEditEmailAuthSettings">
@ -227,15 +230,13 @@
<div class="flex gap-2">
<% if SiteConfig.allow_both_email_signup_and_login %>
<button
class="crayons-btn crayons-btn--danger <%= disable_email_tooltip_class %>"
class="crayons-btn crayons-btn--danger <%= tooltip_class_on_email_auth_disablebtn %>"
data-action="click->config#activateEmailAuthModal"
data-tooltip="<%= disable_email_tooltip_content %>"
<%= disable_button_class %>>
data-tooltip="<%= tooltip_text_email_or_auth_provider_btns %>"
<%= disabled_attr_on_email_auth_disablebtn %>>
Disable
</button>
<% end %>
<% if SiteConfig.allow_both_email_signup_and_login %>
<button
<button
class="crayons-btn crayons-btn--secondary"
data-action="click->config#hideEmailAuthSettings">
Close
@ -252,136 +253,60 @@
</div>
</div>
</section>
<hr class="my-0" />
<div class="form-group">
<%= admin_config_label :authentication_providers %>
<p class="crayons-field__description">
<%= Constants::SiteConfig::DETAILS[:authentication_providers][:description] %>
</p>
<%= select_tag "site_config[authentication_providers]",
options_from_collection_for_select(
authentication_available_providers,
:provider_name,
:official_name,
authentication_enabled_providers.map(&:provider_name),
),
multiple: true,
class: "form-control selectpicker",
data: { target: "config.authenticationProviders" } %>
</div>
<section class="config-authentication__item">
<figure class="config-authentication__item--icon">
<%= inline_svg_tag("twitter.svg", class: "crayons-icon", aria: true, title: "Twitter logo") %>
</figure>
<div class="config-authentication__item--container">
<div class="config-authentication__item--label">
<p class="fw-medium">
Twitter
</p>
</div>
<div class="config-authentication__item--body">
<fieldset class="config-authentication-form">
<div class="crayons-field">
<%= admin_config_label :twitter_key %>
<p class="crayons-field__description">
<%= Constants::SiteConfig::DETAILS[:twitter_key][:description] %>
</p>
<%= f.text_field :twitter_key,
class: "crayons-textfield",
value: SiteConfig.twitter_key,
placeholder: Constants::SiteConfig::DETAILS[:twitter_key][:placeholder] %>
</div>
<div class="crayons-field">
<%= admin_config_label :twitter_secret %>
<p class="crayons-field__description">
<%= Constants::SiteConfig::DETAILS[:twitter_secret][:description] %>
<p>
<%= f.text_field :twitter_secret,
class: "crayons-textfield",
value: SiteConfig.twitter_secret,
placeholder: Constants::SiteConfig::DETAILS[:twitter_secret][:placeholder] %>
</div>
</fieldset>
</div>
<section class="mb-6">
<div class="form-group mb-0">
<%= f.text_field :auth_providers_to_enable,
id: "auth_providers_to_enable",
class: "form-control hidden",
value: SiteConfig.authentication_providers.join(","),
readonly: true %>
</div>
</section>
<section class="config-authentication__item">
<figure class="config-authentication__item--icon">
<%= inline_svg_tag("github.svg", class: "crayons-icon", aria: true, title: "Twitter logo") %>
</figure>
<div class="config-authentication__item--container">
<div class="config-authentication__item--label">
<p class="fw-medium">
Github
</p>
</div>
<div class="config-authentication__item--body">
<fieldset class="config-authentication-form">
<div class="crayons-field">
<%= admin_config_label :github_key %>
<p class="crayons-field__description">
<%= Constants::SiteConfig::DETAILS[:github_key][:description] %>
</p>
<%= f.text_field :github_key,
class: "crayons-textfield",
value: SiteConfig.github_key,
placeholder: Constants::SiteConfig::DETAILS[:github_key][:placeholder] %>
</div>
<div class="crayons-field">
<%= admin_config_label :github_secret %>
<p class="config-authentication__row--subtitle">
<%= Constants::SiteConfig::DETAILS[:github_secret][:description] %>
</p>
<%= f.text_field :github_secret,
class: "crayons-textfield",
value: SiteConfig.github_secret,
placeholder: Constants::SiteConfig::DETAILS[:github_secret][:placeholder] %>
</div>
</fieldset>
</div>
</div>
</section>
<section class="config-authentication__item">
<figure class="config-authentication__item--icon">
<%= inline_svg_tag("facebook.svg", class: "crayons-icon", aria: true, title: "Twitter logo") %>
</figure>
<div class="config-authentication__item--container">
<div class="config-authentication__item--label">
<p class="fw-medium">
Facebook
</p>
</div>
<div class="config-authentication__item--body">
<fieldset class="config-authentication-form">
<div class="crayons-field">
<%= admin_config_label :facebook_key %>
<p class="crayons-field__description">
<%= Constants::SiteConfig::DETAILS[:facebook_key][:description] %>
</p>
<%= f.text_field :facebook_key,
class: "crayons-textfield",
value: SiteConfig.facebook_key,
placeholder: Constants::SiteConfig::DETAILS[:facebook_key][:placeholder] %>
</div>
<div class="crayons-field">
<%= admin_config_label :facebook_secret %>
<p class="crayons-field__description">
<%= Constants::SiteConfig::DETAILS[:facebook_secret][:description] %>
<% authentication_available_providers.each do |provider| %>
<section class="config-authentication__item mb-0">
<figure class="config-authentication__item--icon">
<%= inline_svg_tag("#{provider.provider_name}.svg", class: "crayons-icon", aria: true, title: "#{provider.official_name} logo") %>
</figure>
<div class="config-authentication__item--container">
<div
class="config-authentication__item mb-0 flex items-baseline justify-between <%= tooltip_class_on_auth_provider_enablebtn %>"
data-tooltip="<%= tooltip_text_email_or_auth_provider_btns %>">
<div class="config-authentication__item--label">
<p class="crayons-field__label <%= authentication_enabled_providers.include?(provider) ? "pb-0" : "" %>">
<%= provider.official_name %>
</p>
<%= f.text_field :facebook_secret,
class: "crayons-textfield",
value: SiteConfig.facebook_secret,
placeholder: Constants::SiteConfig::DETAILS[:facebook_secret][:placeholder] %>
<div
id="<%= provider.provider_name %>-enabled-indicator"
class="enabled-indicator-hidden <%= authentication_enabled_providers.include?(provider) ? "enabled-indicator-visible" : "" %>">
<%= inline_svg_tag("checkmark.svg", aria: true, class: "crayons-icon admin-config-checkmark", title: "Checkmark") %>
<small class="crayons-field__description ml-1">Enabled</small>
</div>
</div>
<button
class="crayons-btn crayons-btn--secondary"
id="<%= provider.provider_name %>-auth-btn"
data-button-text="<%= authentication_enabled_providers.include?(provider) ? "edit" : "enable" %>"
data-action="click->config#enableOrEditAuthProvider"
data-auth-provider-enable="<%= provider.provider_name %>"
data-enable-auth="<%= authentication_enabled_providers.include?(provider) ? "true" : "false" %>"
<%= disabled_attr_on_auth_provider_enablebtn %>>
<%= authentication_enabled_providers.include?(provider) ? "Edit" : "Enable" %>
</button>
</div>
</fieldset>
</div>
</div>
<div id="<%= provider.provider_name %>-auth-settings" class="config-authentication__item--body hidden">
<fieldset class="config-authentication-form">
<%= render partial: "auth_provider_settings",
locals: {
f: f,
provider: provider
} %>
</fieldset>
</div>
</div>
</section>
<hr class="my-0" />
<% end %>
</section>
</section>
<div class="crayons-notice crayons-notice--info mb-4">

View file

@ -28,6 +28,16 @@ RSpec.describe AuthenticationHelper, type: :helper do
end
end
describe "#available_providers_array" do
it "returns array of available providers in lowercase" do
provider = Authentication::Providers.available.first
allow(Authentication::Providers).to receive(:available).and_return([provider])
expected_result = provider.to_s
expect(helper.available_providers_array).to match_array([expected_result])
end
end
describe "#recaptcha_configured_and_enabled?" do
context "when recaptcha is enabled" do
before do
@ -54,4 +64,55 @@ RSpec.describe AuthenticationHelper, type: :helper do
expect(recaptcha_configured_and_enabled?).to be(false)
end
end
describe "tooltip classes, attributes and content" do
context "when invite-only-mode enabled and no enabled auth providers" do
before do
allow(SiteConfig).to receive(:invite_only_mode).and_return(true)
allow(SiteConfig).to receive(:authentication_providers).and_return([])
end
it "returns 'crayons-tooltip' class for relevant helpers" do
expect(tooltip_class_on_auth_provider_enablebtn).to eq("crayons-tooltip")
expect(tooltip_class_on_email_auth_disablebtn).to eq("crayons-tooltip")
end
it "returns 'disabled' attribute for relevant helper" do
expect(disabled_attr_on_auth_provider_enablebtn).to eq("disabled")
expect(disabled_attr_on_email_auth_disablebtn).to eq("disabled")
end
it "returns appropriate text for 'tooltip_text_email_or_auth_provider_btns' helper" do
invite_only_mode_warning = "You cannot do this until you disable Invite Only Mode"
only_one_auth_method_warning = "You cannot do this until you enable at least one other registration option"
expect(tooltip_text_email_or_auth_provider_btns).to eq(invite_only_mode_warning)
allow(SiteConfig).to receive(:invite_only_mode).and_return(false)
expect(tooltip_text_email_or_auth_provider_btns).to eq(only_one_auth_method_warning)
end
end
context "when email login enabled and one enabled auth provider" do
before do
allow(SiteConfig).to receive(:allow_email_password_login).and_return(false)
allow(SiteConfig).to receive(:authentication_providers).and_return(["facebook"])
end
it "returns 'crayons-tooltip' class for relevant helpers" do
expect(tooltip_class_on_auth_provider_disablebtn).to eq("crayons-tooltip")
end
it "returns 'disabled' attribute for relevant helper" do
expect(disabled_attr_on_auth_rpovider_disablebtn).to eq("disabled")
end
it "returns appropriate text for 'tooltip_text_email_or_auth_provider_btns' helper" do
only_one_auth_method_warning = "You cannot do this until you enable at least one other registration option"
expect(tooltip_text_email_or_auth_provider_btns).to eq(only_one_auth_method_warning)
end
end
end
end

View file

@ -74,16 +74,16 @@ RSpec.describe "/admin/config", type: :request do
describe "Authentication" do
it "updates enabled authentication providers" do
enabled = Array.wrap(Authentication::Providers.available.first.to_s)
post "/admin/config", params: { site_config: { authentication_providers: enabled },
enabled = Authentication::Providers.available.first.to_s
post "/admin/config", params: { site_config: { auth_providers_to_enable: enabled },
confirmation: confirmation_message }
expect(SiteConfig.authentication_providers).to eq(enabled)
expect(SiteConfig.authentication_providers).to eq([enabled])
end
it "strips empty elements" do
provider = Authentication::Providers.available.first.to_s
enabled = [provider, "", nil]
post "/admin/config", params: { site_config: { authentication_providers: enabled },
enabled = "#{provider}, '', nil"
post "/admin/config", params: { site_config: { auth_providers_to_enable: enabled },
confirmation: confirmation_message }
expect(SiteConfig.authentication_providers).to eq([provider])
end

View file

@ -76,10 +76,11 @@ RSpec.describe "/admin/podcasts", type: :request do
image: fixture_file_upload("files/podcast.png", "image/png"),
slug: "postcast-test-url",
reachable: true,
published: true,
published: true
}
end
# rubocop:disable RSpec/MultipleExpectations
it "updates the podcast" do
put admin_podcast_path(podcast), params: { podcast: update_params }
podcast.reload
@ -97,6 +98,7 @@ RSpec.describe "/admin/podcasts", type: :request do
expect(podcast.reachable).to eq(true)
expect(podcast.published).to eq(true)
end
# rubocop:enable RSpec/MultipleExpectations
it "updates image & pattern_image" do
expect do

View file

@ -8,7 +8,6 @@ RSpec.describe "Creating Comment", type: :system, js: true do
let(:runkit_comment) { compose_runkit_comment "comment 1" }
let(:runkit_comment2) { compose_runkit_comment "comment 2" }
# the article should be created before signing in
let!(:article) { create(:article, user_id: user.id, show_comments: true) }