Configure OIDC

This commit is contained in:
Matt-Yorkley
2022-02-25 18:52:43 +00:00
committed by Maikel Linke
parent b36fae1bbb
commit 617164684c
13 changed files with 174 additions and 6 deletions

View File

@@ -6,3 +6,6 @@ STRIPE_SECRET_TEST_API_KEY="bogus_key"
STRIPE_CUSTOMER="bogus_customer"
SITE_URL="test.host"
OPENID_APP_ID="test-provider"
OPENID_APP_SECRET="12345"

View File

@@ -0,0 +1,7 @@
# frozen_string_literal: true
module Admin
class OidcSettingsController < Spree::Admin::BaseController
def index; end
end
end

View File

@@ -0,0 +1,16 @@
# frozen_string_literal: true
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def openid_connect
spree_current_user.link_from_omniauth(request.env["omniauth.auth"])
redirect_to admin_oidc_settings_path
end
def failure
error_message = request.env["omniauth.error"].to_s
flash[:error] = t("devise.oidc.failure", error: error_message)
super
end
end

View File

@@ -7,8 +7,10 @@ module Spree
searchable_attributes :email
devise :database_authenticatable, :token_authenticatable, :registerable, :recoverable,
:rememberable, :trackable, :validatable,
:encryptable, :confirmable, encryptor: 'authlogic_sha512', reconfirmable: true
:rememberable, :trackable, :validatable, :omniauthable,
:encryptable, :confirmable,
encryptor: 'authlogic_sha512', reconfirmable: true,
omniauth_providers: [:openid_connect]
has_many :orders
belongs_to :ship_address, class_name: 'Spree::Address'
@@ -44,6 +46,8 @@ module Spree
after_create :associate_customers, :associate_orders
validate :limit_owned_enterprises
validates :uid, uniqueness: true, if: lambda { uid.present? }
validates_email :uid, if: lambda { uid.present? }
class DestroyWithOrdersError < StandardError; end
@@ -51,6 +55,10 @@ module Spree
User.admin.count > 0
end
def link_from_omniauth(auth)
update!(provider: auth.provider, uid: auth.uid)
end
# Whether a user has a role or not.
def has_spree_role?(role_in_question)
spree_roles.where(name: role_in_question.to_s).any?

View File

@@ -0,0 +1,25 @@
- content_for :page_title do
= t(".title")
= render 'admin/shared/enterprises_sub_menu'
%div
%h2= t(".connect")
%br
- if spree_current_user.provider == 'openid_connect' && spree_current_user.uid.present?
= t(".already_connected")
= spree_current_user.uid
%br
%br
= t(".view_account")
= link_to t(".les_communs_link"), "#{ Devise.omniauth_configs[:openid_connect].options[:issuer] }/account"
- else
= t(".link_your_account")
%br
%br
= button_to t(".link_account_button"),
Spree::Core::Engine.routes.url_helpers.spree_user_openid_connect_omniauth_authorize_path(auth_type: "login"),
method: :post

View File

@@ -2,3 +2,5 @@
%ul#sub_nav.inline-menu{"data-hook" => "admin_enterprise_sub_tabs"}
= tab :enterprises, url: main_app.admin_enterprises_path
= tab :enterprise_relationships, url: main_app.admin_enterprise_relationships_path
- if ENV["OPENID_APP_ID"].present? && ENV["OPENID_APP_SECRET"].present?
= tab :oidc_settings, url: main_app.admin_oidc_settings_path

View File

@@ -4,7 +4,7 @@
= tab :orders, :subscriptions, :customer_details, :adjustments, :payments, :return_authorizations, url: admin_orders_path('q[s]' => 'completed_at desc'), icon: 'icon-shopping-cart'
= tab :reports, url: main_app.admin_reports_path, icon: 'icon-file'
= tab :general_settings, :mail_methods, :tax_categories, :tax_rates, :tax_settings, :zones, :countries, :states, :payment_methods, :taxonomies, :shipping_methods, :shipping_categories, :enterprise_fees, :contents, :invoice_settings, :matomo_settings, :stripe_connect_settings, label: 'configuration', icon: 'icon-wrench', url: edit_admin_general_settings_path
= tab :enterprises, :enterprise_relationships, url: main_app.admin_enterprises_path
= tab :enterprises, :enterprise_relationships, :oidc_settings, url: main_app.admin_enterprises_path
= tab :customers, url: main_app.admin_customers_path
= tab :enterprise_groups, url: main_app.admin_enterprise_groups_path, label: 'groups'
- if can? :admin, Spree::User

View File

