Skip to content

Commit

Permalink
Switch from Azure AD to Entra ID (#845)
Browse files Browse the repository at this point in the history
* Switch from azure AD to Entra ID

* Update documentation

* update tests to support entra id

* fix docs comments

* migration first cut

* Fix errors in migration
  • Loading branch information
JeffreyThiessen authored Dec 4, 2024
1 parent c910a05 commit 676e08e
Show file tree
Hide file tree
Showing 14 changed files with 108 additions and 50 deletions.
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ gem 'devise', '~> 4.9.4'

# Use OmniAuth auth
gem 'omniauth'
gem 'omniauth-azure-activedirectory-v2'
gem 'omniauth-entra-id'
gem 'omniauth-rails_csrf_protection'
gem 'omniauth-saml'

Expand Down
4 changes: 2 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ GEM
hashie (>= 3.4.6)
rack (>= 2.2.3)
rack-protection
omniauth-azure-activedirectory-v2 (2.3.0)
omniauth-entra-id (3.0.0)
omniauth-oauth2 (~> 1.8)
omniauth-oauth2 (1.8.0)
oauth2 (>= 1.4, < 3)
Expand Down Expand Up @@ -679,7 +679,7 @@ DEPENDENCIES
lookbook (~> 2.1, >= 2.1.1)
minitest (= 5.24.1)
omniauth
omniauth-azure-activedirectory-v2
omniauth-entra-id
omniauth-rails_csrf_protection
omniauth-saml
pagy (~> 9.0.5)
Expand Down
9 changes: 9 additions & 0 deletions app/assets/icons/heroicons/entra_id.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion app/controllers/users/omniauth_callbacks_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def all

alias developer all
alias saml all
alias azure_activedirectory_v2 all
alias entra_id all

def failure
if @user.respond_to?(:errors) && @user.errors.present? && @user.errors.full_messages.present?
Expand Down
6 changes: 3 additions & 3 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ def self.from_omniauth(auth)
user = from_developer(user, auth)
when 'saml'
user = from_saml(user, auth)
when 'azure_activedirectory_v2'
user = from_azure_activedirectory_v2(user, auth)
when 'entra_id'
user = from_entra_id(user, auth)
end

user.save
Expand All @@ -80,7 +80,7 @@ def self.from_saml(user, auth)
user
end

def self.from_azure_activedirectory_v2(user, auth)
def self.from_entra_id(user, auth)
user.first_name = auth.info.first_name
user.last_name = auth.info.last_name
user
Expand Down
14 changes: 7 additions & 7 deletions config/authentication/auth_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@ development:
# developer_icon:
# saml_text:
# saml_icon:
# azure_activedirectory_v2_text:
# azure_activedirectory_v2_icon:
# entra_id_text:
# entra_id_icon:

test:
omniauth_providers: [developer, saml, azure_activedirectory_v2]
omniauth_providers: [developer, saml, entra_id]
# developer_text:
# developer_icon:
# saml_text:
# saml_icon:
# azure_activedirectory_v2_text:
# azure_activedirectory_v2_icon:
# entra_id_text:
# entra_id_icon:

production:
omniauth_providers: []
# saml_text:
# saml_icon:
# azure_activedirectory_v2_text:
# azure_activedirectory_v2_icon:
# entra_id_text:
# entra_id_icon:
4 changes: 2 additions & 2 deletions config/initializers/devise.rb
Original file line number Diff line number Diff line change
Expand Up @@ -305,9 +305,9 @@
config.omniauth :saml, saml_options
end

if Rails.configuration.auth_config['omniauth_providers'].include? 'azure_activedirectory_v2'
if Rails.configuration.auth_config['omniauth_providers'].include? 'entra_id'
# expected keys are: [:client_id, :client_secret, :tenant_id]
config.omniauth :azure_activedirectory_v2, Rails.application.credentials.azure
config.omniauth :entra_id, Rails.application.credentials.entra_id
end
end

Expand Down
49 changes: 49 additions & 0 deletions db/migrate/20241120173553_azure_ad_to_entra_id.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# frozen_string_literal: true

# Migration to move Azure AD users to Entra ID
class AzureAdToEntraId < ActiveRecord::Migration[7.2]
def up
azure_user_list = User.where(provider: 'azure_activedirectory_v2').all

# If there are no AD users in the database, exit early.
unless azure_user_list.count.positive?
Rails.logger.info 'No Azure AD users in database. No Migration needed.'
return
end

tenant_id = find_tenant_id

azure_user_list.each { |u| migrate_user(u, tenant_id) }
end

# Tries to get tenant_id from the entra_id credentials. Raises an error to abort the migration if it cannot be found.
def find_tenant_id
begin
tenant_id = Rails.application.credentials.entra_id[:tenant_id]
rescue NoMethodError
error_msg = 'Could not find credentials for Entra ID'
Rails.logger.error error_msg
raise StandardError, error_msg
end

unless tenant_id
error_msg = 'tenant_id for entra_id is not specified'
Rails.logger.error error_msg
raise StandardError, error_msg
end

tenant_id
end

def migrate_user(user, tenant_id)
old_uid = user.uid
new_uid = tenant_id + old_uid
user.uid = new_uid
user.provider = 'entra_id'
user.save!
end

def down
raise ActiveRecord::IrreversibleMigration
end
end
2 changes: 1 addition & 1 deletion db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 20 additions & 20 deletions docs-site/docs/configuration/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,33 +19,33 @@ development:
# developer_icon:
# saml_text:
# saml_icon:
# azure_activedirectory_v2_text:
# azure_activedirectory_v2_icon:
# entra_id_text:
# entra_id_icon:

test:
omniauth_providers: [developer, saml, azure_activedirectory_v2]
omniauth_providers: [developer, saml, entra_id]
# developer_text:
# developer_icon:
# saml_text:
# saml_icon:
# azure_activedirectory_v2_text:
# azure_activedirectory_v2_icon:
# entra_id_text:
# entra_id_icon:

# production:
# omniauth_providers:
# omniauth_providers: []
# saml_text:
# saml_icon:
# azure_activedirectory_v2_text:
# azure_activedirectory_v2_icon:
# entra_id_text:
# entra_id_icon:
```

For your production environment, uncomment and edit the relevant `production` lines.

For Azure
For Entra ID, formerly Azure Active Directory

```yml
production:
omniauth_providers: [azure_activedirectory_v2]
omniauth_providers: [entra_id]
```
For SAML
Expand All @@ -65,15 +65,15 @@ You can edit this file with the following command.
EDITOR="vim --nofork" bin/rails credentials:edit
```

#### Azure Active Directory V2
#### Entra ID (formerly Azure Active Directory V2)

For Azure, you will need the following lines
For Entra, you will need the following lines

```yml
azure:
client_id: YOUR_AZURE_CLIENT_ID
client_secret: YOUR_AZURE_CLIENT_SECRET
tenant_id: YOUR_AZURE_TENANT_ID
entra_id:
client_id: YOUR_CLIENT_ID
client_secret: YOUR_CLIENT_SECRET
tenant_id: YOUR_TENANT_ID
```
#### SAML
Expand All @@ -91,7 +91,7 @@ saml:
You can change the display name and icon to match your organization.
In the `config/authentication/auth_config.yml` file, edit `_text` and `_icon` fields appropriate for your Azure or SAML setup.
In the `config/authentication/auth_config.yml` file, edit `_text` and `_icon` fields appropriate for your Entra or SAML setup.

Put your organizations name in the `_text` field.

Expand All @@ -101,9 +101,9 @@ Example:

```yml
production:
omniauth_providers: [azure_activedirectory_v2]
omniauth_providers: [entra_id]
# saml_text:
# saml_icon:
azure_activedirectory_v2_text: Tyrell Corporation
azure_activedirectory_v2_icon: tyrell.svg
entra_id_text: Tyrell Corporation
entra_id_icon: tyrell.svg
```
6 changes: 3 additions & 3 deletions test/helpers/view_helper_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ class ViewHelperTest < ActionView::TestCase
include ViewHelper

setup do
%w[developer saml azure_activedirectory_v2].each do |provider|
%w[developer saml entra_id].each do |provider|
Rails.configuration.auth_config["#{provider}_icon"] = '../../../test/fixtures/files/tyrell.svg'
end
end

teardown do
%w[developer saml azure_activedirectory_v2].each do |provider|
%w[developer saml entra_id].each do |provider|
Rails.configuration.auth_config["#{provider}_icon"] = nil
end
end
Expand All @@ -24,7 +24,7 @@ class ViewHelperTest < ActionView::TestCase
end

test 'should load override icons' do
%w[developer saml azure_activedirectory_v2].each do |provider|
%w[developer saml entra_id].each do |provider|
source = viral_icon_source("#{provider}_icon")
assert source.include? %(class="Viral-Icon__Svg icon-#{provider}_icon")
assert source.include? 'viewbox="0 0 1140 1012"'
Expand Down
4 changes: 2 additions & 2 deletions test/integration/omniauth_callbacks_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class OmniauthCallbacksAzureTest < OmniauthIntegrationTestCase
test 'get first user or create' do
valid_azure_login_setup
assert_difference 'User.count' do
get '/users/auth/azure_activedirectory_v2/callback'
get '/users/auth/entra_id/callback'
end

assert session.key? 'warden.user.user.key'
Expand All @@ -41,7 +41,7 @@ class OmniauthCallbacksAzureTest < OmniauthIntegrationTestCase
test 'invalid get first user or create' do
invalid_azure_login_setup
assert_no_changes 'User.count' do
get '/users/auth/azure_activedirectory_v2/callback'
get '/users/auth/entra_id/callback'
end

assert_equal :invalid_credentials, request.env['omniauth.error.type']
Expand Down
6 changes: 3 additions & 3 deletions test/system/sign_in_display_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class SignInDisplayTest < ApplicationSystemTestCase
end

teardown do
%w[developer saml azure_activedirectory_v2].each do |provider|
%w[developer saml entra_id].each do |provider|
Rails.configuration.auth_config["#{provider}_text"] = nil
Rails.configuration.auth_config["#{provider}_icon"] = nil
end
Expand All @@ -20,7 +20,7 @@ class SignInDisplayTest < ApplicationSystemTestCase
within %(div[class="grid gap-2"]) do
assert_selector 'svg', count: 3 # Local Account does not use an icon
assert_text I18n.t(:'devise.sessions.new_with_providers.local_button')
%w[developer saml azure_activedirectory_v2].each do |provider|
%w[developer saml entra_id].each do |provider|
assert_text I18n.t(:'devise.sessions.new_with_providers.omniauth').to_s.sub!(
'%{provider}', # rubocop:disable Style/FormatStringToken
OmniAuth::Utils.camelize(provider)
Expand All @@ -31,7 +31,7 @@ class SignInDisplayTest < ApplicationSystemTestCase
end

test 'should display sign in with custom text and icon' do
custom_provider = 'azure_activedirectory_v2'
custom_provider = 'entra_id'
custom_text = 'Tyrell Corporation'
Rails.configuration.auth_config["#{custom_provider}_text"] = custom_text
Rails.configuration.auth_config["#{custom_provider}_icon"] = @test_file_relative_path
Expand Down
10 changes: 5 additions & 5 deletions test/test_helpers/omniauth_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def invalid_developer_login_setup

module OmniauthAzureHelper
RESPONSE = {
provider: 'azure_activedirectory_v2',
provider: 'entra_id',
uid: '12345678-90-abcd-ef12-34567890abcd',
info: {
email: '[email protected]',
Expand All @@ -49,14 +49,14 @@ module OmniauthAzureHelper

def valid_azure_login_setup
OmniAuth.config.test_mode = true
OmniAuth.config.mock_auth[:azure_activedirectory_v2] = OmniAuth::AuthHash.new(RESPONSE)
Rails.application.env_config['omniauth.auth'] = OmniAuth.config.mock_auth[:azure_activedirectory_v2]
OmniAuth.config.mock_auth[:entra_id] = OmniAuth::AuthHash.new(RESPONSE)
Rails.application.env_config['omniauth.auth'] = OmniAuth.config.mock_auth[:entra_id]
end

def invalid_azure_login_setup
OmniAuth.config.test_mode = true
OmniAuth.config.mock_auth[:azure_activedirectory_v2] = :invalid_credentials
Rails.application.env_config['omniauth.auth'] = OmniAuth.config.mock_auth[:azure_activedirectory_v2]
OmniAuth.config.mock_auth[:entra_id] = :invalid_credentials
Rails.application.env_config['omniauth.auth'] = OmniAuth.config.mock_auth[:entra_id]
end
end

Expand Down

0 comments on commit 676e08e

Please sign in to comment.