Fixes issue #2741: Authenticate users via SAML.
This commit is contained in:
parent
128e8dea72
commit
a93250e210
8 changed files with 125 additions and 2 deletions
1
Gemfile
1
Gemfile
|
@ -74,6 +74,7 @@ gem 'omniauth-google-oauth2'
|
||||||
gem 'omniauth-linkedin-oauth2'
|
gem 'omniauth-linkedin-oauth2'
|
||||||
gem 'omniauth-microsoft-office365'
|
gem 'omniauth-microsoft-office365'
|
||||||
gem 'omniauth-oauth2'
|
gem 'omniauth-oauth2'
|
||||||
|
gem 'omniauth-saml'
|
||||||
gem 'omniauth-twitter'
|
gem 'omniauth-twitter'
|
||||||
gem 'omniauth-weibo-oauth2'
|
gem 'omniauth-weibo-oauth2'
|
||||||
|
|
||||||
|
|
|
@ -345,6 +345,9 @@ GEM
|
||||||
omniauth-rails_csrf_protection (0.1.2)
|
omniauth-rails_csrf_protection (0.1.2)
|
||||||
actionpack (>= 4.2)
|
actionpack (>= 4.2)
|
||||||
omniauth (>= 1.3.1)
|
omniauth (>= 1.3.1)
|
||||||
|
omniauth-saml (1.10.1)
|
||||||
|
omniauth (~> 1.3, >= 1.3.2)
|
||||||
|
ruby-saml (~> 1.7)
|
||||||
omniauth-twitter (1.4.0)
|
omniauth-twitter (1.4.0)
|
||||||
omniauth-oauth (~> 1.1)
|
omniauth-oauth (~> 1.1)
|
||||||
rack
|
rack
|
||||||
|
@ -456,6 +459,8 @@ GEM
|
||||||
rubocop-rspec (1.33.0)
|
rubocop-rspec (1.33.0)
|
||||||
rubocop (>= 0.60.0)
|
rubocop (>= 0.60.0)
|
||||||
ruby-progressbar (1.10.1)
|
ruby-progressbar (1.10.1)
|
||||||
|
ruby-saml (1.10.2)
|
||||||
|
nokogiri (>= 1.5.10)
|
||||||
ruby_dep (1.5.0)
|
ruby_dep (1.5.0)
|
||||||
rubyzip (1.2.2)
|
rubyzip (1.2.2)
|
||||||
safe_yaml (1.0.5)
|
safe_yaml (1.0.5)
|
||||||
|
@ -610,6 +615,7 @@ DEPENDENCIES
|
||||||
omniauth-microsoft-office365
|
omniauth-microsoft-office365
|
||||||
omniauth-oauth2
|
omniauth-oauth2
|
||||||
omniauth-rails_csrf_protection
|
omniauth-rails_csrf_protection
|
||||||
|
omniauth-saml
|
||||||
omniauth-twitter
|
omniauth-twitter
|
||||||
omniauth-weibo-oauth2
|
omniauth-weibo-oauth2
|
||||||
pg (= 0.21.0)
|
pg (= 0.21.0)
|
||||||
|
|
|
@ -106,4 +106,9 @@ App.Config.set('auth_provider_all', {
|
||||||
name: 'Weibo'
|
name: 'Weibo'
|
||||||
config: 'auth_weibo'
|
config: 'auth_weibo'
|
||||||
class: 'weibo'
|
class: 'weibo'
|
||||||
|
saml:
|
||||||
|
url: '/auth/saml'
|
||||||
|
name: 'SAML'
|
||||||
|
config: 'auth_saml'
|
||||||
|
class: 'saml'
|
||||||
})
|
})
|
||||||
|
|
|
@ -72,7 +72,7 @@
|
||||||
<th>Icon</th>
|
<th>Icon</th>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
</tr>
|
</tr>
|
||||||
<% for icon in ['archived-modifier','arrow-down','arrow-left','arrow-right','arrow-up','bold','chain','chat','checkbox-checked','checkbox-indeterminate','checkbox','checkmark','clipboard','clock','cloud','cog','crown','danger','dashboard','diagonal-cross','document','download','draft-modifier','draggable','dropdown-list','email-button','email','external','eye','eyedropper','facebook-button','facebook','file-archive','file-code','file-email','file-excel','file-pdf','file-powerpoint','file-text','file-unknown','file-word','form','forward','full-logo','github-button','gitlab-button','google-button','group','help','horizontal-rule','important','in-process','inactive-organization','inactive-user','info','internal-modifier','italic','knowledge-base-answer','knowledge-base','line-left-arrow','line-right-arrow','linkedin-button','list','loading','lock-open','lock','logo','logotype','long-arrow-down','long-arrow-right','low-priority','magnifier','marker','message','minus-small','minus','mood-bad','mood-good','mood-ok','mood-sad','mood-superbad','mood-supergood','mute','note','oauth2-button','office365-button','one-ticket','organization','outbound-calls','overflow-button','overviews','package','paperclip','pen','person','phone','plus-small','plus','printer','radio-checked','radio','rearange','received-calls','reload','reopening','reply-all','reply','report','searchdetail','signout','small-dot','sms','spinner-small','split','status-modified-outer-circle','status','stopwatch','strikethrough','switchView','task-state','team','telegram','templates','tools','total-tickets','trash','twitter-button','twitter','underline','unmute','unordered-list','user','web','weibo-button','zoom-in','zoom-out']: %>
|
<% for icon in ['archived-modifier','arrow-down','arrow-left','arrow-right','arrow-up','bold','chain','chat','checkbox-checked','checkbox-indeterminate','checkbox','checkmark','clipboard','clock','cloud','cog','crown','danger','dashboard','diagonal-cross','document','download','draft-modifier','draggable','dropdown-list','email-button','email','external','eye','eyedropper','facebook-button','facebook','file-archive','file-code','file-email','file-excel','file-pdf','file-powerpoint','file-text','file-unknown','file-word','form','forward','full-logo','github-button','gitlab-button','google-button','group','help','horizontal-rule','important','in-process','inactive-organization','inactive-user','info','internal-modifier','italic','knowledge-base-answer','knowledge-base','line-left-arrow','line-right-arrow','linkedin-button','list','loading','lock-open','lock','logo','logotype','long-arrow-down','long-arrow-right','low-priority','magnifier','marker','message','minus-small','minus','mood-bad','mood-good','mood-ok','mood-sad','mood-superbad','mood-supergood','mute','note','oauth2-button','office365-button','one-ticket','organization','outbound-calls','overflow-button','overviews','package','paperclip','pen','person','phone','plus-small','plus','printer','radio-checked','radio','rearange','received-calls','reload','reopening','reply-all','reply','report','saml-button','searchdetail','signout','small-dot','sms','spinner-small','split','status-modified-outer-circle','status','stopwatch','strikethrough','switchView','task-state','team','telegram','templates','tools','total-tickets','trash','twitter-button','twitter','underline','unmute','unordered-list','user','web','saml-button','zoom-in','zoom-out']: %>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<%- @Icon( "#{icon}") %>
|
<%- @Icon( "#{icon}") %>
|
||||||
|
@ -84,4 +84,4 @@
|
||||||
<% end %>
|
<% end %>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3081,6 +3081,10 @@ ol.tabs li {
|
||||||
background: hsl(0,0%,27%);
|
background: hsl(0,0%,27%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.auth-provider--saml {
|
||||||
|
background: hsl(0,0%,27%);
|
||||||
|
}
|
||||||
|
|
||||||
.provider-name {
|
.provider-name {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,8 @@ Rails.application.config.middleware.use OmniAuth::Builder do
|
||||||
# weibo database connect
|
# weibo database connect
|
||||||
provider :weibo_database, 'not_change_will_be_set_by_database', 'not_change_will_be_set_by_database'
|
provider :weibo_database, 'not_change_will_be_set_by_database', 'not_change_will_be_set_by_database'
|
||||||
|
|
||||||
|
# SAML database connect
|
||||||
|
provider :saml_database
|
||||||
end
|
end
|
||||||
|
|
||||||
# This fixes issue #1642 and is required for setups in which Zammad is used
|
# This fixes issue #1642 and is required for setups in which Zammad is used
|
||||||
|
|
79
db/migrate/20190715141227_saml_auth.rb
Normal file
79
db/migrate/20190715141227_saml_auth.rb
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
class SamlAuth < ActiveRecord::Migration[5.2]
|
||||||
|
def up
|
||||||
|
# return if it's a new setup
|
||||||
|
return if !Setting.find_by(name: 'system_init_done')
|
||||||
|
|
||||||
|
Setting.create_if_not_exists(
|
||||||
|
title: 'Authentication via %s',
|
||||||
|
name: 'auth_saml',
|
||||||
|
area: 'Security::ThirdPartyAuthentication',
|
||||||
|
description: 'Enables user authentication via %s.',
|
||||||
|
options: {
|
||||||
|
form: [
|
||||||
|
{
|
||||||
|
display: '',
|
||||||
|
null: true,
|
||||||
|
name: 'auth_saml',
|
||||||
|
tag: 'boolean',
|
||||||
|
options: {
|
||||||
|
true => 'yes',
|
||||||
|
false => 'no',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
preferences: {
|
||||||
|
controller: 'SettingsAreaSwitch',
|
||||||
|
sub: ['auth_saml_credentials'],
|
||||||
|
title_i18n: ['SAML'],
|
||||||
|
description_i18n: ['SAML'],
|
||||||
|
permission: ['admin.security'],
|
||||||
|
},
|
||||||
|
state: false,
|
||||||
|
frontend: true
|
||||||
|
)
|
||||||
|
Setting.create_if_not_exists(
|
||||||
|
title: 'SAML App Credentials',
|
||||||
|
name: 'auth_saml_credentials',
|
||||||
|
area: 'Security::ThirdPartyAuthentication::SAML',
|
||||||
|
description: 'Enables user authentication via SAML.',
|
||||||
|
options: {
|
||||||
|
form: [
|
||||||
|
{
|
||||||
|
display: 'IDP SSO target URL',
|
||||||
|
null: true,
|
||||||
|
name: 'idp_sso_target_url',
|
||||||
|
tag: 'input',
|
||||||
|
placeholder: 'https://capriza.github.io/samling/samling.html',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
display: 'IDP certificate',
|
||||||
|
null: true,
|
||||||
|
name: 'idp_cert',
|
||||||
|
tag: 'input',
|
||||||
|
placeholder: '-----BEGIN CERTIFICATE-----\n...-----END CERTIFICATE-----',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
display: 'IDP certificate fingerprint',
|
||||||
|
null: true,
|
||||||
|
name: 'idp_cert_fingerprint',
|
||||||
|
tag: 'input',
|
||||||
|
placeholder: 'E7:91:B2:E1:...',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
display: 'Name Identifier Format',
|
||||||
|
null: true,
|
||||||
|
name: 'name_identifier_format',
|
||||||
|
tag: 'input',
|
||||||
|
placeholder: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
state: {},
|
||||||
|
preferences: {
|
||||||
|
permission: ['admin.security'],
|
||||||
|
},
|
||||||
|
frontend: false
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
26
vendor/lib/saml_database.rb
vendored
Normal file
26
vendor/lib/saml_database.rb
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
class SamlDatabase < OmniAuth::Strategies::SAML
|
||||||
|
option :name, 'saml'
|
||||||
|
|
||||||
|
def initialize(app, *args, &block)
|
||||||
|
|
||||||
|
http_type = Setting.get('http_type')
|
||||||
|
fqdn = Setting.get('fqdn')
|
||||||
|
|
||||||
|
# Use meta URL as entity id/issues as it is best practice.
|
||||||
|
# See: https://community.zammad.org/t/saml-oidc-third-party-authentication/2533/13
|
||||||
|
entity_id = "#{http_type}://#{fqdn}/auth/saml/metadata"
|
||||||
|
assertion_consumer_service_url = "#{http_type}://#{fqdn}/auth/saml/callback"
|
||||||
|
|
||||||
|
config = Setting.get('auth_saml_credentials') || {}
|
||||||
|
options = config.reject { |k,v| v.blank? }
|
||||||
|
.merge(
|
||||||
|
:assertion_consumer_service_url => assertion_consumer_service_url,
|
||||||
|
:issuer => entity_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
args[0] = options
|
||||||
|
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
Loading…
Reference in a new issue