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>
This commit is contained in:
Michael Kohl 2021-04-12 14:41:09 +07:00 committed by GitHub
parent 1f52ebf302
commit 5406b0576e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
88 changed files with 1345 additions and 1125 deletions

View file

@ -75,7 +75,8 @@ gem "rack-attack", "~> 6.5.0" # Used to throttle requests to prevent brute force
gem "rack-cors", "~> 1.1" # Middleware that will make Rack-based apps CORS compatible
gem "rack-timeout", "~> 0.6" # Rack middleware which aborts requests that have been running for longer than a specified timeout
gem "rails", "~> 6.1" # Ruby on Rails
gem "rails-settings-cached", ">= 2.1.1" # Settings plugin for Rails that makes managing a table of global key, value pairs easy.
# Pinned since we have a monkey-patch against this version
gem "rails-settings-cached", "= 2.5.2" # Settings plugin for Rails that makes managing a table of global key, value pairs easy.
gem "ransack", "~> 2.4" # Searching and sorting
gem "recaptcha", "~> 5.7", require: "recaptcha/rails" # Helpers for the reCAPTCHA API
gem "redcarpet", "~> 3.5" # A fast, safe and extensible Markdown to (X)HTML parser

View file

@ -86,7 +86,7 @@ GEM
activerecord (>= 5.0, < 6.2)
addressable (2.7.0)
public_suffix (>= 2.0.2, < 5.0)
ahoy_email (2.0.2)
ahoy_email (2.0.3)
actionmailer (>= 5)
addressable (>= 2.3.2)
nokogiri
@ -168,7 +168,7 @@ GEM
activesupport (>= 3.2.0)
carrierwave
fastimage
chartkick (3.4.2)
chartkick (4.0.2)
childprocess (3.0.0)
cloudinary (1.20.0)
aws_cf_signer
@ -424,13 +424,13 @@ GEM
listen (3.5.1)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
loofah (2.9.0)
loofah (2.9.1)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
lumberjack (1.2.8)
mail (2.7.1)
mini_mime (>= 0.1.1)
marcel (1.0.0)
marcel (1.0.1)
memoizable (0.4.2)
thread_safe (~> 0.3, >= 0.3.1)
memory_profiler (1.0.0)
@ -462,16 +462,12 @@ GEM
http-2 (~> 0.11)
netrc (0.11.0)
nio4r (2.5.7)
nokogiri (1.11.3-arm64-darwin)
racc (~> 1.4)
nokogiri (1.11.3-x86_64-darwin)
racc (~> 1.4)
nokogiri (1.11.3-x86_64-linux)
racc (~> 1.4)
notiffany (0.1.3)
nenv (~> 0.1)
shellany (~> 0.0)
oauth (0.5.5)
oauth (0.5.6)
oauth2 (1.4.7)
faraday (>= 0.8, < 2.0)
jwt (>= 1.0, < 3.0)
@ -508,7 +504,7 @@ GEM
rack
orm_adapter (0.5.0)
parallel (1.20.1)
parser (3.0.0.0)
parser (3.0.1.0)
ast (~> 2.4.1)
patron (0.13.3)
pg (1.2.3)
@ -843,8 +839,6 @@ GEM
zonebie (0.6.1)
PLATFORMS
arm64-darwin
x86_64-darwin
x86_64-linux
DEPENDENCIES
@ -940,7 +934,7 @@ DEPENDENCIES
rack-host-redirect (~> 1.3)
rack-timeout (~> 0.6)
rails (~> 6.1)
rails-settings-cached (>= 2.1.1)
rails-settings-cached (= 2.5.2)
ransack (~> 2.4)
recaptcha (~> 5.7)
redcarpet (~> 3.5)

View file

@ -56,7 +56,7 @@ class Runtime {
} else if (/Android/i.test(window.navigator.userAgent)) {
return 'Android';
} else if (/Linux/i.test(window.navigator.platform)) {
return 'Linux';
return 'Linux';
}
return 'Unsupported';

View file

