diff --git a/Gemfile b/Gemfile
index da451f7be..8561f8891 100644
--- a/Gemfile
+++ b/Gemfile
@@ -74,6 +74,7 @@ gem 'omniauth-google-oauth2'
gem 'omniauth-linkedin-oauth2'
gem 'omniauth-microsoft-office365'
gem 'omniauth-oauth2'
+gem 'omniauth-saml'
gem 'omniauth-twitter'
gem 'omniauth-weibo-oauth2'
diff --git a/Gemfile.lock b/Gemfile.lock
index fdb353fc2..bce39abb3 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -345,6 +345,9 @@ GEM
omniauth-rails_csrf_protection (0.1.2)
actionpack (>= 4.2)
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-oauth (~> 1.1)
rack
@@ -456,6 +459,8 @@ GEM
rubocop-rspec (1.33.0)
rubocop (>= 0.60.0)
ruby-progressbar (1.10.1)
+ ruby-saml (1.10.2)
+ nokogiri (>= 1.5.10)
ruby_dep (1.5.0)
rubyzip (1.2.2)
safe_yaml (1.0.5)
@@ -610,6 +615,7 @@ DEPENDENCIES
omniauth-microsoft-office365
omniauth-oauth2
omniauth-rails_csrf_protection
+ omniauth-saml
omniauth-twitter
omniauth-weibo-oauth2
pg (= 0.21.0)
diff --git a/app/assets/javascripts/app/controllers/_profile/linked_accounts.coffee b/app/assets/javascripts/app/controllers/_profile/linked_accounts.coffee
index a5c431bbd..d8ee745ef 100644
--- a/app/assets/javascripts/app/controllers/_profile/linked_accounts.coffee
+++ b/app/assets/javascripts/app/controllers/_profile/linked_accounts.coffee
@@ -106,4 +106,9 @@ App.Config.set('auth_provider_all', {
name: 'Weibo'
config: 'auth_weibo'
class: 'weibo'
+ saml:
+ url: '/auth/saml'
+ name: 'SAML'
+ config: 'auth_saml'
+ class: 'saml'
})
diff --git a/app/assets/javascripts/app/views/layout_ref/ui.jst.eco b/app/assets/javascripts/app/views/layout_ref/ui.jst.eco
index 962a6e4b0..4301d6ca5 100644
--- a/app/assets/javascripts/app/views/layout_ref/ui.jst.eco
+++ b/app/assets/javascripts/app/views/layout_ref/ui.jst.eco
@@ -72,7 +72,7 @@
<%- @Icon( "#{icon}") %>
@@ -84,4 +84,4 @@
<% end %>
-
\ No newline at end of file
+
diff --git a/app/assets/stylesheets/zammad.scss b/app/assets/stylesheets/zammad.scss
index 6bcb25d58..1561debe0 100644
--- a/app/assets/stylesheets/zammad.scss
+++ b/app/assets/stylesheets/zammad.scss
@@ -3081,6 +3081,10 @@ ol.tabs li {
background: hsl(0,0%,27%);
}
+ &.auth-provider--saml {
+ background: hsl(0,0%,27%);
+ }
+
.provider-name {
flex: 1;
}
diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb
index 99391fc43..1f7682c97 100644
--- a/config/initializers/omniauth.rb
+++ b/config/initializers/omniauth.rb
@@ -49,6 +49,8 @@ Rails.application.config.middleware.use OmniAuth::Builder do
# weibo database connect
provider :weibo_database, 'not_change_will_be_set_by_database', 'not_change_will_be_set_by_database'
+ # SAML database connect
+ provider :saml_database
end
# This fixes issue #1642 and is required for setups in which Zammad is used
diff --git a/db/migrate/20190715141227_saml_auth.rb b/db/migrate/20190715141227_saml_auth.rb
new file mode 100644
index 000000000..6314a46a1
--- /dev/null
+++ b/db/migrate/20190715141227_saml_auth.rb
@@ -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
diff --git a/vendor/lib/saml_database.rb b/vendor/lib/saml_database.rb
new file mode 100644
index 000000000..766dbd937
--- /dev/null
+++ b/vendor/lib/saml_database.rb
@@ -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
|