@@ -142,3 +142,25 @@ Devise::TokenAuthenticatable.setup do |config|
# Defines name of the authentication token params key
config.token_authentication_key = :auth_token
end
if ENV["OPENID_APP_ID"].present? && ENV["OPENID_APP_SECRET"].present?
Devise.setup do |config|
protocol = Rails.env.development? ? "http://" : "https://"
config.omniauth :openid_connect, {
name: :openid_connect,
issuer: "https://login.lescommuns.org/auth/realms/data-food-consortium",
scope: [:openid, :profile, :email],
response_type: :code,
uid_field: "email",
discovery: true,
client_auth_method: :jwks,
client_options: {
identifier: ENV["OPENID_APP_ID"],
secret: ENV["OPENID_APP_SECRET"],
redirect_uri: "#{protocol}#{ENV["SITE_URL"]}/user/spree_user/auth/openid_connect/callback",
jwks_uri: 'https://login.lescommuns.org/auth/realms/data-food-consortium/protocol/openid-connect/certs'
}
}
end
end

View File

@@ -224,7 +224,8 @@ en:
updated_not_active: "Your password has been reset, but your email has not been confirmed yet."
updated: "Your password was changed successfully. You are now signed in."
send_instructions: "You will receive an email with instructions about how to confirm your account in a few minutes."
oidc:
failure: "Could not sign in: %{error}"
home_page_alert_html: "Home page alert HTML"
hub_signup_case_studies_html: "Hub signup case studies HTML"
hub_signup_detail_html: "Hub signup detail HTML"
@@ -1423,6 +1424,15 @@ en:
formatted_data: Formatted Data
packing:
name: "Packing Reports"
oidc_settings:
index:
title: "OIDC Settings"
connect: "Connect Your Account"
already_connected: "Your account is already linked to this DFC authorization account:"
les_communs_link: "Les Communs Open ID server"
link_your_account: "You need first to link your account with the authorization provider used by DFC (Les Communs Open ID Connect)."
link_account_button: "Link your Les Communs OIDC Account"
view_account: "To view your account, see:"
subscriptions:
index:
title: "Subscriptions"

View File

@@ -107,6 +107,8 @@ Openfoodnetwork::Application.routes.draw do
put :unpause, on: :member
end
resources :oidc_settings, only: :index
resources :subscription_line_items, only: [], format: :json do
post :build, on: :collection
end

View File

@@ -9,12 +9,14 @@ end
# Overriding Devise routes to use our own controller
Spree::Core::Engine.routes.draw do
devise_for :spree_user,
:router_name => "spree",
:class_name => 'Spree::User',
:controllers => { :sessions => 'spree/user_sessions',
:registrations => 'user_registrations',
:passwords => 'user_passwords',
:confirmations => 'user_confirmations'},
:skip => [:unlocks, :omniauth_callbacks],
:confirmations => 'user_confirmations',
:omniauth_callbacks => "omniauth_callbacks" },
:skip => [:unlocks],
:path_names => { :sign_out => 'logout' },
:path_prefix => :user

View File

@@ -240,4 +240,18 @@ describe Spree::User do
expect(user.disabled).to be_falsey
end
end
describe "#link_from_omniauth" do
let!(:user) { create(:user, email: "user@email.com") }
let(:auth) { double(:auth, provider: "openid_connect", uid: "user@email.com") }
it "creates a user without errors" do
user.link_from_omniauth(auth)
expect(user.errors.present?).to be false
expect(user.confirmed?).to be true
expect(user.provider).to eq "openid_connect"
expect(user.uid).to eq "user@email.com"
end
end
end

View File

@@ -0,0 +1,57 @@
# frozen_string_literal: true
require 'spec_helper'
describe OmniauthCallbacksController, type: :request do
include AuthenticationHelper
OmniAuth.config.test_mode = true
subject do
login_as user
post '/user/spree_user/auth/openid_connect/callback', params: { code: 'code123' }
request.env['devise.mapping'] = Devise.mappings[:spree_user]
request.env['omniauth.auth'] = omniauth_response
end
let(:user) { create(:user) }
context 'when the omniauth setup is returning with an authorization' do
let!(:omniauth_response) do
OmniAuth.config.mock_auth[:openid_connect] = OmniAuth::AuthHash.new(
'provider' => 'openid_connect',
'uid' => 'john@doe.com',
'info' => {
'email' => 'john@doe.com',
'first_name' => 'John',
'last_name' => 'Doe'
}
)
end
it 'is successful' do
subject
expect(user.provider).to eq "openid_connect"
expect(user.uid).to eq "john@doe.com"
expect(request.cookies[:omniauth_connect]).to be_nil
expect(response.status).to eq(302)
end
end
context 'when the omniauth openid_connect is mocked with an error' do
let!(:omniauth_response) do
OmniAuth.config.mock_auth[:openid_connect] = :invalid_credentials
end
it 'fails with bad auth data' do
subject
expect(user.provider).to be_nil
expect(user.uid).to be_nil
expect(request.cookies[:omniauth_connect]).to be_nil
expect(response.status).to eq(302)
end
end
end