@ -1,31 +1,9 @@
module Admin
class ConfigsController < Admin::ApplicationController
include SiteConfigParams
EMOJI_ONLY_FIELDS = %w[community_emoji].freeze
IMAGE_FIELDS =
%w[
main_social_image
logo_png
secondary_logo_url
campaign_sidebar_image
mascot_image_url
mascot_footer_image_url
onboarding_background_image
].freeze
VALID_URL = %r{\A(http|https)://([/|.\w\s-])*.[a-z]{2,5}(:[0-9]{1,5})?(/.*)?\z}.freeze
layout "admin"
before_action :extra_authorization_and_confirmation, only: [:create]
def show
@confirmation_text = confirmation_text
end
class ConfigsController < Admin::SettingsController
include SettingsParams
def create
result = SiteConfigs::Upsert.call(site_config_params)
result = ::Settings::Upsert.call(settings_params)
if result.success?
Audit::Logger.log(:internal, current_user, params.dup)
bust_content_change_caches
@ -34,20 +12,5 @@ module Admin
redirect_to admin_config_path, alert: "😭 #{result.errors.to_sentence}"
end
end
private
def confirmation_text
"My username is @#{current_user.username} and this action is 100% safe and appropriate."
end
def raise_confirmation_mismatch_error
raise ActionController::BadRequest.new, "The confirmation key does not match"
end
def extra_authorization_and_confirmation
not_authorized unless current_user.has_role?(:super_admin)
raise_confirmation_mismatch_error if params.require(:confirmation) != confirmation_text
end
end
end

View file

@ -0,0 +1,22 @@
module Admin
module Settings
class AuthenticationsController < Admin::ApplicationController
def create
result = ::Authentication::SettingsUpsert.call(settings_params)
if result.success?
Audit::Logger.log(:internal, current_user, params.dup)
redirect_to admin_config_path, notice: "Site configuration was successfully updated."
else
redirect_to admin_config_path, alert: "😭 #{result.errors.to_sentence}"
end
end
def settings_params
params
.require(:settings_authentication)
.permit(*::Settings::Authentication.keys, :auth_providers_to_enable)
end
end
end
end

View file

@ -0,0 +1,30 @@
module Admin
class SettingsController < Admin::ApplicationController
MISMATCH_ERROR = "The confirmation key does not match".freeze
before_action :extra_authorization_and_confirmation, only: [:create]
layout "admin"
def create; end
def show
@confirmation_text = confirmation_text
end
private
def extra_authorization_and_confirmation
not_authorized unless current_user.has_role?(:super_admin)
raise_confirmation_mismatch_error if params.require(:confirmation) != confirmation_text
end
def confirmation_text
"My username is @#{current_user.username} and this action is 100% safe and appropriate."
end
def raise_confirmation_mismatch_error
raise ActionController::BadRequest.new, MISMATCH_ERROR
end
end
end

View file

@ -2,7 +2,7 @@ module Api
module V0
module Admin
class ConfigsController < ApiController
include SiteConfigParams
include SettingsParams
before_action :authenticate_with_api_key_or_current_user!
before_action :authorize_super_admin
@ -13,16 +13,30 @@ module Api
end
def update
result = SiteConfigs::Upsert.call(site_config_params)
if result.success?
@site_configs = SiteConfig.all
# NOTE: citizen428 - this is not going to scale but I want to wait
# until we extract at least one more settings model before deciding
# how to change this.
settings_result = ::Settings::Upsert.call(settings_params)
auth_settings_result = ::Authentication::SettingsUpsert.call(auth_settings_params)
if settings_result.success? && auth_settings_result.success?
@site_configs = SiteConfig.all + Settings::Authentication.all
Audit::Logger.log(:internal, @user, params.dup)
bust_content_change_caches
render "show"
else
render json: { error: result.errors.to_sentence, status: 422 }, status: :unprocessable_entity
errors = settings_result.errors + auth_settings_result.errors
render json: { error: errors.to_sentence, status: 422 }, status: :unprocessable_entity
end
end
private
def auth_settings_params
params
.require(:site_config)
.permit(*::Settings::Authentication.keys, :providers_to_enable)
end
end
end
end

View file

@ -1,16 +1,26 @@
module SiteConfigParams
SPECIAL_PARAMS_TO_ADD =
%w[authentication_providers email_addresses meta_keywords credit_prices_in_cents auth_providers_to_enable].freeze
def site_config_params
module SettingsParams
SPECIAL_PARAMS_TO_ADD = %w[
credit_prices_in_cents
email_addresses
meta_keywords
].freeze
def settings_params
has_emails = params.dig(:site_config, :email_addresses).present?
params[:site_config][:email_addresses][:default] = ApplicationConfig["DEFAULT_EMAIL"] if has_emails
params.require(:site_config)&.permit(
(SiteConfig.keys + SPECIAL_PARAMS_TO_ADD).map(&:to_sym),
authentication_providers: [],
settings_keys.map(&:to_sym),
social_media_handles: SiteConfig.social_media_handles.keys,
email_addresses: SiteConfig.email_addresses.keys,
meta_keywords: SiteConfig.meta_keywords.keys,
credit_prices_in_cents: SiteConfig.credit_prices_in_cents.keys,
)
end
private
def settings_keys
SiteConfig.keys + SPECIAL_PARAMS_TO_ADD
end
end

View file

@ -51,7 +51,7 @@ class FeedbackMessagesController < ApplicationController
private
def recaptcha_verified?
recaptcha_params = { secret_key: SiteConfig.recaptcha_secret_key }
recaptcha_params = { secret_key: Settings::Authentication.recaptcha_secret_key }
params["g-recaptcha-response"] && verify_recaptcha(recaptcha_params)
end

View file

@ -13,7 +13,8 @@ class RegistrationsController < Devise::RegistrationsController
end
def create
not_authorized unless SiteConfig.allow_email_password_registration || SiteConfig.waiting_on_first_user
not_authorized unless Settings::Authentication.allow_email_password_registration ||
SiteConfig.waiting_on_first_user
not_authorized if SiteConfig.waiting_on_first_user && ENV["FOREM_OWNER_SECRET"].present? &&
ENV["FOREM_OWNER_SECRET"] != params[:user][:forem_owner_secret]
@ -51,13 +52,13 @@ class RegistrationsController < Devise::RegistrationsController
end
def recaptcha_verified?
recaptcha_params = { secret_key: SiteConfig.recaptcha_secret_key }
recaptcha_params = { secret_key: Settings::Authentication.recaptcha_secret_key }
params["g-recaptcha-response"] && verify_recaptcha(recaptcha_params)
end
def check_allowed_email(resource)
domain = resource.email.split("@").last
allow_list = SiteConfig.allowed_registration_email_domains
allow_list = Settings::Authentication.allowed_registration_email_domains
return if allow_list.empty? || allow_list.include?(domain)
resource.email = nil

View file

@ -96,7 +96,7 @@ module ApplicationHelper
end
def invite_only_mode?
SiteConfig.invite_only_mode?
Settings::Authentication.invite_only_mode?
end
def any_enabled_auth_providers?
@ -274,7 +274,7 @@ module ApplicationHelper
estimated_user_count > LARGE_USERBASE_THRESHOLD
end
def admin_config_label(method, content = nil)
def admin_config_label(method, content = nil, model: SiteConfig)
content ||= tag.span(method.to_s.humanize)
if method.to_sym.in?(VerifySetupCompleted::MANDATORY_CONFIGS)
@ -282,7 +282,8 @@ module ApplicationHelper
content = safe_join([content, required])
end
tag.label(content, class: "site-config__label crayons-field__label", for: "site_config_#{method}")
label_prefix = model.name.split("::").map(&:underscore).join("_")
tag.label(content, class: "site-config__label crayons-field__label", for: "#{label_prefix}_#{method}")
end
def admin_config_description(content)

View file

@ -46,8 +46,9 @@ module AuthenticationHelper
end
def invite_only_mode_or_no_enabled_auth_options
SiteConfig.invite_only_mode ||
(authentication_enabled_providers.none? && !SiteConfig.allow_email_password_registration)
Settings::Authentication.invite_only_mode ||
(authentication_enabled_providers.none? &&
!Settings::Authentication.allow_email_password_registration)
end
def tooltip_class_on_auth_provider_enablebtn

View file

@ -293,8 +293,12 @@ export default class ConfigController extends Controller {
.forEach((provider) => {
const { providerName } = provider.dataset;
if (
!document.getElementById(`site_config_${providerName}_key`).value ||
!document.getElementById(`site_config_${providerName}_secret`).value
!document.getElementById(
`settings_authentication_${providerName}_key`,
).value ||
!document.getElementById(
`settings_authentication_${providerName}_secret`,
).value
) {
providersWithMissingKeys.push(providerName);
}

View file

@ -1,7 +1,9 @@
import { callHistoricalAPI, callReferrersAPI } from './client';
function resetActive(activeButton) {
const buttons = document.querySelectorAll('.crayons-tabs--analytics .crayons-tabs__item');
const buttons = document.querySelectorAll(
'.crayons-tabs--analytics .crayons-tabs__item',
);
for (let i = 0; i < buttons.length; i += 1) {
const button = buttons[i];
button.classList.remove('crayons-tabs__item--current');

View file

@ -27,7 +27,7 @@ function isFormField(element) {
/**
* Function to handle converting key presses to callback functions
*
*
* @param {KeyboardEvent} e Keyboard event
* @param {String} keys special keys formatted in a string
* @param {Array} chain array of past keys
@ -96,7 +96,7 @@ export function useKeyboardShortcuts(
const [keyChain, setKeyChain] = useState([]);
const [mergedOptions, setMergedOptions] = useState({
...defaultOptions,
...options
...options,
});
// update mergedOptions if options prop changes

View file

@ -0,0 +1,82 @@
module Constants
module Settings
module Authentication
DETAILS = {
allowed_registration_email_domains: {
description: "Restrict registration to only certain emails? (comma-separated list)",
placeholder: "dev.to, forem.com, codenewbie.org"
},
apple_client_id: {
description:
"The \"App Bundle\" code for the Authentication Service configured in the Apple Developer Portal",
placeholder: "com.example.app"
},
apple_team_id: {
description:
"The \"Team ID\" of your Apple Developer Account",
placeholder: ""
},
apple_key_id: {
description:
"The \"Key ID\" from the Authentication Service configured in the Apple Developer Portal",
placeholder: ""
},
apple_pem: {
description:
"The \"PEM\" key from the Authentication Service configured in the Apple Developer Portal",
placeholder: "-----BEGIN PRIVATE KEY-----\nMIGTAQrux...QPe8Yb\n-----END PRIVATE KEY-----\\n"
},
display_email_domain_allow_list_publicly: {
description: "Do you want to display the list of allowed domains, or keep it private?"
},
facebook_key: {
description:
"The \"App ID\" portion of the Basic Settings section of the App page on the Facebook Developer Portal",
placeholder: ""
},
facebook_secret: {
description:
"The \"App Secret\" portion of the Basic Settings section of the App page on the Facebook Developer Portal",
placeholder: ""
},
github_key: {
description: "The \"Client ID\" portion of the GitHub Oauth Apps portal",
placeholder: ""
},
github_secret: {
description: "The \"Client Secret\" portion of the GitHub Oauth Apps portal",
placeholder: ""
},
invite_only_mode: {
description: "Only users invited by email can join this community.",
placeholder: ""
},
recaptcha_site_key: {
description: "Add the site key for Google reCAPTCHA, which is used for reporting abuse",
placeholder: "What is the Google reCAPTCHA site key?"
},
recaptcha_secret_key: {
description: "Add the secret key for Google reCAPTCHA, which is used for reporting abuse",
placeholder: "What is the Google reCAPTCHA secret key?"
},
require_captcha_for_email_password_registration: {
description: "People will be required to fill out a captcha when
they're creating a new account in your community",
placeholder: ""
},
twitter_key: {
description: "The \"API key\" portion of consumer keys in the Twitter developer portal.",
placeholder: ""
},
twitter_secret: {
description: "The \"API secret key\" portion of consumer keys in the Twitter developer portal.",
placeholder: ""
},
providers: {
description: "How can users sign in?",
placeholder: ""
}
}.freeze
end
end
end

View file

@ -4,19 +4,6 @@ module Constants
SVG_PLACEHOLDER = "<svg ...></svg>".freeze
DETAILS = {
require_captcha_for_email_password_registration: {
description: "People will be required to fill out a captcha when
they're creating a new account in your community",
placeholder: ""
},
allowed_registration_email_domains: {
description: "Restrict registration to only certain emails? (comma-separated list)",
placeholder: "dev.to, forem.com, codenewbie.org"
},
authentication_providers: {
description: "How can users sign in?",
placeholder: ""
},
campaign_articles_require_approval: {
description: "",
placeholder: "Campaign stories show up on sidebar with approval?"
@ -90,9 +77,6 @@ module Constants
default_font: {
description: "Determines the default Base Reading Font (registered users can change this in their UX settings)"
},
display_email_domain_allow_list_publicly: {
description: "Do you want to display the list of allowed domains, or keep it private?"
},
display_jobs_banner: {
description: "Display a jobs banner that points users to the jobs page when they type 'job'" \
"or 'jobs' in the search box",
@ -110,36 +94,6 @@ module Constants
description: "The label for the top of the experience level range of a post",
placeholder: "Senior Devs"
},
facebook_key: {
description:
"The \"App ID\" portion of the Basic Settings section of the App page on the Facebook Developer Portal",
placeholder: ""
},
facebook_secret: {
description:
"The \"App Secret\" portion of the Basic Settings section of the App page on the Facebook Developer Portal",
placeholder: ""
},
apple_client_id: {
description:
"The \"App Bundle\" code for the Authentication Service configured in the Apple Developer Portal",
placeholder: "com.example.app"
},
apple_team_id: {
description:
"The \"Team ID\" of your Apple Developer Account",
placeholder: ""
},
apple_key_id: {
description:
"The \"Key ID\" from the Authentication Service configured in the Apple Developer Portal",
placeholder: ""
},
apple_pem: {
description:
"The \"PEM\" key from the Authentication Service configured in the Apple Developer Portal",
placeholder: "-----BEGIN PRIVATE KEY-----\nMIGTAQrux...QPe8Yb\n-----END PRIVATE KEY-----\\n"
},
favicon_url: {
description: "Used as the site favicon",
placeholder: IMAGE_PLACEHOLDER
@ -157,14 +111,6 @@ module Constants
description: "Determines background/border of buttons etc. Must be dark enough to contrast with white text.",
placeholder: "#0a0a0a"
},
github_key: {
description: "The \"Client ID\" portion of the GitHub Oauth Apps portal",
placeholder: ""
},
github_secret: {
description: "The \"Client Secret\" portion of the GitHub Oauth Apps portal",
placeholder: ""
},
ga_tracking_id: {
description: "Google Analytics Tracking ID, e.g. UA-71991000-1",
placeholder: ""
@ -177,10 +123,6 @@ module Constants
description: "Minimum score needed for a post to show up on the unauthenticated home page.",
placeholder: "0"
},
invite_only_mode: {
description: "Only users invited by email can join this community.",
placeholder: ""
},
jobs_url: {
description: "URL of the website where open positions are posted",
placeholder: "Jobs URL"
@ -258,14 +200,6 @@ module Constants
description: "Determines how often periodic email digests are sent",
placeholder: 2
},
recaptcha_site_key: {
description: "Add the site key for Google reCAPTCHA, which is used for reporting abuse",
placeholder: "What is the Google reCAPTCHA site key?"
},
recaptcha_secret_key: {
description: "Add the secret key for Google reCAPTCHA, which is used for reporting abuse",
placeholder: "What is the Google reCAPTCHA secret key?"
},
secondary_logo_url: {
description: "A place for an alternate logo, if you have one. Used throughout member onboarding and in some sign in forms.",
placeholder: IMAGE_PLACEHOLDER
@ -327,14 +261,6 @@ module Constants
description: "Used as the twitter hashtag of the community",
placeholder: "#DEVCommunity"
},
twitter_key: {
description: "The \"API key\" portion of consumer keys in the Twitter developer portal.",
placeholder: ""
},
twitter_secret: {
description: "The \"API secret key\" portion of consumer keys in the Twitter developer portal.",
placeholder: ""
},
user_considered_new_days: {
description: "The number of days a user is considered new. The default is 3 days, but you can disable this entirely by inputting 0.",
placeholder: ::SiteConfig.user_considered_new_days

View file

@ -0,0 +1,44 @@
module Settings
class Authentication < RailsSettings::Base
self.table_name = :settings_authentications
# The configuration is cached, change this if you want to force update
# the cache, or call Settings::Authentication.clear_cache
cache_prefix { "v1" }
field :allow_email_password_login, type: :boolean, default: true
field :allow_email_password_registration, type: :boolean, default: false
field :allowed_registration_email_domains, type: :array, default: %w[], validates: {
valid_domain_csv: true
}
field :apple_client_id, type: :string
field :apple_key_id, type: :string
field :apple_pem, type: :string
field :apple_team_id, type: :string
field :display_email_domain_allow_list_publicly, type: :boolean, default: false
field :facebook_key, type: :string
field :facebook_secret, type: :string
field :github_key, type: :string, default: ApplicationConfig["GITHUB_KEY"]
field :github_secret, type: :string, default: ApplicationConfig["GITHUB_SECRET"]
field :invite_only_mode, type: :boolean, default: false
field :providers, type: :array, default: %w[]
field :require_captcha_for_email_password_registration, type: :boolean, default: false
field :twitter_key, type: :string, default: ApplicationConfig["TWITTER_KEY"]
field :twitter_secret, type: :string, default: ApplicationConfig["TWITTER_SECRET"]
# Google ReCATPCHA keys
field :recaptcha_site_key, type: :string, default: ApplicationConfig["RECAPTCHA_SITE"]
field :recaptcha_secret_key, type: :string, default: ApplicationConfig["RECAPTCHA_SECRET"]
# Apple uses different keys than the usual `PROVIDER_NAME_key` or
# `PROVIDER_NAME_secret` so these will help the generalized authentication
# code to work, i.e. https://github.com/forem/forem/blob/master/app/helpers/authentication_helper.rb#L26-L29
def self.apple_key
return unless apple_client_id.present? && apple_key_id.present? &&
apple_pem.present? && apple_team_id.present?
"present"
end
singleton_class.alias_method(:apple_secret, :apple_key)
end
end

View file

@ -29,26 +29,12 @@ class SiteConfig < RailsSettings::Base
field :health_check_token, type: :string
field :video_encoder_key, type: :string
# Authentication
field :allow_email_password_registration, type: :boolean, default: false
field :allow_email_password_login, type: :boolean, default: true
# NOTE: @citizen428 These two values will be removed once we fully migrated
# to Settings::Authentication. Until then we need them for the data update script.
field :allowed_registration_email_domains, type: :array, default: %w[], validates: {
valid_domain_csv: true
}
field :display_email_domain_allow_list_publicly, type: :boolean, default: false
field :require_captcha_for_email_password_registration, type: :boolean, default: false
field :authentication_providers, type: :array, default: %w[]
field :invite_only_mode, type: :boolean, default: false
field :twitter_key, type: :string, default: ApplicationConfig["TWITTER_KEY"]
field :twitter_secret, type: :string, default: ApplicationConfig["TWITTER_SECRET"]
field :github_key, type: :string, default: ApplicationConfig["GITHUB_KEY"]
field :github_secret, type: :string, default: ApplicationConfig["GITHUB_SECRET"]
field :facebook_key, type: :string
field :facebook_secret, type: :string
field :apple_client_id, type: :string
field :apple_key_id, type: :string
field :apple_pem, type: :string
field :apple_team_id, type: :string
# Campaign
field :campaign_call_to_action, type: :string, default: "Share your project"
@ -98,10 +84,6 @@ class SiteConfig < RailsSettings::Base
# Google Analytics Tracking ID, e.g. UA-71991000-1
field :ga_tracking_id, type: :string, default: ApplicationConfig["GA_TRACKING_ID"]
# Google ReCATPCHA keys
field :recaptcha_site_key, type: :string, default: ApplicationConfig["RECAPTCHA_SITE"]
field :recaptcha_secret_key, type: :string, default: ApplicationConfig["RECAPTCHA_SECRET"]
# Images
field :main_social_image,
type: :string,
@ -248,17 +230,6 @@ class SiteConfig < RailsSettings::Base
app_domain == "dev.to"
end
# Apple uses different keys than the usual `PROVIDER_NAME_key` or
# `PROVIDER_NAME_secret` so these will help the generalized authentication
# code to work, i.e. https://github.com/forem/forem/blob/master/app/helpers/authentication_helper.rb#L26-L29
def self.apple_key
return unless apple_client_id.present? && apple_key_id.present? &&
apple_pem.present? && apple_team_id.present?
"present"
end
singleton_class.__send__(:alias_method, :apple_secret, :apple_key)
# To get default values
def self.get_default(field)
get_field(field)[:default]

View file

@ -44,9 +44,9 @@ module Authentication
# TODO: [@forem/oss] ideally this should be "available - disabled"
# we can get there once we have feature flags
def self.enabled
return [] if SiteConfig.invite_only_mode
return [] if Settings::Authentication.invite_only_mode
SiteConfig.authentication_providers.map(&:to_sym).sort
Settings::Authentication.providers.map(&:to_sym).sort
end
def self.enabled_for_user(user)

View file

@ -0,0 +1,68 @@
module Authentication
class SettingsUpsert
attr_reader :errors
def self.call(configs)
new(configs).call
end
def initialize(configs)
@configs = configs
@errors = []
end
def call
upsert_configs
self
end
def success?
@errors.none?
end
private
# NOTE: @citizen428 - This was adapted from Settings::Upsert. I'll see if
# a pattern for refactoring emerges in the future, but for now I'll leave
# this as-is.
def upsert_configs
@configs.each do |key, value|
if key == "auth_providers_to_enable"
update_enabled_providers(value) unless value.class.name != "String"
elsif value.is_a?(Array) && value.any?
Settings::Authentication.public_send("#{key}=", value.reject(&:blank?))
elsif value.present?
Settings::Authentication.public_send("#{key}=", value.strip)
end
rescue ActiveRecord::RecordInvalid => e
@errors << e.message
next
end
end
def update_enabled_providers(value)
enabled_providers = value.split(",").filter_map do |entry|
entry unless invalid_provider_entry(entry)
end
return if email_login_disabled_with_one_or_less_auth_providers(enabled_providers)
Settings::Authentication.providers = enabled_providers
end
def invalid_provider_entry(entry)
entry.blank? ||
Authentication::Providers.available.map(&:to_s).exclude?(entry) ||
provider_keys_missing(entry)
end
def provider_keys_missing(entry)
Settings::Authentication.public_send("#{entry}_key").blank? ||
Settings::Authentication.public_send("#{entry}_secret").blank?
end
def email_login_disabled_with_one_or_less_auth_providers(enabled_providers)
!Settings::Authentication.allow_email_password_login &&
enabled_providers.count <= 1
end
end
end

View file

@ -7,8 +7,8 @@ module Github
# @param credentials [Hash] the OAuth credentials, {client_id:, client_secret:} or {access_token:}
def initialize(credentials = nil)
credentials ||= {
client_id: SiteConfig.github_key,
client_secret: SiteConfig.github_secret
client_id: Settings::Authentication.github_key,
client_secret: Settings::Authentication.github_secret
}
@credentials = check_credentials!(credentials)
end

View file

@ -31,7 +31,8 @@ module ReCaptcha
private
def keys_configured?
SiteConfig.recaptcha_site_key.present? && SiteConfig.recaptcha_secret_key.present?
Settings::Authentication.recaptcha_site_key.present? &&
Settings::Authentication.recaptcha_secret_key.present?
end
end
end

View file

@ -1,13 +1,10 @@
module ReCaptcha
class CheckRegistrationEnabled
def self.call
new.call
end
def call
# ReCaptcha::CheckEnabled.call without a user parameter will return `true`
# if the ReCaptcha SiteConfig keys are configured, and `false` otherwise
ReCaptcha::CheckEnabled.call && SiteConfig.require_captcha_for_email_password_registration
ReCaptcha::CheckEnabled.call &&
Settings::Authentication.require_captcha_for_email_password_registration
end
end
end

View file

@ -1,4 +1,4 @@
module SiteConfigs
module Settings
class Upsert
EMOJI_ONLY_FIELDS = %w[community_emoji].freeze
VALID_DOMAIN = /^[a-zA-Z0-9]{1,61}[a-zA-Z0-9](?:\.[a-zA-Z]{2,})+$/.freeze
@ -34,9 +34,7 @@ module SiteConfigs
def upsert_configs
@configs.each do |key, value|
if key == "auth_providers_to_enable"
update_enabled_auth_providers(value) unless value.class.name != "String"
elsif value.is_a?(Array)
if 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?
@ -60,27 +58,6 @@ module SiteConfigs
@configs[:credit_prices_in_cents]&.transform_values!(&:to_i)
end
def update_enabled_auth_providers(value)
enabled_providers = value.split(",").filter_map do |entry|
entry unless invalid_provider_entry(entry)
end
SiteConfig.public_send("authentication_providers=", enabled_providers) unless
email_login_disabled_with_one_or_less_auth_providers(enabled_providers)
end
def email_login_disabled_with_one_or_less_auth_providers(enabled_providers)
!SiteConfig.allow_email_password_login && enabled_providers.count <= 1
end
def invalid_provider_entry(entry)
entry.blank? || Authentication::Providers.available.map(&:to_s).exclude?(entry) ||
provider_keys_missing(entry)
end
def provider_keys_missing(entry)
SiteConfig.public_send("#{entry}_key").blank? || SiteConfig.public_send("#{entry}_secret").blank?
end
def create_tags_if_not_created
# Bulk create tags if they should exist.
# This is an acts-as-taggable-on as used on saving of an Article, etc.

View file

@ -57,8 +57,8 @@ module TwitterClient
def target
Twitter::REST::Client.new(
consumer_key: SiteConfig.twitter_key.presence || ApplicationConfig["TWITTER_KEY"],
consumer_secret: SiteConfig.twitter_secret.presence || ApplicationConfig["TWITTER_SECRET"],
consumer_key: Settings::Authentication.twitter_key.presence || ApplicationConfig["TWITTER_KEY"],
consumer_secret: Settings::Authentication.twitter_secret.presence || ApplicationConfig["TWITTER_SECRET"],
user_agent: "TwitterRubyGem/#{Twitter::Version} (#{URL.url})",
timeouts: {
connect: 5,

View file

@ -11,42 +11,42 @@ requires a custom partial
<div class="crayons-field">
<%= admin_config_label :apple_client_id %>
<p class="crayons-field__description">
<%= Constants::SiteConfig::DETAILS[:apple_client_id][:description] %>
<%= Constants::Settings::Authentication::DETAILS[:apple_client_id][:description] %>
</p>
<%= f.text_field :apple_client_id,
class: "crayons-textfield",
value: SiteConfig.apple_client_id,
placeholder: Constants::SiteConfig::DETAILS[:apple_client_id][:placeholder] %>
value: Settings::Authentication.apple_client_id,
placeholder: Constants::Settings::Authentication::DETAILS[:apple_client_id][:placeholder] %>
</div>
<div class="crayons-field">
<%= admin_config_label :apple_key_id %>
<p class="crayons-field__description">
<%= Constants::SiteConfig::DETAILS[:apple_key_id][:description] %>
<%= Constants::Settings::Authentication::DETAILS[:apple_key_id][:description] %>
<p>
<%= f.text_field :apple_key_id,
class: "crayons-textfield",
value: SiteConfig.apple_key_id,
placeholder: Constants::SiteConfig::DETAILS[:apple_key_id][:placeholder] %>
value: Settings::Authentication.apple_key_id,
placeholder: Constants::Settings::Authentication::DETAILS[:apple_key_id][:placeholder] %>
</div>
<div class="crayons-field">
<%= admin_config_label :apple_pem %>
<p class="crayons-field__description">
<%= Constants::SiteConfig::DETAILS[:apple_pem][:description] %>
<%= Constants::Settings::Authentication::DETAILS[:apple_pem][:description] %>
<p>
<%= f.text_field :apple_pem,
class: "crayons-textfield",
value: SiteConfig.apple_pem,
placeholder: Constants::SiteConfig::DETAILS[:apple_pem][:placeholder] %>
value: Settings::Authentication.apple_pem,
placeholder: Constants::Settings::Authentication::DETAILS[:apple_pem][:placeholder] %>
</div>
<div class="crayons-field">
<%= admin_config_label :apple_team_id %>
<p class="crayons-field__description">
<%= Constants::SiteConfig::DETAILS[:apple_team_id][:description] %>
<%= Constants::Settings::Authentication::DETAILS[:apple_team_id][:description] %>
<p>
<%= f.text_field :apple_team_id,
class: "crayons-textfield",
value: SiteConfig.apple_team_id,
placeholder: Constants::SiteConfig::DETAILS[:apple_team_id][:placeholder] %>
value: Settings::Authentication.apple_team_id,
placeholder: Constants::Settings::Authentication::DETAILS[:apple_team_id][:placeholder] %>
</div>
<div class="crayons-btn-actions">
<% if authentication_provider_enabled?(provider) %>

View file

@ -2,22 +2,22 @@
<div class="crayons-field">
<%= admin_config_label :"#{provider.provider_name}_key" %>
<p class="crayons-field__description">
<%= Constants::SiteConfig::DETAILS[:"#{provider.provider_name}_key"][:description] %>
<%= Constants::Settings::Authentication::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] %>
value: Settings::Authentication.public_send("#{provider.provider_name}_key"),
placeholder: Constants::Settings::Authentication::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] %>
<%= Constants::Settings::Authentication::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] %>
value: Settings::Authentication.public_send("#{provider.provider_name}_secret"),
placeholder: Constants::Settings::Authentication::DETAILS[:"#{provider.provider_name}_secret"][:placeholder] %>
</div>
<div class="crayons-btn-actions">
<% if authentication_provider_enabled?(provider) %>

File diff suppressed because it is too large Load diff

View file

@ -24,7 +24,7 @@
</div>
<% else %>
<div class="registration__actions-email" id="sign-in-password-form">
<% if SiteConfig.allow_email_password_login %>
<% if Settings::Authentication.allow_email_password_login %>
<div class="registration__hr">
<span class="registration__hr-label">
Have a password? Continue with your email address

View file

@ -1,6 +1,6 @@
<% title "Welcome!" %>
<% if params[:state] == "email_signup" && SiteConfig.allow_email_password_registration %>
<% if params[:state] == "email_signup" && Settings::Authentication.allow_email_password_registration %>
<%= render "shared/authentication/email_registration_form" %>
<% elsif params[:state] == "email_signup" %>
<div class="crayons-notice crayons-notice--danger mb-10 mt-8 mx-auto" style="width: 680px;max-width:94%;">

View file

@ -69,7 +69,7 @@
<% if ReCaptcha::CheckEnabled.call(current_user) %>
<div class="recaptcha-tag-container">
<%= recaptcha_tags site_key: SiteConfig.recaptcha_site_key %>
<%= recaptcha_tags site_key: Settings::Authentication.recaptcha_site_key %>
</div>
<% end %>

View file

@ -29,15 +29,15 @@
<% else %>
<p class="pb-4 fw-bold">Create your account</p>
<% end %>
<% if SiteConfig.display_email_domain_allow_list_publicly &&
SiteConfig.allowed_registration_email_domains.any? %>
<% if Settings::Authentication.display_email_domain_allow_list_publicly &&
Settings::Authentication.allowed_registration_email_domains.any? %>
<div class="crayons-notice crayons-notice--info">
<% if SiteConfig.allowed_registration_email_domains.one? %>
Registration restricted to <strong>@<%= SiteConfig.allowed_registration_email_domains.first %></strong> emails.
<% if Settings::Authentication.allowed_registration_email_domains.one? %>
Registration restricted to <strong>@<%= Settings::Authentication.allowed_registration_email_domains.first %></strong> emails.
<% else %>
Registration restricted to the following emails
<ul>
<% SiteConfig.allowed_registration_email_domains.each do |domain| %>
<% Settings::Authentication.allowed_registration_email_domains.each do |domain| %>
<li>
<strong>@<%= domain %></strong></strong>
</li>
@ -89,7 +89,7 @@
<% if ReCaptcha::CheckRegistrationEnabled.call %>
<div class="recaptcha-tag-container mt-2">
<%= recaptcha_tags site_key: SiteConfig.recaptcha_site_key %>
<%= recaptcha_tags site_key: Settings::Authentication.recaptcha_site_key %>
</div>
<% end %>

View file

@ -8,7 +8,7 @@
<% end %>
<% end %>
<% end %>
<% if params[:state] == "new-user" && SiteConfig.allow_email_password_registration && !SiteConfig.invite_only_mode %>
<% if params[:state] == "new-user" && Settings::Authentication.allow_email_password_registration && !Settings::Authentication.invite_only_mode %>
<%= link_to "#{inline_svg_tag('email.svg', aria: true, class: 'crayons-icon', title: 'email')}Sign up with Email".html_safe,
request.params.merge(state: "email_signup").except("i"),
class: "crayons-btn crayons-btn--l crayons-btn--brand-email crayons-btn--icon-left whitespace-nowrap",

View file

@ -1,27 +1,27 @@
TWITTER_OMNIAUTH_SETUP = lambda do |env|
env["omniauth.strategy"].options[:consumer_key] = SiteConfig.twitter_key
env["omniauth.strategy"].options[:consumer_secret] = SiteConfig.twitter_secret
env["omniauth.strategy"].options[:consumer_key] = Settings::Authentication.twitter_key
env["omniauth.strategy"].options[:consumer_secret] = Settings::Authentication.twitter_secret
end
GITHUB_OMNIUATH_SETUP = lambda do |env|
env["omniauth.strategy"].options[:client_id] = SiteConfig.github_key
env["omniauth.strategy"].options[:client_secret] = SiteConfig.github_secret
env["omniauth.strategy"].options[:client_id] = Settings::Authentication.github_key
env["omniauth.strategy"].options[:client_secret] = Settings::Authentication.github_secret
env["omniauth.strategy"].options[:scope] = "user:email"
end
FACEBOOK_OMNIAUTH_SETUP = lambda do |env|
env["omniauth.strategy"].options[:client_id] = SiteConfig.facebook_key
env["omniauth.strategy"].options[:client_secret] = SiteConfig.facebook_secret
env["omniauth.strategy"].options[:client_id] = Settings::Authentication.facebook_key
env["omniauth.strategy"].options[:client_secret] = Settings::Authentication.facebook_secret
env["omniauth.strategy"].options[:token_params][:parse] = :json
end
APPLE_OMNIAUTH_SETUP = lambda do |env|
env["omniauth.strategy"].options[:client_id] = SiteConfig.apple_client_id
env["omniauth.strategy"].options[:client_id] = Settings::Authentication.apple_client_id
env["omniauth.strategy"].options[:scope] = "email name"
env["omniauth.strategy"].options[:key_id] = SiteConfig.apple_key_id
env["omniauth.strategy"].options[:pem] = SiteConfig.apple_pem.to_s.gsub("\\n", "\n")
env["omniauth.strategy"].options[:key_id] = Settings::Authentication.apple_key_id
env["omniauth.strategy"].options[:pem] = Settings::Authentication.apple_pem.to_s.gsub("\\n", "\n")
env["omniauth.strategy"].options[:provider_ignores_state] = true
env["omniauth.strategy"].options[:team_id] = SiteConfig.apple_team_id
env["omniauth.strategy"].options[:team_id] = Settings::Authentication.apple_team_id
end
Devise.setup do |config|

View file

@ -0,0 +1,44 @@
require "request_store"
# Completely remove original implementation
RailsSettings.__send__(:remove_const, :RequestCache)
module RailsSettings
class Base < ActiveRecord::Base # rubocop:disable Rails/ApplicationRecord
def self.inherited(subclass)
super
# Define a unique RequestCache class for each settings klass
request_cache = Class.new(ActiveSupport::CurrentAttributes) do
attribute :settings
end
subclass.const_set(:RequestCache, request_cache)
# Override existing methods to use the local RequestCache class
subclass.instance_eval do
define_singleton_method(:cache_key) do
subclass_cache_key = subclass.name.underscore.tr("/", "_")
scope = [subclass_cache_key]
scope << @cache_prefix.call if @cache_prefix
scope.join("/")
end
define_singleton_method(:clear_cache) do
subclass::RequestCache.reset
Rails.cache.delete(cache_key)
end
define_singleton_method(:_all_settings) do
subclass::RequestCache.settings ||=
Rails.cache.fetch(cache_key, expires_in: 1.week) do
vars = unscoped.select("var, value")
result = {}
vars.each { |record| result[record.var] = record.value }
result.with_indifferent_access
end
end
singleton_class.instance_eval { private :_all_settings }
end
end
end
end

View file

@ -53,6 +53,9 @@ Rails.application.routes.draw do
resources :organization_memberships, only: %i[update destroy create]
resources :permissions, only: %i[index]
resources :reactions, only: [:update]
namespace :settings do
resources :authentications, only: [:create]
end
namespace :users do
resources :gdpr_delete_requests, only: %i[index destroy]
end

View file

@ -91,7 +91,6 @@ describe('Authentication Section', () => {
.findByText('Update Site Configuration')
.click();
cy.findByText('Setup not complete').should('be.visible');
cy.get('.crayons-modal__box__body > ul > li')
.contains('facebook')
.should('be.visible');
@ -107,8 +106,8 @@ describe('Authentication Section', () => {
cy.get('@authSectionForm').findByText('Authentication').click();
cy.get('#facebook-auth-btn').click();
cy.get('#site_config_facebook_key').type('randomkey');
cy.get('#site_config_facebook_secret').type('randomsecret');
cy.get('#settings_authentication_facebook_key').type('randomkey');
cy.get('#settings_authentication_facebook_secret').type('randomsecret');
cy.get('@user').then(({ username }) => {
cy.get('@authSectionForm')
.findByPlaceholderText('Confirmation text')

View file

@ -127,16 +127,16 @@ Cypress.Commands.add(
) => {
return cy.request(
'POST',
'/admin/config',
`utf8=%E2%9C%93&site_config%5Binvite_only_mode%5D=${toPayload(
'/admin/settings/authentications',
`utf8=%E2%9C%93&settings_authentication%5Binvite_only_mode%5D=${toPayload(
inviteOnlyMode,
)}&site_config%5Ballow_email_password_registration%5D=${toPayload(
)}&settings_authentication%5Ballow_email_password_registration%5D=${toPayload(
emailRegistration,
)}&site_config%5Ballowed_registration_email_domains%5D=${allowedEmailDomains}&site_config%5Bdisplay_email_domain_allow_list_publicly%5D=${toPayload(
)}&settings_authentication%5Ballowed_registration_email_domains%5D=${allowedEmailDomains}&settings_authentication%5Bdisplay_email_domain_allow_list_publicly%5D=${toPayload(
publicEmailDomainList,
)}&site_config%5Brequire_captcha_for_email_password_registration%5D=${toPayload(
)}&settings_authentication%5Brequire_captcha_for_email_password_registration%5D=${toPayload(
requireRecaptcha,
)}&site_config%5Brecaptcha_site_key%5D=${recaptchaSiteKey}&site_config%5Brecaptcha_secret_key%5D=${recaptchaSecretKey}&site_config%5Bauth_providers_to_enable%5D=${authProvidersToEnable}&site_config%5Bfacebook_key%5D=${facebookKey}&site_config%5Bfacebook_secret%5D=${facebookSecret}&site_config%5Bgithub_key%5D=${githubKey}&site_config%5Bgithub_secret%5D=${githubSecret}&site_config%5Btwitter_key%5D=${twitterKey}&site_config%5Btwitter_secret%5D=${twitterSecret}&confirmation=My+username+is+%40${username}+and+this+action+is+100%25+safe+and+appropriate.&commit=Update+Site+Configuration`,
)}&settings_authentication%5Brecaptcha_site_key%5D=${recaptchaSiteKey}&settings_authentication%5Brecaptcha_secret_key%5D=${recaptchaSecretKey}&settings_authentication%5Bauth_providers_to_enable%5D=${authProvidersToEnable}&settings_authentication%5Bfacebook_key%5D=${facebookKey}&settings_authentication%5Bfacebook_secret%5D=${facebookSecret}&settings_authentication%5Bgithub_key%5D=${githubKey}&settings_authentication%5Bgithub_secret%5D=${githubSecret}&settings_authentication%5Btwitter_key%5D=${twitterKey}&settings_authentication%5Btwitter_secret%5D=${twitterSecret}&confirmation=My+username+is+%40${username}+and+this+action+is+100%25+safe+and+appropriate.&commit=Update+Site+Configuration`,
);
},
);

View file

@ -0,0 +1,16 @@
class CreateSettings < ActiveRecord::Migration[6.0]
def self.up
create_table :settings_authentications do |t|
t.string :var, null: false
t.text :value, null: true
t.timestamps
end
add_index :settings_authentications, :var, unique: true
end
def self.down
drop_table :settings_authentications
end
end

View file

@ -1048,6 +1048,14 @@ ActiveRecord::Schema.define(version: 2021_03_31_181505) do
t.index ["name"], name: "index_roles_on_name"
end
create_table "settings_authentications", force: :cascade do |t|
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.text "value"
t.string "var", null: false
t.index ["var"], name: "index_settings_authentications_on_var", unique: true
end
create_table "site_configs", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false

View file

@ -17,7 +17,7 @@ puts "Seeding with multiplication factor: #{SEEDS_MULTIPLIER}\n\n"
SiteConfig.public = true
SiteConfig.waiting_on_first_user = false
SiteConfig.authentication_providers = Authentication::Providers.available
Settings::Authentication.providers = Authentication::Providers.available
##############################################################################

View file

@ -0,0 +1,42 @@
module DataUpdateScripts
class MoveAuthenticationSettings
AUTHENTICATION_SETTINGS = %w[
allow_email_password_login
allow_email_password_registration
apple_client_id
apple_key_id
apple_pem
apple_team_id
display_email_domain_allow_list_publicly
facebook_key
facebook_secret
github_key
github_secret
invite_only_mode
require_captcha_for_email_password_registration
twitter_key
twitter_secret
].freeze
ATTRIBUTES = %i[var value created_at updated_at].freeze
def run
return if Settings::Authentication.any?
SiteConfig.transaction do
config_relation = SiteConfig.where(var: AUTHENTICATION_SETTINGS)
config_values = config_relation.pluck(*ATTRIBUTES).map do |values|
ATTRIBUTES.zip(values).to_h
end
Settings::Authentication.insert_all(config_values) if config_values.present?
# This field has a validation we don't want to skip
Settings::Authentication.allowed_registration_email_domains =
SiteConfig.allowed_registration_email_domains
# This field got renamed so we migrate it explicitly
Settings::Authentication.providers = SiteConfig.authentication_providers
end
end
end
end

View file

@ -58,8 +58,8 @@ RSpec.describe AuthenticationHelper, type: :helper do
describe "#authentication_provider_enabled?" do
before do
allow(SiteConfig).to receive(:invite_only_mode).and_return(false)
allow(SiteConfig).to receive(:authentication_providers).and_return(%i[twitter github])
allow(Settings::Authentication).to receive(:invite_only_mode).and_return(false)
allow(Settings::Authentication).to receive(:providers).and_return(%i[twitter github])
end
it "returns true when a provider has been enabled" do
@ -76,9 +76,9 @@ RSpec.describe AuthenticationHelper, type: :helper do
describe "tooltip classes, attributes and content" do
context "when invite-only-mode enabled and no enabled registration options" do
before do
allow(SiteConfig).to receive(:invite_only_mode).and_return(true)
allow(SiteConfig).to receive(:authentication_providers).and_return([])
allow(SiteConfig).to receive(:allow_email_password_registration).and_return(false)
allow(Settings::Authentication).to receive(:invite_only_mode).and_return(true)
allow(Settings::Authentication).to receive(:providers).and_return([])
allow(Settings::Authentication).to receive(:allow_email_password_registration).and_return(false)
end
it "returns 'crayons-tooltip' class for relevant helpers" do

View file

@ -0,0 +1,19 @@
require "rails_helper"
RSpec.describe Settings::Authentication, type: :model do
describe "validations" do
describe "validating domain lists" do
it "allows valid domain lists" do
expect do
described_class.allowed_registration_email_domains = "example.com, example2.com"
end.not_to raise_error
end
it "rejects invalid domain lists" do
expect do
described_class.allowed_registration_email_domains = "example.com, e.c"
end.to raise_error(/must be a comma-separated list of valid domains/)
end
end
end
end

View file

@ -27,20 +27,6 @@ RSpec.describe SiteConfig, type: :model do
end
end
describe "validating domain lists" do
it "allows valid domain lists" do
expect do
described_class.allowed_registration_email_domains = "example.com, example2.com"
end.not_to raise_error
end
it "rejects invalid domain lists" do
expect do
described_class.allowed_registration_email_domains = "example.com, e.c"
end.to raise_error(/must be a comma-separated list of valid domains/)
end
end
describe "validating emojis strings" do
it "allows emoji-only strings" do
expect do

View file

@ -30,7 +30,7 @@ RSpec.describe User, type: :model do
before do
omniauth_mock_providers_payload
allow(SiteConfig).to receive(:authentication_providers).and_return(Authentication::Providers.available)
allow(Settings::Authentication).to receive(:providers).and_return(Authentication::Providers.available)
end
describe "validations" do
@ -884,7 +884,7 @@ RSpec.describe User, type: :model do
end
it "returns true if the user has all the enabled providers" do
allow(SiteConfig).to receive(:authentication_providers).and_return(Authentication::Providers.available)
allow(Settings::Authentication).to receive(:providers).and_return(Authentication::Providers.available)
user = create(:user, :with_identity)

View file

@ -23,7 +23,7 @@ RSpec.describe GithubRepoPolicy, type: :policy do
before do
omniauth_mock_github_payload
allow(SiteConfig).to receive(:authentication_providers).and_return(Authentication::Providers.available)
allow(Settings::Authentication).to receive(:providers).and_return(Authentication::Providers.available)
end
it { is_expected.to permit_actions(%i[index update_or_create]) }

View file

@ -48,13 +48,12 @@ RSpec.describe "/admin/config", type: :request do
end
it "updates site config admin action taken" do
Timecop.freeze do
expect(SiteConfig.admin_action_taken_at).not_to eq(5.minutes.ago)
allow(SiteConfig).to receive(:admin_action_taken_at).and_return(5.minutes.ago)
post "/admin/config", params: { site_config: { health_check_token: "token" },
confirmation: confirmation_message }
expect(SiteConfig.admin_action_taken_at).to eq(5.minutes.ago)
end
expect do
post "/admin/config", params: {
site_config: { health_check_token: "token" },
confirmation: confirmation_message
}
end.to change(SiteConfig, :admin_action_taken_at)
end
describe "API tokens" do
@ -75,15 +74,15 @@ RSpec.describe "/admin/config", type: :request do
describe "Authentication" do
it "updates enabled authentication providers" do
enabled = Authentication::Providers.available.last.to_s
post admin_config_path, params: {
site_config: {
post admin_settings_authentications_path, params: {
settings_authentication: {
"#{enabled}_key": "someKey",
"#{enabled}_secret": "someSecret",
auth_providers_to_enable: enabled
},
confirmation: confirmation_message
}
expect(SiteConfig.authentication_providers).to eq([enabled])
expect(Settings::Authentication.providers).to eq([enabled])
end
describe "Campaigns" do
@ -97,81 +96,97 @@ RSpec.describe "/admin/config", type: :request do
it "strips empty elements" do
provider = Authentication::Providers.available.last.to_s
enabled = "#{provider}, '', nil"
post admin_config_path, params: {
site_config: {
post admin_settings_authentications_path, params: {
settings_authentication: {
"#{provider}_key": "someKey",
"#{provider}_secret": "someSecret",
auth_providers_to_enable: enabled
},
confirmation: confirmation_message
}
expect(SiteConfig.authentication_providers).to eq([provider])
expect(Settings::Authentication.providers).to eq([provider])
end
it "does not update enabled authentication providers if any associated key missing" do
enabled = Authentication::Providers.available.first.to_s
post admin_config_path, params: {
site_config: {
post admin_settings_authentications_path, params: {
settings_authentication: {
"#{enabled}_key": "someKey",
"#{enabled}_secret": "",
auth_providers_to_enable: enabled
},
confirmation: confirmation_message
}
expect(SiteConfig.authentication_providers).to eq([])
expect(Settings::Authentication.providers).to eq([])
end
it "enables proper domains to allow list" do
proper_list = "dev.to, forem.com, forem.dev"
post "/admin/config", params: { site_config: { allowed_registration_email_domains: proper_list },
confirmation: confirmation_message }
expect(SiteConfig.allowed_registration_email_domains).to eq(%w[dev.to forem.com forem.dev])
post admin_settings_authentications_path, params: {
settings_authentication: { allowed_registration_email_domains: proper_list },
confirmation: confirmation_message
}
expect(Settings::Authentication.allowed_registration_email_domains).to eq(%w[dev.to forem.com forem.dev])
end
it "allows 2-character domains" do
proper_list = "dev.to, forem.com, 2u.com"
post "/admin/config", params: { site_config: { allowed_registration_email_domains: proper_list },
confirmation: confirmation_message }
expect(SiteConfig.allowed_registration_email_domains).to eq(%w[dev.to forem.com 2u.com])
post admin_settings_authentications_path, params: {
settings_authentication: { allowed_registration_email_domains: proper_list },
confirmation: confirmation_message
}
expect(Settings::Authentication.allowed_registration_email_domains).to eq(%w[dev.to forem.com 2u.com])
end
it "does not allow improper domain list" do
impproper_list = "dev.to, foremcom, forem.dev"
post "/admin/config", params: { site_config: { allowed_registration_email_domains: impproper_list },
confirmation: confirmation_message }
expect(SiteConfig.allowed_registration_email_domains).not_to eq(%w[dev.to foremcom forem.dev])
post admin_settings_authentications_path, params: {
settings_authentication: { allowed_registration_email_domains: impproper_list },
confirmation: confirmation_message
}
expect(Settings::Authentication.allowed_registration_email_domains).not_to eq(%w[dev.to foremcom forem.dev])
end
it "enables display_email_domain_allow_list_publicly" do
post "/admin/config", params: { site_config: { display_email_domain_allow_list_publicly: true },
confirmation: confirmation_message }
expect(SiteConfig.display_email_domain_allow_list_publicly).to be(true)
post admin_settings_authentications_path, params: {
settings_authentication: { display_email_domain_allow_list_publicly: true },
confirmation: confirmation_message
}
expect(Settings::Authentication.display_email_domain_allow_list_publicly).to be(true)
end
it "enables email authentication" do
post "/admin/config", params: { site_config: { allow_email_password_registration: true },
confirmation: confirmation_message }
expect(SiteConfig.allow_email_password_registration).to be(true)
expect(SiteConfig.allow_email_password_login).to be(true)
post admin_settings_authentications_path, params: {
settings_authentication: { allow_email_password_registration: true },
confirmation: confirmation_message
}
expect(Settings::Authentication.allow_email_password_registration).to be(true)
expect(Settings::Authentication.allow_email_password_login).to be(true)
end
it "disables email authentication" do
post "/admin/config", params: { site_config: { allow_email_password_registration: false },
confirmation: confirmation_message }
expect(SiteConfig.allow_email_password_registration).to be(false)
expect(SiteConfig.allow_email_password_login).to be(true)
post admin_settings_authentications_path, params: {
settings_authentication: { allow_email_password_registration: false },
confirmation: confirmation_message
}
expect(Settings::Authentication.allow_email_password_registration).to be(false)
expect(Settings::Authentication.allow_email_password_login).to be(true)
end
it "enables invite-only-mode" do
post "/admin/config", params: { site_config: { invite_only_mode: true },
confirmation: confirmation_message }
expect(SiteConfig.invite_only_mode).to be(true)
post admin_settings_authentications_path, params: {
settings_authentication: { invite_only_mode: true },
confirmation: confirmation_message
}
expect(Settings::Authentication.invite_only_mode).to be(true)
end
it "disables invite-only-mode & enables just email registration" do
post "/admin/config", params: { site_config: { invite_only_mode: false },
confirmation: confirmation_message }
expect(SiteConfig.invite_only_mode).to be(false)
post admin_settings_authentications_path, params: {
settings_authentication: { invite_only_mode: false },
confirmation: confirmation_message
}
expect(Settings::Authentication.invite_only_mode).to be(false)
end
end
@ -449,7 +464,7 @@ RSpec.describe "/admin/config", type: :request do
expect(SiteConfig.mascot_footer_image_width).to eq(expected_default_mascot_footer_image_width)
post "/admin/config", params: { site_config:
{ mascot_footer_image_width: expected_mascot_footer_image_width },
{ mascot_footer_image_width: expected_mascot_footer_image_width },
confirmation: confirmation_message }
expect(SiteConfig.mascot_footer_image_width).to eq(expected_mascot_footer_image_width)
end
@ -461,7 +476,7 @@ RSpec.describe "/admin/config", type: :request do
expect(SiteConfig.mascot_footer_image_height).to eq(expected_default_mascot_footer_image_height)
post "/admin/config", params: { site_config:
{ mascot_footer_image_height: expected_mascot_footer_image_height },
{ mascot_footer_image_height: expected_mascot_footer_image_height },
confirmation: confirmation_message }
expect(SiteConfig.mascot_footer_image_height).to eq(expected_mascot_footer_image_height)
end
@ -738,12 +753,12 @@ RSpec.describe "/admin/config", type: :request do
it "updates recaptcha_site_key and recaptcha_secret_key" do
site_key = "hi-ho"
secret_key = "lets-go"
post "/admin/config", params: {
site_config: { recaptcha_site_key: site_key, recaptcha_secret_key: secret_key },
post admin_settings_authentications_path, params: {
settings_authentication: { recaptcha_site_key: site_key, recaptcha_secret_key: secret_key },
confirmation: confirmation_message
}
expect(SiteConfig.recaptcha_site_key).to eq site_key
expect(SiteConfig.recaptcha_secret_key).to eq secret_key
expect(Settings::Authentication.recaptcha_site_key).to eq site_key
expect(Settings::Authentication.recaptcha_secret_key).to eq secret_key
end
end

View file

@ -151,7 +151,7 @@ RSpec.describe "admin/users", type: :request do
before do
omniauth_mock_providers_payload
allow(SiteConfig).to receive(:authentication_providers).and_return(Authentication::Providers.available)
allow(Settings::Authentication).to receive(:providers).and_return(Authentication::Providers.available)
end
it "removes the given identity" do

View file

@ -64,9 +64,11 @@ RSpec.describe "Api::V0::Admin::Configs", type: :request do
it "enables proper domains to allow list" do
proper_list = "dev.to, forem.com, forem.dev"
put api_admin_config_path, params: { site_config: { allowed_registration_email_domains: proper_list } }.to_json,
put api_admin_config_path, params: {
site_config: { allowed_registration_email_domains: proper_list }
}.to_json,
headers: headers
expect(SiteConfig.allowed_registration_email_domains).to eq(%w[dev.to forem.com forem.dev])
expect(Settings::Authentication.allowed_registration_email_domains).to eq(%w[dev.to forem.com forem.dev])
end
it "does not allow improper domain list" do
@ -74,7 +76,7 @@ RSpec.describe "Api::V0::Admin::Configs", type: :request do
put api_admin_config_path,
params: { site_config: { allowed_registration_email_domains: improper_list } }.to_json,
headers: headers
expect(SiteConfig.allowed_registration_email_domains).not_to eq(%w[dev.to foremcom forem.dev])
expect(Settings::Authentication.allowed_registration_email_domains).not_to eq(%w[dev.to foremcom forem.dev])
end
it "removes space suggested_tags" do

View file

@ -12,7 +12,7 @@ RSpec.describe "Editor", type: :request do
context "when email login is allowed in /admin/config" do
before do
allow(SiteConfig).to receive(:allow_email_password_login).and_return(true)
allow(Settings::Authentication).to receive(:allow_email_password_login).and_return(true)
end
it "asks the non logged in user to sign in, with email signin enabled" do

View file

@ -13,8 +13,8 @@ RSpec.describe "feedback_messages", type: :request do
end
def mock_recaptcha_config_enabled
allow(SiteConfig).to receive(:recaptcha_secret_key).and_return("someSecretKey")
allow(SiteConfig).to receive(:recaptcha_site_key).and_return("someSiteKey")
allow(Settings::Authentication).to receive(:recaptcha_secret_key).and_return("someSecretKey")
allow(Settings::Authentication).to receive(:recaptcha_site_key).and_return("someSiteKey")
end
valid_abuse_report_params = {
@ -81,8 +81,8 @@ RSpec.describe "feedback_messages", type: :request do
context "with valid params and recaptcha not configured" do
before do
allow(SiteConfig).to receive(:recaptcha_secret_key).and_return(nil)
allow(SiteConfig).to receive(:recaptcha_site_key).and_return(nil)
allow(Settings::Authentication).to receive(:recaptcha_secret_key).and_return(nil)
allow(Settings::Authentication).to receive(:recaptcha_site_key).and_return(nil)
end
it "does not show the recaptcha tag" do

View file

@ -41,7 +41,7 @@ RSpec.describe "GithubRepos", type: :request do
before do
omniauth_mock_github_payload
allow(SiteConfig).to receive(:authentication_providers).and_return(Authentication::Providers.available)
allow(Settings::Authentication).to receive(:providers).and_return(Authentication::Providers.available)
allow(Github::OauthClient).to receive(:new).and_return(github_client)
end

View file

@ -18,7 +18,7 @@ RSpec.describe "Registrations", type: :request do
it "only shows the single sign on options if they are present" do
allow(Authentication::Providers).to receive(:enabled).and_return([])
allow(SiteConfig).to receive(:allow_email_password_login).and_return(false)
allow(Settings::Authentication).to receive(:allow_email_password_login).and_return(false)
get sign_up_path
@ -28,7 +28,7 @@ RSpec.describe "Registrations", type: :request do
context "when email login is enabled in /admin/config" do
before do
allow(SiteConfig).to receive(:allow_email_password_login).and_return(true)
allow(Settings::Authentication).to receive(:allow_email_password_login).and_return(true)
end
it "shows the sign in text for password based authentication" do
@ -40,7 +40,7 @@ RSpec.describe "Registrations", type: :request do
context "when email login is disabled in /admin/config" do
before do
allow(SiteConfig).to receive(:allow_email_password_login).and_return(false)
allow(Settings::Authentication).to receive(:allow_email_password_login).and_return(false)
end
it "does not show the sign in text for password based authentication" do
@ -62,7 +62,7 @@ RSpec.describe "Registrations", type: :request do
describe "Create Account" do
context "when email registration allowed" do
before { allow(SiteConfig).to receive(:allow_email_password_registration).and_return(true) }
before { allow(Settings::Authentication).to receive(:allow_email_password_registration).and_return(true) }
it "shows the sign in page with email option" do
get sign_up_path, params: { state: "new-user" }
@ -78,10 +78,10 @@ RSpec.describe "Registrations", type: :request do
end
context "when email registration not allowed" do
before { allow(SiteConfig).to receive(:allow_email_password_registration).and_return(false) }
before { allow(Settings::Authentication).to receive(:allow_email_password_registration).and_return(false) }
it "does not show email sign up option" do
allow(SiteConfig).to receive(:allow_email_password_registration).and_return(false)
allow(Settings::Authentication).to receive(:allow_email_password_registration).and_return(false)
get sign_up_path, params: { state: "new-user" }
expect(response.body).not_to include("Sign up with Email")
@ -90,10 +90,10 @@ RSpec.describe "Registrations", type: :request do
context "when email registration allowed and captcha required" do
before do
allow(SiteConfig).to receive(:recaptcha_secret_key).and_return("someSecretKey")
allow(SiteConfig).to receive(:recaptcha_site_key).and_return("someSiteKey")
allow(SiteConfig).to receive(:allow_email_password_registration).and_return(true)
allow(SiteConfig).to receive(:require_captcha_for_email_password_registration).and_return(true)
allow(Settings::Authentication).to receive(:recaptcha_secret_key).and_return("someSecretKey")
allow(Settings::Authentication).to receive(:recaptcha_site_key).and_return("someSiteKey")
allow(Settings::Authentication).to receive(:allow_email_password_registration).and_return(true)
allow(Settings::Authentication).to receive(:require_captcha_for_email_password_registration).and_return(true)
end
it "displays the captcha box on email signup page" do
@ -162,7 +162,8 @@ RSpec.describe "Registrations", type: :request do
context "when site is not configured to accept email registration" do
before do
allow(SiteConfig).to receive(:allow_email_password_registration).and_return(false)
allow(Settings::Authentication)
.to receive(:allow_email_password_registration).and_return(false)
end
it "disallows communities where email registration is not allowed" do
@ -172,7 +173,8 @@ RSpec.describe "Registrations", type: :request do
context "when site is configured to accept email registration" do
before do
allow(SiteConfig).to receive(:allow_email_password_registration).and_return(true)
allow(Settings::Authentication)
.to receive(:allow_email_password_registration).and_return(true)
end
it "does not raise disallowed if community is set to allow email" do
@ -228,8 +230,8 @@ RSpec.describe "Registrations", type: :request do
context "when email registration allowed and email allow list empty" do
before do
allow(SiteConfig).to receive(:allow_email_password_registration).and_return(true)
allow(SiteConfig).to receive(:allowed_registration_email_domains).and_return([])
allow(Settings::Authentication).to receive(:allow_email_password_registration).and_return(true)
allow(Settings::Authentication).to receive(:allowed_registration_email_domains).and_return([])
end
it "creates user when email in allow list" do
@ -245,8 +247,9 @@ RSpec.describe "Registrations", type: :request do
context "when email registration allowed and email allow list present" do
before do
allow(SiteConfig).to receive(:allow_email_password_registration).and_return(true)
allow(SiteConfig).to receive(:allowed_registration_email_domains).and_return(["dev.to", "forem.com"])
allow(Settings::Authentication).to receive(:allow_email_password_registration).and_return(true)
allow(Settings::Authentication).to receive(:allowed_registration_email_domains).and_return(["dev.to",
"forem.com"])
end
it "does not create user when email not in allow list" do
@ -272,10 +275,10 @@ RSpec.describe "Registrations", type: :request do
context "when site configured to accept email registration AND require captcha" do
before do
allow(SiteConfig).to receive(:recaptcha_secret_key).and_return("someSecretKey")
allow(SiteConfig).to receive(:recaptcha_site_key).and_return("someSiteKey")
allow(SiteConfig).to receive(:allow_email_password_registration).and_return(true)
allow(SiteConfig).to receive(:require_captcha_for_email_password_registration).and_return(true)
allow(Settings::Authentication).to receive(:recaptcha_secret_key).and_return("someSecretKey")
allow(Settings::Authentication).to receive(:recaptcha_site_key).and_return("someSiteKey")
allow(Settings::Authentication).to receive(:allow_email_password_registration).and_return(true)
allow(Settings::Authentication).to receive(:require_captcha_for_email_password_registration).and_return(true)
end
it "creates user when valid params passed and recaptcha completed" do

View file

@ -348,7 +348,7 @@ RSpec.describe "UserSettings", type: :request do
before do
omniauth_mock_providers_payload
allow(SiteConfig).to receive(:authentication_providers).and_return(Authentication::Providers.available)
allow(Settings::Authentication).to receive(:providers).and_return(Authentication::Providers.available)
sign_in user
end

View file

@ -3,7 +3,7 @@ require "rails_helper"
RSpec.describe Authentication::Authenticator, type: :service do
before do
omniauth_mock_providers_payload
allow(SiteConfig).to receive(:authentication_providers).and_return(Authentication::Providers.available)
allow(Settings::Authentication).to receive(:providers).and_return(Authentication::Providers.available)
end
context "when authenticating through an unknown provider" do

View file

@ -9,7 +9,7 @@ RSpec.describe Authentication::Providers, type: :service do
end
it "raises an exception if a provider is available but not enabled" do
allow(SiteConfig).to receive(:authentication_providers).and_return(%w[github])
allow(Settings::Authentication).to receive(:providers).and_return(%w[github])
expect do
described_class.get!(:twitter)
@ -17,7 +17,7 @@ RSpec.describe Authentication::Providers, type: :service do
end
it "loads the correct provider class" do
allow(SiteConfig).to receive(:authentication_providers).and_return(described_class.available)
allow(Settings::Authentication).to receive(:providers).and_return(described_class.available)
is_subclass_of = (
described_class.get!(:twitter) < Authentication::Providers::Provider
@ -36,7 +36,7 @@ RSpec.describe Authentication::Providers, type: :service do
describe ".enabled" do
context "when one of the available providers is disabled" do
it "only lists those that remain enabled" do
allow(SiteConfig).to receive(:authentication_providers).and_return(%w[github])
allow(Settings::Authentication).to receive(:providers).and_return(%w[github])
expect(described_class.enabled).to eq(%i[github])
end
@ -45,13 +45,13 @@ RSpec.describe Authentication::Providers, type: :service do
describe ".enabled?" do
it "returns true if a provider is enabled" do
allow(SiteConfig).to receive(:authentication_providers).and_return(%w[github])
allow(Settings::Authentication).to receive(:providers).and_return(%w[github])
expect(described_class.enabled?(:github)).to be(true)
end
it "returns false if a provider is not enabled" do
allow(SiteConfig).to receive(:authentication_providers).and_return(%w[twitter])
allow(Settings::Authentication).to receive(:providers).and_return(%w[twitter])
expect(described_class.enabled?(:github)).to be(false)
end

View file

@ -20,7 +20,7 @@ RSpec.describe Broadcasts::WelcomeNotification::Generator, type: :service do
allow(Notification).to receive(:send_welcome_notification).and_call_original
allow(User).to receive(:mascot_account).and_return(mascot_account)
allow(SiteConfig).to receive(:staff_user_id).and_return(mascot_account.id)
allow(SiteConfig).to receive(:authentication_providers).and_return(Authentication::Providers.available)
allow(Settings::Authentication).to receive(:providers).and_return(Authentication::Providers.available)
end
it "requires a valid user id" do

View file

@ -14,8 +14,8 @@ RSpec.describe ReCaptcha::CheckEnabled, type: :request do
describe "ReCaptcha for user actions like Abuse Reports (FeedbackMessages)" do
context "when recaptcha SiteConfig keys are not configured" do
it "marks ReCaptcha as not enabled regardless of the param passed in" do
allow(SiteConfig).to receive(:recaptcha_site_key).and_return(nil)
allow(SiteConfig).to receive(:recaptcha_secret_key).and_return(nil)
allow(Settings::Authentication).to receive(:recaptcha_site_key).and_return(nil)
allow(Settings::Authentication).to receive(:recaptcha_secret_key).and_return(nil)
expect(described_class.call).to be(false)
expect(described_class.call(older_user)).to be(false)
@ -24,8 +24,8 @@ RSpec.describe ReCaptcha::CheckEnabled, type: :request do
context "when recaptcha SiteConfig keys are configured" do
before do
allow(SiteConfig).to receive(:recaptcha_site_key).and_return("someSecretKey")
allow(SiteConfig).to receive(:recaptcha_secret_key).and_return("someSiteKey")
allow(Settings::Authentication).to receive(:recaptcha_site_key).and_return("someSecretKey")
allow(Settings::Authentication).to receive(:recaptcha_secret_key).and_return("someSiteKey")
end
it "marks ReCaptcha as enabled when logged out (parameter is nil)" do

View file

@ -4,23 +4,25 @@ RSpec.describe ReCaptcha::CheckRegistrationEnabled, type: :request do
describe "ReCaptcha for registration" do
context "when recaptcha is enabled" do
before do
allow(SiteConfig).to receive(:require_captcha_for_email_password_registration).and_return(true)
allow(Settings::Authentication)
.to receive(:require_captcha_for_email_password_registration).and_return(true)
end
it "is enabled if both site & secret keys present" do
allow(SiteConfig).to receive(:recaptcha_secret_key).and_return("someSecretKey")
allow(SiteConfig).to receive(:recaptcha_site_key).and_return("someSiteKey")
allow(Settings::Authentication).to receive(:recaptcha_secret_key).and_return("someSecretKey")
allow(Settings::Authentication).to receive(:recaptcha_site_key).and_return("someSiteKey")
expect(described_class.call).to be(true)
end
it "is disabled if site or secret key missing" do
allow(SiteConfig).to receive(:recaptcha_site_key).and_return("")
allow(Settings::Authentication).to receive(:recaptcha_site_key).and_return("")
expect(described_class.call).to be(false)
end
end
it "is disabled if recaptcha disabled for email signup" do
allow(SiteConfig).to receive(:require_captcha_for_email_password_registration).and_return(false)
allow(Settings::Authentication)
.to receive(:require_captcha_for_email_password_registration).and_return(false)
expect(described_class.call).to be(false)
end
end

View file

@ -13,7 +13,7 @@ RSpec.describe TwitterClient::Client, type: :service, vcr: true do
it "works properly when SiteConfig is set" do
VCR.use_cassette("twitter_client_status") do
allow(SiteConfig).to receive(:twitter_key).and_return("test")
allow(Settings::Authentication).to receive(:twitter_key).and_return("test")
tweet = described_class.status(tweet_id)
expect(tweet.text).to be_present
end

View file

@ -6,7 +6,7 @@ RSpec.describe Users::Delete, type: :service do
before do
omniauth_mock_github_payload
allow(SiteConfig).to receive(:authentication_providers).and_return(Authentication::Providers.available)
allow(Settings::Authentication).to receive(:providers).and_return(Authentication::Providers.available)
allow(EdgeCache::Bust).to receive(:new).and_return(cache_bust)
allow(cache_bust).to receive(:call)
end

View file

@ -5,7 +5,7 @@ RSpec.describe "Beta provider access", type: :system do
let(:beta_providers) { ["Apple"] }
before do
allow(SiteConfig).to receive(:authentication_providers).and_return(Authentication::Providers.available)
allow(Settings::Authentication).to receive(:providers).and_return(Authentication::Providers.available)
end
context "when a user tries to sign_up" do

View file

@ -9,7 +9,7 @@ RSpec.describe "Creator config edit", type: :system, js: true do
context "when a creator browses /admin/config" do
before do
sign_in admin
allow(SiteConfig).to receive(:invite_only_mode).and_return(false)
allow(Settings::Authentication).to receive(:invite_only_mode).and_return(false)
end
it "presents all available OAuth providers" do

View file

@ -20,7 +20,7 @@ RSpec.describe "Omniauth redirect_uri", type: :system do
end
it "relies on SiteConfig.app_domain to generate correct callbacks_url" do
allow(SiteConfig).to receive(:authentication_providers).and_return(Authentication::Providers.available)
allow(Settings::Authentication).to receive(:providers).and_return(Authentication::Providers.available)
visit sign_up_path
Authentication::Providers.available.each do |provider_name|
provider_auth_button = find("button.crayons-btn--brand-#{provider_name}")

View file

@ -11,7 +11,7 @@ RSpec.describe "Redirects authentication using Referer", type: :system do
before do
omniauth_mock_twitter_payload
allow(SiteConfig).to receive(:authentication_providers).and_return(Authentication::Providers.available)
allow(Settings::Authentication).to receive(:providers).and_return(Authentication::Providers.available)
OmniAuth.config.mock_auth[:twitter].info.email = user.email
end

View file

@ -6,7 +6,7 @@ RSpec.describe "Authenticating with Apple", vcr: { cassette_name: "fastly_sloan"
before do
omniauth_mock_apple_payload
Flipper.enable(:apple_auth)
allow(SiteConfig).to receive(:authentication_providers).and_return(Authentication::Providers.available)
allow(Settings::Authentication).to receive(:providers).and_return(Authentication::Providers.available)
end
context "when a user is new" do

View file

@ -5,8 +5,8 @@ RSpec.describe "Authenticating with Email" do
let(:sign_up_link) { "Sign up with Email" }
before do
allow(SiteConfig).to receive(:allow_email_password_registration).and_return(true)
allow(SiteConfig).to receive(:allow_email_password_login).and_return(true)
allow(Settings::Authentication).to receive(:allow_email_password_registration).and_return(true)
allow(Settings::Authentication).to receive(:allow_email_password_login).and_return(true)
end
context "when a user is new" do
@ -112,7 +112,7 @@ RSpec.describe "Authenticating with Email" do
context "when community is in invite only mode" do
before do
allow(SiteConfig).to receive(:invite_only_mode).and_return(true)
allow(Settings::Authentication).to receive(:invite_only_mode).and_return(true)
end
it "doesn't present the authentication option" do

View file

@ -5,7 +5,7 @@ RSpec.describe "Authenticating with Facebook" do
before do
omniauth_mock_facebook_payload
allow(SiteConfig).to receive(:authentication_providers).and_return(Authentication::Providers.available)
allow(Settings::Authentication).to receive(:providers).and_return(Authentication::Providers.available)
end
context "when a user is new" do
@ -225,7 +225,7 @@ RSpec.describe "Authenticating with Facebook" do
context "when community is in invite only mode" do
before do
allow(SiteConfig).to receive(:invite_only_mode).and_return(true)
allow(Settings::Authentication).to receive(:invite_only_mode).and_return(true)
end
it "doesn't present the authentication option" do

View file

@ -5,7 +5,7 @@ RSpec.describe "Authenticating with GitHub" do
before do
omniauth_mock_github_payload
allow(SiteConfig).to receive(:authentication_providers).and_return(Authentication::Providers.available)
allow(Settings::Authentication).to receive(:providers).and_return(Authentication::Providers.available)
end
context "when a user is new" do
@ -191,7 +191,7 @@ RSpec.describe "Authenticating with GitHub" do
context "when community is in invite only mode" do
before do
allow(SiteConfig).to receive(:invite_only_mode).and_return(true)
allow(Settings::Authentication).to receive(:invite_only_mode).and_return(true)
end
it "doesn't present the authentication option" do

View file

@ -5,7 +5,7 @@ RSpec.describe "Authenticating with Twitter" do
before do
omniauth_mock_twitter_payload
allow(SiteConfig).to receive(:authentication_providers).and_return(Authentication::Providers.available)
allow(Settings::Authentication).to receive(:providers).and_return(Authentication::Providers.available)
end
context "when a user is new" do
@ -183,7 +183,7 @@ RSpec.describe "Authenticating with Twitter" do
context "when community is in invite only mode" do
before do
allow(SiteConfig).to receive(:invite_only_mode).and_return(true)
allow(Settings::Authentication).to receive(:invite_only_mode).and_return(true)
end
it "doesn't present the authentication option" do

View file

@ -4,8 +4,8 @@ RSpec.describe "User with suspended username tried to sign up via OAuth" do
before do
omniauth_mock_twitter_payload
allow(SiteConfig)
.to receive(:authentication_providers)
allow(Settings::Authentication)
.to receive(:providers)
.and_return(Authentication::Providers.available)
allow(ForemStatsClient).to receive(:increment)

View file

@ -11,7 +11,7 @@ RSpec.describe "Authenticating with a password" do
let!(:user) { create(:user, password: password, password_confirmation: password) }
before do
allow(SiteConfig).to receive(:authentication_providers).and_return(Authentication::Providers.available)
allow(Settings::Authentication).to receive(:providers).and_return(Authentication::Providers.available)
visit sign_up_path
end

View file

@ -8,7 +8,7 @@ Gem::Specification.new do |s|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
s.require_paths = ["lib".freeze]
s.authors = ["Tom Cocca".freeze]
s.date = "2021-03-31"
s.date = "2021-04-09"
s.description = "acts_as_follower is a Rubygem to allow any model to follow any other model. This is accomplished through a double polymorphic relationship on the Follow model. There is also built in support for blocking/un-blocking follow records. Main uses would be for Users to follow other Users or for Users to follow Books, etc\u2026 (Basically, to develop the type of follow system that GitHub has)".freeze
s.email = ["tom dot cocca at gmail dot com".freeze]
s.files = [".gitignore".freeze, ".travis.yml".freeze, "Gemfile".freeze, "MIT-LICENSE".freeze, "README.rdoc".freeze, "Rakefile".freeze, "acts_as_follower.gemspec".freeze, "init.rb".freeze, "lib/acts_as_follower.rb".freeze, "lib/acts_as_follower/follow_scopes.rb".freeze, "lib/acts_as_follower/followable.rb".freeze, "lib/acts_as_follower/follower.rb".freeze, "lib/acts_as_follower/follower_lib.rb".freeze, "lib/acts_as_follower/railtie.rb".freeze, "lib/acts_as_follower/version.rb".freeze, "lib/generators/USAGE".freeze, "lib/generators/acts_as_follower_generator.rb".freeze, "lib/generators/templates/migration.rb".freeze, "lib/generators/templates/model.rb".freeze, "test/README".freeze, "test/acts_as_followable_test.rb".freeze, "test/acts_as_follower_test.rb".freeze, "test/dummy30/Gemfile".freeze, "test/dummy30/Rakefile".freeze, "test/dummy30/app/models/application_record.rb".freeze, "test/dummy30/app/models/band.rb".freeze, "test/dummy30/app/models/band/punk.rb".freeze, "test/dummy30/app/models/band/punk/pop_punk.rb".freeze, "test/dummy30/app/models/custom_record.rb".freeze, "test/dummy30/app/models/some.rb".freeze, "test/dummy30/app/models/user.rb".freeze, "test/dummy30/config.ru".freeze, "test/dummy30/config/application.rb".freeze, "test/dummy30/config/boot.rb".freeze, "test/dummy30/config/database.yml".freeze, "test/dummy30/config/environment.rb".freeze, "test/dummy30/config/environments/development.rb".freeze, "test/dummy30/config/environments/test.rb".freeze, "test/dummy30/config/initializers/backtrace_silencers.rb".freeze, "test/dummy30/config/initializers/inflections.rb".freeze, "test/dummy30/config/initializers/secret_token.rb".freeze, "test/dummy30/config/initializers/session_store.rb".freeze, "test/dummy30/config/locales/en.yml".freeze, "test/dummy30/config/routes.rb".freeze, "test/factories/bands.rb".freeze, "test/factories/somes.rb".freeze, "test/factories/users.rb".freeze, "test/follow_test.rb".freeze, "test/schema.rb".freeze, "test/test_helper.rb".freeze]
@ -16,7 +16,6 @@ Gem::Specification.new do |s|
s.licenses = ["MIT".freeze]
s.rubygems_version = "3.1.4".freeze
s.summary = "A Rubygem to add Follow functionality for ActiveRecord models".freeze
s.test_files = ["test/README".freeze, "test/acts_as_followable_test.rb".freeze, "test/acts_as_follower_test.rb".freeze, "test/dummy30/Gemfile".freeze, "test/dummy30/Rakefile".freeze, "test/dummy30/app/models/application_record.rb".freeze, "test/dummy30/app/models/band.rb".freeze, "test/dummy30/app/models/band/punk.rb".freeze, "test/dummy30/app/models/band/punk/pop_punk.rb".freeze, "test/dummy30/app/models/custom_record.rb".freeze, "test/dummy30/app/models/some.rb".freeze, "test/dummy30/app/models/user.rb".freeze, "test/dummy30/config.ru".freeze, "test/dummy30/config/application.rb".freeze, "test/dummy30/config/boot.rb".freeze, "test/dummy30/config/database.yml".freeze, "test/dummy30/config/environment.rb".freeze, "test/dummy30/config/environments/development.rb".freeze, "test/dummy30/config/environments/test.rb".freeze, "test/dummy30/config/initializers/backtrace_silencers.rb".freeze, "test/dummy30/config/initializers/inflections.rb".freeze, "test/dummy30/config/initializers/secret_token.rb".freeze, "test/dummy30/config/initializers/session_store.rb".freeze, "test/dummy30/config/locales/en.yml".freeze, "test/dummy30/config/routes.rb".freeze, "test/factories/bands.rb".freeze, "test/factories/somes.rb".freeze, "test/factories/users.rb".freeze, "test/follow_test.rb".freeze, "test/schema.rb".freeze, "test/test_helper.rb".freeze]
s.installed_by_version = "3.1.4" if s.respond_to? :installed_by_version

Binary file not shown.

BIN
vendor/cache/ahoy_email-2.0.3.gem vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
vendor/cache/chartkick-4.0.2.gem vendored Normal file

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

BIN
vendor/cache/loofah-2.9.1.gem vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
vendor/cache/marcel-1.0.1.gem vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
vendor/cache/oauth-0.5.6.gem vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
vendor/cache/parser-3.0.1.0.gem vendored Normal file

Binary file not shown.