Compare commits
7 commits
Author | SHA1 | Date | |
---|---|---|---|
|
91fc619ca1 | ||
|
6e16670396 | ||
|
d4587b01f7 | ||
|
834f880fa8 | ||
|
83660b47f7 | ||
|
e94db861c5 | ||
|
fb65f18b5f |
6 changed files with 155 additions and 6 deletions
2
Gemfile
2
Gemfile
|
@ -135,6 +135,8 @@ gem 'stripe'
|
||||||
## to populate db with sample data
|
## to populate db with sample data
|
||||||
gem 'faker'
|
gem 'faker'
|
||||||
|
|
||||||
|
gem 'ruby-saml', '= 1.11'
|
||||||
|
|
||||||
group :production, :staging do
|
group :production, :staging do
|
||||||
# we dont want request timing out in development while using byebug
|
# we dont want request timing out in development while using byebug
|
||||||
gem 'rack-timeout'
|
gem 'rack-timeout'
|
||||||
|
|
13
Gemfile.lock
13
Gemfile.lock
|
@ -427,14 +427,14 @@ GEM
|
||||||
netrc (0.11.0)
|
netrc (0.11.0)
|
||||||
newrelic_rpm (8.9.0)
|
newrelic_rpm (8.9.0)
|
||||||
nio4r (2.5.8)
|
nio4r (2.5.8)
|
||||||
nokogiri (1.13.7)
|
nokogiri (1.13.8)
|
||||||
mini_portile2 (~> 2.8.0)
|
mini_portile2 (~> 2.8.0)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.13.7-arm64-darwin)
|
nokogiri (1.13.8-arm64-darwin)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.13.7-x86_64-darwin)
|
nokogiri (1.13.8-x86_64-darwin)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.13.7-x86_64-linux)
|
nokogiri (1.13.8-x86_64-linux)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
oauth (0.5.10)
|
oauth (0.5.10)
|
||||||
orm_adapter (0.5.0)
|
orm_adapter (0.5.0)
|
||||||
|
@ -558,6 +558,8 @@ GEM
|
||||||
rubocop-rspec (2.12.1)
|
rubocop-rspec (2.12.1)
|
||||||
rubocop (~> 1.31)
|
rubocop (~> 1.31)
|
||||||
ruby-progressbar (1.11.0)
|
ruby-progressbar (1.11.0)
|
||||||
|
ruby-saml (1.11.0)
|
||||||
|
nokogiri (>= 1.5.10)
|
||||||
ruby-vips (2.1.4)
|
ruby-vips (2.1.4)
|
||||||
ffi (~> 1.12)
|
ffi (~> 1.12)
|
||||||
ruby2_keywords (0.0.5)
|
ruby2_keywords (0.0.5)
|
||||||
|
@ -773,6 +775,7 @@ DEPENDENCIES
|
||||||
rubocop-performance
|
rubocop-performance
|
||||||
rubocop-rails
|
rubocop-rails
|
||||||
rubocop-rspec
|
rubocop-rspec
|
||||||
|
ruby-saml (= 1.11)
|
||||||
scout_apm
|
scout_apm
|
||||||
seed_dump
|
seed_dump
|
||||||
sentry-rails (~> 5.3)
|
sentry-rails (~> 5.3)
|
||||||
|
@ -805,4 +808,4 @@ RUBY VERSION
|
||||||
ruby 3.0.4p208
|
ruby 3.0.4p208
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.3.17
|
2.3.16
|
||||||
|
|
|
@ -5,6 +5,7 @@ class DashboardController < ActionController::Base
|
||||||
around_action :switch_locale
|
around_action :switch_locale
|
||||||
before_action :ensure_installation_onboarding, only: [:index]
|
before_action :ensure_installation_onboarding, only: [:index]
|
||||||
before_action :redirect_to_custom_domain_page
|
before_action :redirect_to_custom_domain_page
|
||||||
|
before_action :redirect_to_saml_login
|
||||||
|
|
||||||
layout 'vueapp'
|
layout 'vueapp'
|
||||||
|
|
||||||
|
@ -47,6 +48,10 @@ class DashboardController < ActionController::Base
|
||||||
redirect_to "/hc/#{portal.slug}"
|
redirect_to "/hc/#{portal.slug}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def redirect_to_saml_login
|
||||||
|
redirect_to '/saml' and return unless current_user
|
||||||
|
end
|
||||||
|
|
||||||
def app_config
|
def app_config
|
||||||
{
|
{
|
||||||
APP_VERSION: Chatwoot.config[:version],
|
APP_VERSION: Chatwoot.config[:version],
|
||||||
|
@ -57,4 +62,29 @@ class DashboardController < ActionController::Base
|
||||||
IS_ENTERPRISE: ChatwootApp.enterprise?
|
IS_ENTERPRISE: ChatwootApp.enterprise?
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def saml_settings
|
||||||
|
settings = OneLogin::RubySaml::Settings.new
|
||||||
|
|
||||||
|
settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume"
|
||||||
|
settings.sp_entity_id = "http://#{request.host}/saml/metadata"
|
||||||
|
settings.idp_entity_id = 'https://app.onelogin.com/saml/metadata/1835014'
|
||||||
|
settings.idp_sso_target_url = 'https://app.onelogin.com/trust/saml2/http-post/sso/1835014'
|
||||||
|
settings.idp_slo_target_url = 'https://app.onelogin.com/trust/saml2/http-redirect/slo/1835014'
|
||||||
|
settings.name_identifier_format = 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'
|
||||||
|
|
||||||
|
# Optional for most SAML IdPs
|
||||||
|
settings.authn_context = 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'
|
||||||
|
# or as an array
|
||||||
|
settings.authn_context = [
|
||||||
|
'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport',
|
||||||
|
'urn:oasis:names:tc:SAML:2.0:ac:classes:Password'
|
||||||
|
]
|
||||||
|
|
||||||
|
# Optional bindings (defaults to Redirect for logout POST for ACS)
|
||||||
|
settings.single_logout_service_binding = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect' # or :post, :redirect
|
||||||
|
settings.assertion_consumer_service_binding = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST' # or :post, :redirect
|
||||||
|
|
||||||
|
settings
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
106
app/controllers/saml_controller.rb
Normal file
106
app/controllers/saml_controller.rb
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
# This controller expects you to use the URLs /saml/init and /saml/consume in your OneLogin application.
|
||||||
|
class SamlController < ApplicationController
|
||||||
|
# skip_before_action :verify_authenticity_token, :only => [:consume]
|
||||||
|
layout 'vueapp'
|
||||||
|
include SsoAuthenticatable
|
||||||
|
|
||||||
|
def index
|
||||||
|
request = OneLogin::RubySaml::Authrequest.new
|
||||||
|
redirect_to(request.create(saml_settings))
|
||||||
|
end
|
||||||
|
|
||||||
|
def consume
|
||||||
|
response = OneLogin::RubySaml::Response.new(params['SAMLResponse'])
|
||||||
|
response.settings = saml_settings
|
||||||
|
# We validate the SAML Response and check if the user already exists in the system
|
||||||
|
if response.is_valid?
|
||||||
|
# authorize_success, log the user
|
||||||
|
find_the_resource(response.nameid)
|
||||||
|
create_session_and_assign_token
|
||||||
|
|
||||||
|
encoded_email = ERB::Util.url_encode(@resource.email)
|
||||||
|
redirect_to "https://db7b-103-51-75-84.in.ngrok.io/app/login?email=#{encoded_email}&sso_auth_token=#{@resource.generate_sso_auth_token}"
|
||||||
|
else
|
||||||
|
Rails.logger.error "Response Invalid. Errors: #{response.errors}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def metadata
|
||||||
|
settings = saml_settings
|
||||||
|
meta = OneLogin::RubySaml::Metadata.new
|
||||||
|
render xml: meta.generate(settings, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
def logout
|
||||||
|
# If we're given a logout request, handle it in the IdP logout initiated method
|
||||||
|
idp_logout_request
|
||||||
|
end
|
||||||
|
|
||||||
|
# Method to handle IdP initiated logouts
|
||||||
|
def idp_logout_request
|
||||||
|
settings = saml_settings
|
||||||
|
logout_request = OneLogin::RubySaml::SloLogoutrequest.new(params[:SAMLRequest], settings: settings)
|
||||||
|
unless logout_request.is_valid?
|
||||||
|
error_msg = "IdP initiated LogoutRequest was not valid!. Errors: #{logout_request.errors}"
|
||||||
|
Rails.logger.error error_msg
|
||||||
|
render inline: error_msg
|
||||||
|
end
|
||||||
|
Rails.logger.info "IdP initiated Logout for #{logout_request.nameid}"
|
||||||
|
|
||||||
|
# Actually log out this session
|
||||||
|
reset_session
|
||||||
|
|
||||||
|
logout_response = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, logout_request.id, nil, RelayState: params[:RelayState])
|
||||||
|
redirect_to logout_response
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def find_the_resource(email)
|
||||||
|
@resource = User.find_by(email: email)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_session_and_assign_token
|
||||||
|
create_and_assign_token
|
||||||
|
sign_in(:user, @resource, store: true, bypass: false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_and_assign_token
|
||||||
|
if @resource.respond_to?(:with_lock)
|
||||||
|
@resource.with_lock do
|
||||||
|
@token = @resource.create_token
|
||||||
|
@resource.save!
|
||||||
|
end
|
||||||
|
else
|
||||||
|
@token = @resource.create_token
|
||||||
|
@resource.save!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_create_success
|
||||||
|
render partial: 'devise/auth.json', locals: { resource: @resource }
|
||||||
|
end
|
||||||
|
|
||||||
|
def saml_settings
|
||||||
|
settings = OneLogin::RubySaml::Settings.new
|
||||||
|
|
||||||
|
settings.soft = true
|
||||||
|
|
||||||
|
settings.assertion_consumer_service_url = 'https://db7b-103-51-75-84.in.ngrok.io/saml/consume'
|
||||||
|
settings.sp_entity_id = 'https://db7b-103-51-75-84.in.ngrok.io/saml/metadata'
|
||||||
|
|
||||||
|
settings.idp_entity_id = 'https://app.onelogin.com/saml2'
|
||||||
|
settings.idp_sso_target_url = 'https://chatwoot-dev.onelogin.com/trust/saml2/http-post/sso/de789d10-0617-44e9-8fd6-9d798809cfbf'
|
||||||
|
settings.idp_slo_target_url = 'https://chatwoot-dev.onelogin.com/trust/saml2/http-redirect/slo/1861655'
|
||||||
|
|
||||||
|
settings.name_identifier_format = 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'
|
||||||
|
settings.idp_cert_fingerprint = 'FD:17:5E:81:F8:F5:88:EF:21:AB:94:44:3E:4A:C4:72:94:E2:63:AE'
|
||||||
|
settings.idp_cert_fingerprint_algorithm = 'http://www.w3.org/2000/09/xmldsig#sha1'
|
||||||
|
|
||||||
|
# Optional bindings (defaults to Redirect for logout POST for ACS)
|
||||||
|
settings.single_logout_service_binding = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect' # or :post, :redirect
|
||||||
|
settings.assertion_consumer_service_binding = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST' # or :post, :redirect
|
||||||
|
|
||||||
|
settings
|
||||||
|
end
|
||||||
|
end
|
|
@ -81,7 +81,7 @@
|
||||||
</form>
|
</form>
|
||||||
<div class="column text-center sigin--footer">
|
<div class="column text-center sigin--footer">
|
||||||
<span>{{ $t('REGISTER.HAVE_AN_ACCOUNT') }}</span>
|
<span>{{ $t('REGISTER.HAVE_AN_ACCOUNT') }}</span>
|
||||||
<router-link to="/app/login">
|
<router-link to="/saml">
|
||||||
{{
|
{{
|
||||||
useInstallationName(
|
useInstallationName(
|
||||||
$t('LOGIN.TITLE'),
|
$t('LOGIN.TITLE'),
|
||||||
|
|
|
@ -228,6 +228,14 @@ Rails.application.routes.draw do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
resources :saml, only: [:index] do
|
||||||
|
collection do
|
||||||
|
post :consume
|
||||||
|
get :metadata
|
||||||
|
get :logout
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if ChatwootApp.enterprise?
|
if ChatwootApp.enterprise?
|
||||||
namespace :enterprise, defaults: { format: 'json' } do
|
namespace :enterprise, defaults: { format: 'json' } do
|
||||||
namespace :api do
|
namespace :api do
|
||||||
|
|
Loading…
Reference in a new issue