Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
e7568f5a
Commit
e7568f5a
authored
Dec 08, 2021
by
Benoit BERAUD
Committed by
Vasilii Iakliushin
Dec 08, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Support external authentification of users through multiple SAML providers
parent
f934bd0e
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
251 additions
and
3 deletions
+251
-3
app/controllers/omniauth_callbacks_controller.rb
app/controllers/omniauth_callbacks_controller.rb
+1
-1
app/helpers/auth_helper.rb
app/helpers/auth_helper.rb
+11
-0
app/views/profiles/accounts/_providers.html.haml
app/views/profiles/accounts/_providers.html.haml
+5
-2
doc/integration/saml.md
doc/integration/saml.md
+68
-0
spec/helpers/auth_helper_spec.rb
spec/helpers/auth_helper_spec.rb
+166
-0
No files found.
app/controllers/omniauth_callbacks_controller.rb
View file @
e7568f5a
...
@@ -9,7 +9,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
...
@@ -9,7 +9,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
after_action
:verify_known_sign_in
after_action
:verify_known_sign_in
protect_from_forgery
except:
[
:kerberos
,
:saml
,
:cas3
,
:failure
],
with: :exception
,
prepend:
true
protect_from_forgery
except:
[
:kerberos
,
:saml
,
:cas3
,
:failure
]
+
AuthHelper
.
saml_providers
,
with: :exception
,
prepend:
true
feature_category
:authentication_and_authorization
feature_category
:authentication_and_authorization
...
...
app/helpers/auth_helper.rb
View file @
e7568f5a
...
@@ -86,6 +86,17 @@ module AuthHelper
...
@@ -86,6 +86,17 @@ module AuthHelper
auth_providers
.
select
{
|
provider
|
form_based_provider?
(
provider
)
}
auth_providers
.
select
{
|
provider
|
form_based_provider?
(
provider
)
}
end
end
def
saml_providers
auth_providers
.
select
{
|
provider
|
auth_strategy_class
(
provider
)
==
'OmniAuth::Strategies::SAML'
}
end
def
auth_strategy_class
(
provider
)
config
=
Gitlab
::
Auth
::
OAuth
::
Provider
.
config_for
(
provider
)
return
if
config
.
nil?
||
config
[
'args'
].
blank?
config
.
args
[
'strategy_class'
]
end
def
any_form_based_providers_enabled?
def
any_form_based_providers_enabled?
form_based_providers
.
any?
{
|
provider
|
form_enabled_for_sign_in?
(
provider
)
}
form_based_providers
.
any?
{
|
provider
|
form_enabled_for_sign_in?
(
provider
)
}
end
end
...
...
app/views/profiles/accounts/_providers.html.haml
View file @
e7568f5a
...
@@ -6,10 +6,12 @@
...
@@ -6,10 +6,12 @@
-
providers
.
each
do
|
provider
|
-
providers
.
each
do
|
provider
|
-
unlink_allowed
=
unlink_provider_allowed?
(
provider
)
-
unlink_allowed
=
unlink_provider_allowed?
(
provider
)
-
link_allowed
=
link_provider_allowed?
(
provider
)
-
link_allowed
=
link_provider_allowed?
(
provider
)
-
has_icon
=
provider_has_icon?
(
provider
)
-
if
unlink_allowed
||
link_allowed
-
if
unlink_allowed
||
link_allowed
-
if
auth_active?
(
provider
)
-
if
auth_active?
(
provider
)
-
if
unlink_allowed
-
if
unlink_allowed
=
link_to
unlink_profile_account_path
(
provider:
provider
),
method: :delete
,
class:
button_class
do
=
link_to
unlink_profile_account_path
(
provider:
provider
),
method: :delete
,
class:
button_class
do
-
if
has_icon
.social-provider-btn-image.gl-button-icon
=
provider_image_tag
(
provider
)
.social-provider-btn-image.gl-button-icon
=
provider_image_tag
(
provider
)
.gl-button-text
.gl-button-text
=
s_
(
'Profiles|Disconnect %{provider}'
)
%
{
provider:
label_for_provider
(
provider
)
}
=
s_
(
'Profiles|Disconnect %{provider}'
)
%
{
provider:
label_for_provider
(
provider
)
}
...
@@ -19,6 +21,7 @@
...
@@ -19,6 +21,7 @@
=
s_
(
'Profiles|%{provider} Active'
)
%
{
provider:
label_for_provider
(
provider
)
}
=
s_
(
'Profiles|%{provider} Active'
)
%
{
provider:
label_for_provider
(
provider
)
}
-
elsif
link_allowed
-
elsif
link_allowed
=
link_to
omniauth_authorize_path
(
:user
,
provider
),
method: :post
,
class:
button_class
do
=
link_to
omniauth_authorize_path
(
:user
,
provider
),
method: :post
,
class:
button_class
do
-
if
has_icon
.social-provider-btn-image.gl-button-icon
=
provider_image_tag
(
provider
)
.social-provider-btn-image.gl-button-icon
=
provider_image_tag
(
provider
)
.gl-button-text
.gl-button-text
=
s_
(
'Profiles|Connect %{provider}'
)
%
{
provider:
label_for_provider
(
provider
)
}
=
s_
(
'Profiles|Connect %{provider}'
)
%
{
provider:
label_for_provider
(
provider
)
}
...
...
doc/integration/saml.md
View file @
e7568f5a
...
@@ -163,6 +163,74 @@ On the sign in page there should now be a SAML button below the regular sign in
...
@@ -163,6 +163,74 @@ On the sign in page there should now be a SAML button below the regular sign in
Click the icon to begin the authentication process. If everything goes well the user
Click the icon to begin the authentication process. If everything goes well the user
is returned to GitLab and signed in.
is returned to GitLab and signed in.
### Use multiple SAML identity providers
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/14361) in GitLab 14.6.
You can configure GitLab to use multiple SAML identity providers if:
-
Each provider has a unique name set that matches a name set in
`args`
.
-
The providers' names are:
-
Used in OmniAuth configuration for properties based on the provider name. For example,
`allowBypassTwoFactor`
,
`allowSingleSignOn`
, and
`syncProfileFromProvider`
.
-
Used for association to each existing user as an additional identity.
-
The
`assertion_consumer_service_url`
matches the provider name.
-
The
`strategy_class`
is explicitly set because it cannot be inferred from provider name.
Example multiple providers configuration for Omnibus GitLab:
```
ruby
gitlab_rails
[
'omniauth_providers'
]
=
[
{
name:
'saml_1'
,
args:
{
name:
'saml_1'
,
# This is mandatory and must match the provider name
strategy_class:
'OmniAuth::Strategies::SAML'
assertion_consumer_service_url:
'https://gitlab.example.com/users/auth/saml_1/callback'
,
# URL must match the name of the provider
...
# Put here all the required arguments similar to a single provider
},
label:
'Provider 1'
# Differentiate the two buttons and providers in the UI
},
{
name:
'saml_2'
,
args:
{
name:
'saml_2'
,
# This is mandatory and must match the provider name
strategy_class:
'OmniAuth::Strategies::SAML'
assertion_consumer_service_url:
'https://gitlab.example.com/users/auth/saml_2/callback'
,
# URL must match the name of the provider
...
# Put here all the required arguments similar to a single provider
},
label:
'Provider 2'
# Differentiate the two buttons and providers in the UI
}
]
```
Example providers configuration for installations from source:
```
yaml
omniauth
:
providers
:
-
{
name
:
'
saml_1'
,
args
:
{
name
:
'
saml_1'
,
# This is mandatory and must match the provider name
strategy_class
:
'
OmniAuth::Strategies::SAML'
,
assertion_consumer_service_url
:
'
https://gitlab.example.com/users/auth/saml_1/callback'
,
# URL must match the name of the provider
...
# Put here all the required arguments similar to a single provider
},
label
:
'
Provider
1'
# Differentiate the two buttons and providers in the UI
}
-
{
name
:
'
saml_2'
,
args
:
{
name
:
'
saml_2'
,
# This is mandatory and must match the provider name
strategy_class
:
'
OmniAuth::Strategies::SAML'
,
assertion_consumer_service_url
:
'
https://gitlab.example.com/users/auth/saml_2/callback'
,
# URL must match the name of the provider
...
# Put here all the required arguments similar to a single provider
},
label
:
'
Provider
2'
# Differentiate the two buttons and providers in the UI
}
```
### Notes on configuring your identity provider
### Notes on configuring your identity provider
When configuring a SAML app on the IdP, you need at least:
When configuring a SAML app on the IdP, you need at least:
...
...
spec/helpers/auth_helper_spec.rb
View file @
e7568f5a
...
@@ -395,4 +395,170 @@ RSpec.describe AuthHelper do
...
@@ -395,4 +395,170 @@ RSpec.describe AuthHelper do
end
end
end
end
end
end
describe
'#auth_strategy_class'
do
subject
(
:auth_strategy_class
)
{
helper
.
auth_strategy_class
(
name
)
}
context
'when configuration specifies no provider'
do
let
(
:name
)
{
'does_not_exist'
}
before
do
allow
(
Gitlab
.
config
.
omniauth
).
to
receive
(
:providers
).
and_return
([])
end
it
'returns false'
do
expect
(
auth_strategy_class
).
to
be_falsey
end
end
context
'when configuration specifies a provider with args but without strategy_class'
do
let
(
:name
)
{
'google_oauth2'
}
let
(
:provider
)
do
Struct
.
new
(
:name
,
:args
).
new
(
name
,
'app_id'
=>
'YOUR_APP_ID'
)
end
before
do
allow
(
Gitlab
.
config
.
omniauth
).
to
receive
(
:providers
).
and_return
([
provider
])
end
it
'returns false'
do
expect
(
auth_strategy_class
).
to
be_falsey
end
end
context
'when configuration specifies a provider with args and strategy_class'
do
let
(
:name
)
{
'provider1'
}
let
(
:strategy
)
{
'OmniAuth::Strategies::LDAP'
}
let
(
:provider
)
do
Struct
.
new
(
:name
,
:args
).
new
(
name
,
'strategy_class'
=>
strategy
)
end
before
do
allow
(
Gitlab
.
config
.
omniauth
).
to
receive
(
:providers
).
and_return
([
provider
])
end
it
'returns the class'
do
expect
(
auth_strategy_class
).
to
eq
(
strategy
)
end
end
context
'when configuration specifies another provider with args and another strategy_class'
do
let
(
:name
)
{
'provider1'
}
let
(
:strategy
)
{
'OmniAuth::Strategies::LDAP'
}
let
(
:provider
)
do
Struct
.
new
(
:name
,
:args
).
new
(
'another_name'
,
'strategy_class'
=>
strategy
)
end
before
do
allow
(
Gitlab
.
config
.
omniauth
).
to
receive
(
:providers
).
and_return
([
provider
])
end
it
'returns false'
do
expect
(
auth_strategy_class
).
to
be_falsey
end
end
end
describe
'#saml_providers'
do
subject
(
:saml_providers
)
{
helper
.
saml_providers
}
let
(
:saml_strategy
)
{
'OmniAuth::Strategies::SAML'
}
let
(
:saml_provider_1_name
)
{
'saml_provider_1'
}
let
(
:saml_provider_1
)
do
Struct
.
new
(
:name
,
:args
).
new
(
saml_provider_1_name
,
'strategy_class'
=>
saml_strategy
)
end
let
(
:saml_provider_2_name
)
{
'saml_provider_2'
}
let
(
:saml_provider_2
)
do
Struct
.
new
(
:name
,
:args
).
new
(
saml_provider_2_name
,
'strategy_class'
=>
saml_strategy
)
end
let
(
:ldap_provider_name
)
{
'ldap_provider'
}
let
(
:ldap_strategy
)
{
'OmniAuth::Strategies::LDAP'
}
let
(
:ldap_provider
)
do
Struct
.
new
(
:name
,
:args
).
new
(
ldap_provider_name
,
'strategy_class'
=>
ldap_strategy
)
end
let
(
:google_oauth2_provider_name
)
{
'google_oauth2'
}
let
(
:google_oauth2_provider
)
do
Struct
.
new
(
:name
,
:args
).
new
(
google_oauth2_provider_name
,
'app_id'
=>
'YOUR_APP_ID'
)
end
context
'when configuration specifies no provider'
do
before
do
allow
(
Devise
).
to
receive
(
:omniauth_providers
).
and_return
([])
allow
(
Gitlab
.
config
.
omniauth
).
to
receive
(
:providers
).
and_return
([])
end
it
'returns an empty list'
do
expect
(
saml_providers
).
to
be_empty
end
end
context
'when configuration specifies a provider with a SAML strategy_class'
do
before
do
allow
(
Devise
).
to
receive
(
:omniauth_providers
).
and_return
([
saml_provider_1_name
])
allow
(
Gitlab
.
config
.
omniauth
).
to
receive
(
:providers
).
and_return
([
saml_provider_1
])
end
it
'returns the provider'
do
expect
(
saml_providers
).
to
match_array
([
saml_provider_1_name
])
end
end
context
'when configuration specifies two providers with a SAML strategy_class'
do
before
do
allow
(
Devise
).
to
receive
(
:omniauth_providers
).
and_return
([
saml_provider_1_name
,
saml_provider_2_name
])
allow
(
Gitlab
.
config
.
omniauth
).
to
receive
(
:providers
).
and_return
([
saml_provider_1
,
saml_provider_2
])
end
it
'returns the provider'
do
expect
(
saml_providers
).
to
match_array
([
saml_provider_1_name
,
saml_provider_2_name
])
end
end
context
'when configuration specifies a provider with a non-SAML strategy_class'
do
before
do
allow
(
Devise
).
to
receive
(
:omniauth_providers
).
and_return
([
ldap_provider_name
])
allow
(
Gitlab
.
config
.
omniauth
).
to
receive
(
:providers
).
and_return
([
ldap_provider
])
end
it
'returns an empty list'
do
expect
(
saml_providers
).
to
be_empty
end
end
context
'when configuration specifies four providers but only two with SAML strategy_class'
do
before
do
allow
(
Devise
).
to
receive
(
:omniauth_providers
).
and_return
([
saml_provider_1_name
,
ldap_provider_name
,
saml_provider_2_name
,
google_oauth2_provider_name
])
allow
(
Gitlab
.
config
.
omniauth
).
to
receive
(
:providers
).
and_return
([
saml_provider_1
,
ldap_provider
,
saml_provider_2
,
google_oauth2_provider
])
end
it
'returns the provider'
do
expect
(
saml_providers
).
to
match_array
([
saml_provider_1_name
,
saml_provider_2_name
])
end
end
end
end
end
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment