Move Okta and Azure AD configurations to application settings [SCI-7609] (#4732)

This commit is contained in:
Alex Kriuchykhin 2023-01-31 11:55:31 +01:00 committed by GitHub
parent 4439d044ab
commit e1c4aa9469
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 115 additions and 112 deletions

View file

@ -18,10 +18,11 @@ module Users
def customazureactivedirectory
auth = request.env['omniauth.auth']
provider_id = auth.dig(:extra, :raw_info, :aud)
provider_conf = Rails.configuration.x.azure_ad_apps[provider_id]
settings = ApplicationSettings.instance
provider_conf = settings.values['azure_ad_apps'].find { |v| v['enable_sign_in'] && v['app_id'] == provider_id }
raise StandardError, 'No matching Azure AD provider config found' if provider_conf.blank?
auth.provider = provider_conf[:provider]
auth.provider = provider_conf['provider_name']
return redirect_to connected_accounts_path if current_user

View file

@ -219,6 +219,14 @@ module ApplicationHelper
'icon_small/missing.png'
end
def okta_configured?
ApplicationSettings.instance.values['okta'].present?
end
def azure_ad_configured?
ApplicationSettings.instance.values['azure_ad_apps'].present?
end
def wopi_enabled?
ENV['WOPI_ENABLED'] == 'true'
end

View file

@ -1,7 +1,7 @@
<% Rails.configuration.x.azure_ad_apps.select { |uid, config| config[:enable_sign_in] }.each do |uid, config| %>
<% ApplicationSettings.instance.values['azure_ad_apps'].select { |v| v['enable_sign_in'] }.each do |config| %>
<div class="form-group">
<%= form_tag user_customazureactivedirectory_omniauth_authorize_path(provider: config[:provider]), method: :post do %>
<%= submit_tag config[:sign_in_label], class: 'btn btn-azure-ad' %>
<%= form_tag user_customazureactivedirectory_omniauth_authorize_path(provider: config['provider']), method: :post do %>
<%= submit_tag config['sign_in_label'] || t('devise.sessions.new.azure_ad_submit'), class: 'btn btn-azure-ad' %>
<% end %>
</div>
<% end %>

View file

@ -27,7 +27,7 @@
<div data-hook="omniauth-sign-in-links"></div>
<% end -%>
<%- if devise_mapping.omniauthable? && Devise.omniauth_configs[:okta].present? %>
<%- if devise_mapping.omniauthable? && okta_configured? %>
<div class="okta-sign-in-actions">
<%= form_tag omniauth_authorize_path(resource_name, :okta), method: :post do %>
<%= submit_tag t('devise.okta.sign_in_label'), class: 'btn btn-okta' %>
@ -41,7 +41,9 @@
<% end -%>
<% end -%>
<div class="azure-sign-in-actions">
<%= render partial: "users/shared/azure_sign_in_links", locals: { resource_name: resource_name } %>
</div>
<% if devise_mapping.omniauthable? && azure_ad_configured? %>
<div class="azure-sign-in-actions">
<%= render partial: "users/shared/azure_sign_in_links", locals: { resource_name: resource_name } %>
</div>
<% end %>
</div>

View file

@ -1,88 +1,58 @@
# frozen_string_literal: true
Rails.application.configure do
vars = ENV.select { |name, _| name =~ /^[[:alnum:]]*_AZURE_AD_APP_ID/ }
config.x.azure_ad_apps = HashWithIndifferentAccess.new if vars.present?
begin
azure_app_ids = ENV.select { |name, _| name =~ /^[[:alnum:]]*_AZURE_AD_APP_ID/ }
settings = ApplicationSettings.instance
vars.each do |name, value|
azure_app_ids.each do |name, value|
app_name = name.sub('_AZURE_AD_APP_ID', '')
config.x.azure_ad_apps[value] = {}
app_config = {}
app_config['app_id'] = value
tenant_id = ENV["#{app_name}_AZURE_AD_TENANT_ID"]
tenant_id = ENV.fetch("#{app_name}_AZURE_AD_TENANT_ID")
raise StandardError, "No Tenant ID for #{app_name} Azure app" unless tenant_id
config.x.azure_ad_apps[value][:tenant_id] = tenant_id
app_config['tenant_id'] = tenant_id
client_secret = ENV["#{app_name}_AZURE_AD_CLIENT_SECRET"]
client_secret = ENV.fetch("#{app_name}_AZURE_AD_CLIENT_SECRET")
raise StandardError, "No Client Secret for #{app_name} Azure app" unless client_secret
config.x.azure_ad_apps[value][:client_secret] = client_secret
app_config['client_secret'] = client_secret
iss = ENV["#{app_name}_AZURE_AD_ISS"]
iss = ENV.fetch("#{app_name}_AZURE_AD_ISS")
raise StandardError, "No ISS for #{app_name} Azure app" unless iss
config.x.azure_ad_apps[value][:iss] = iss
app_config['iss'] = iss
conf_url = ENV["#{app_name}_AZURE_AD_CONF_URL"]
conf_url = ENV.fetch("#{app_name}_AZURE_AD_CONF_URL")
raise StandardError, "No CONF_URL for #{app_name} Azure app" unless conf_url
config.x.azure_ad_apps[value][:conf_url] = conf_url
app_config['conf_url'] = conf_url
provider = ENV["#{app_name}_AZURE_AD_PROVIDER_NAME"]
provider = ENV.fetch("#{app_name}_AZURE_AD_PROVIDER_NAME")
raise StandardError, "No PROVIDER_NAME for #{app_name} Azure app" unless provider
config.x.azure_ad_apps[value][:provider] = provider
app_config['provider_name'] = provider
config.x.azure_ad_apps[value][:enable_sign_in] = ENV["#{app_name}_AZURE_AD_ENABLE_SIGN_IN"] == 'true'
app_config['enable_sign_in'] = ENV["#{app_name}_AZURE_AD_ENABLE_SIGN_IN"] == 'true'
next unless config.x.azure_ad_apps[value][:enable_sign_in]
next unless app_config['enable_sign_in']
config.x.azure_ad_apps[value][:sign_in_label] = ENV["#{app_name}_AZURE_AD_SIGN_IN_LABEL"] || 'Sign in with Azure AD'
config.x.azure_ad_apps[value][:auto_link_on_sign_in] = ENV["#{app_name}_AZURE_AD_AUTO_LINK_ON_SIGN_IN"] == 'true'
app_config['sign_in_label'] = ENV.fetch("#{app_name}_AZURE_AD_SIGN_IN_LABEL")
app_config['auto_link_on_sign_in'] = ENV["#{app_name}_AZURE_AD_AUTO_LINK_ON_SIGN_IN"] == 'true'
if ENV["#{app_name}_AZURE_AD_SIGN_IN_POLICY"]
config.x.azure_ad_apps[value][:sign_in_policy] = ENV["#{app_name}_AZURE_AD_SIGN_IN_POLICY"]
app_config['sign_in_policy'] = ENV["#{app_name}_AZURE_AD_SIGN_IN_POLICY"]
end
existing_index = settings.values['azure_ad_apps'].find_index { |v| v['app_id'] == value }
if existing_index
settings.values['azure_ad_apps'][existing_index] = app_config
else
settings.values['azure_ad_apps'] << app_config
end
end
# Checking additional configurations in ApplicationSettings JSON. Key and values should be strings there.
begin
if ApplicationSettings.instance.values['azure_ad_apps']&.is_a?(Array)
config.x.azure_ad_apps ||= HashWithIndifferentAccess.new
settings = ApplicationSettings.instance
settings.values['azure_ad_apps'].each do |azure_ad_app|
app_config = {}
app_id = azure_ad_app['app_id']
Rails.logger.error('No app_id present for the entry in Azure app settings') && next unless app_id
app_config[:tenant_id] = azure_ad_app['tenant_id']
Rails.logger.error("No tenant id for #{app_id} Azure app") && next unless app_config[:tenant_id]
app_config[:client_secret] = azure_ad_app['client_secret']
Rails.logger.error("No client secret for #{app_id} Azure app") && next unless app_config[:client_secret]
app_config[:iss] = azure_ad_app['iss']
Rails.logger.error("No iss for #{app_id} Azure app") && next unless app_config[:iss]
app_config[:conf_url] = azure_ad_app['conf_url']
Rails.logger.error("No conf_url for #{app_id} Azure app") && next unless app_config[:conf_url]
app_config[:provider] = azure_ad_app['provider_name']
Rails.logger.error("No provider_name for #{app_id} Azure app") && next unless app_config[:provider]
app_config[:enable_sign_in] = azure_ad_app['enable_sign_in'] == 'true'
if app_config[:enable_sign_in]
app_config[:sign_in_label] = azure_ad_app['sign_in_label'] || 'Sign in with Azure AD'
app_config[:auto_link_on_sign_in] = azure_ad_app['auto_link_on_sign_in'] == 'true'
app_config[:sign_in_policy] = azure_ad_app['sign_in_policy'] if azure_ad_app['sign_in_policy']
end
config.x.azure_ad_apps[app_id] = app_config
end
end
rescue ActiveRecord::ActiveRecordError, PG::ConnectionBad
Rails.logger.info('Not connected to database, skipping additional Azure AD configuration')
end
settings.save! if azure_app_ids.present?
rescue ActiveRecord::ActiveRecordError, PG::ConnectionBad
Rails.logger.info('Not connected to database, skipping additional Azure AD configuration')
end

View file

@ -301,36 +301,6 @@ Devise.setup do |config|
Rails.application.config.x.disable_local_passwords = ENV['DISABLE_LOCAL_PASSWORDS'] == 'true'
if [ENV['OKTA_CLIENT_ID'], ENV['OKTA_CLIENT_SECRET'], ENV['OKTA_DOMAIN']].all?(&:present?)
OKTA_OAUTH2_BASE_URL =
if ENV['OKTA_AUTH_SERVER_ID'].blank?
"https://#{ENV['OKTA_DOMAIN']}/oauth2"
else
"https://#{ENV['OKTA_DOMAIN']}/oauth2/#{ENV['OKTA_AUTH_SERVER_ID']}"
end
client_options = {
site: "https://#{ENV['OKTA_DOMAIN']}",
authorize_url: "#{OKTA_OAUTH2_BASE_URL}/v1/authorize",
token_url: "#{OKTA_OAUTH2_BASE_URL}/v1/token",
user_info_url: "#{OKTA_OAUTH2_BASE_URL}/v1/userinfo"
}
client_options[:audience] = ENV['OKTA_CLIENT_ID'] if ENV['OKTA_AUDIENCE'].present?
if ENV['OKTA_AUTH_SERVER_ID'].present?
client_options[:authorization_server] = ENV['OKTA_AUTH_SERVER_ID']
else
client_options[:use_org_auth_server] = true
end
config.omniauth(
:okta,
ENV['OKTA_CLIENT_ID'],
ENV['OKTA_CLIENT_SECRET'],
scope: 'openid profile email',
fields: %w(profile email),
client_options: client_options,
strategy_class: OmniAuth::Strategies::Okta
)
end
# ==> Warden configuration
# If you want to use other strategies, that are not supported by Devise, or
# change the failure app, you can configure them inside the config.warden block.

View file

@ -0,0 +1,18 @@
# frozen_string_literal: true
begin
settings = ApplicationSettings.instance
provider_conf = settings.values['okta']
if provider_conf.blank? && %w(OKTA_CLIENT_ID OKTA_CLIENT_SECRET OKTA_DOMAIN).all? { |v| ENV.fetch(v, nil).present? }
provider_conf = {}
provider_conf['client_id'] = ENV.fetch('OKTA_CLIENT_ID')
provider_conf['client_secret'] = ENV.fetch('OKTA_CLIENT_SECRET')
provider_conf['domain'] = ENV.fetch('OKTA_DOMAIN')
provider_conf['auth_server_id'] = ENV['OKTA_AUTH_SERVER_ID'] if ENV['OKTA_AUTH_SERVER_ID'].present?
provider_conf['audience'] = ENV['OKTA_AUDIENCE'] if ENV['OKTA_AUDIENCE'].present?
settings.values['okta'] = provider_conf
settings.save!
end
rescue ActiveRecord::ActiveRecordError, PG::ConnectionBad
Rails.logger.info('Not connected to database, skipping additional Okta configuration')
end

View file

@ -3,7 +3,8 @@
require 'omniauth/strategies/custom_azure_active_directory'
AZURE_SETUP_PROC = lambda do |env|
providers = Rails.configuration.x.azure_ad_apps.select { |_, v| v[:enable_sign_in] == true }
settings = ApplicationSettings.instance
providers = settings.values['azure_ad_apps'].select { |v| v['enable_sign_in'] }
raise StandardError, 'No Azure AD config available for sign in' if providers.blank?
req = Rack::Request.new(env)
@ -11,28 +12,60 @@ AZURE_SETUP_PROC = lambda do |env|
if providers.size > 1
if req.params['id_token'].present? # Callback phase
unverified_jwt_payload, = JWT.decode(req.params['id_token'], nil, false)
raise StandardError, 'No Azure AD config available for sign in' if providers[unverified_jwt_payload['aud']].blank?
provider_id = unverified_jwt_payload['aud']
provider_conf = providers.select { |v| v['app_id'] == unverified_jwt_payload['aud'] }
else # Authorization phase
raise ActionController::ParameterMissing, 'Provider name is missing' if req.params['provider'].blank?
provider_id = providers.select { |_, v| v[:provider] == req.params['provider'] }.keys.first
raise StandardError, 'No Azure AD config available for sign in' if provider_id.blank?
provider_conf = providers.find { |v| v['provider_name'] == req.params['provider'] }
end
raise StandardError, 'No Azure AD config available for sign in' if provider_conf.blank?
end
provider_id ||= providers.keys.first
provider_conf = providers[provider_id]
env['omniauth.strategy'].options[:client_id] = provider_id.to_s
env['omniauth.strategy'].options[:client_secret] = provider_conf[:client_secret]
env['omniauth.strategy'].options[:tenant_id] = provider_conf[:tenant_id]
env['omniauth.strategy'].options[:sign_in_policy] = provider_conf[:sign_in_policy]
provider_conf ||= providers.first
env['omniauth.strategy'].options[:client_id] = provider_conf['app_id']
env['omniauth.strategy'].options[:client_secret] = provider_conf['client_secret']
env['omniauth.strategy'].options[:tenant_id] = provider_conf['tenant_id']
env['omniauth.strategy'].options[:sign_in_policy] = provider_conf['sign_in_policy']
env['omniauth.strategy'].options[:name] = 'customazureactivedirectory'
end
OKTA_SETUP_PROC = lambda do |env|
settings = ApplicationSettings.instance
provider_conf = settings.values['okta']
raise StandardError, 'No Okta config available for sign in' if provider_conf.blank?
oauth2_base_url =
if provider_conf['auth_server_id'].blank?
"https://#{provider_conf['domain']}/oauth2"
else
"https://#{provider_conf['domain']}/oauth2/#{provider_conf['auth_server_id']}"
end
client_options = {
site: "https://#{provider_conf['domain']}",
authorize_url: "#{oauth2_base_url}/v1/authorize",
token_url: "#{oauth2_base_url}/v1/token",
user_info_url: "#{oauth2_base_url}/v1/userinfo"
}
client_options[:audience] = provider_conf['audience'] if provider_conf['audience'].present?
if provider_conf['auth_server_id'].present?
client_options[:authorization_server] = provider_conf['auth_server_id']
client_options[:use_org_auth_server] = false
else
client_options[:use_org_auth_server] = true
end
env['omniauth.strategy'].options[:client_id] = provider_conf['client_id']
env['omniauth.strategy'].options[:client_secret] = provider_conf['client_secret']
env['omniauth.strategy'].options[:client_options] = client_options
end
Rails.application.config.middleware.use OmniAuth::Builder do
provider OmniAuth::Strategies::CustomAzureActiveDirectory, setup: AZURE_SETUP_PROC
end
Rails.application.config.middleware.use OmniAuth::Builder do
provider OmniAuth::Strategies::Okta, setup: OKTA_SETUP_PROC
end
OmniAuth.config.logger = Rails.logger

View file

@ -33,6 +33,7 @@ en:
password_placeholder: "Enter password"
remember_me: "Remember me"
submit: "Log in"
azure_ad_submit: "Sign in with Azure AD"
2fa:
title: "Two-factor authentication"
description: "Enter the one-time code found in your authenticator app to log in to SciNote."