Skip to content
Snippets Groups Projects
Unverified Commit 755ce3e9 authored by Drew Blessing's avatar Drew Blessing Committed by Drew Blessing
Browse files

Allow SAML response to set certain user attributes on creation

Allow SAML to set user attributes via additional attribute
statements in the SAML response. These allowed attributes are
currently limited to `can_create_group` and `projects_limit`.
parent 1b962786
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -220,6 +220,46 @@ On subsequent visits, you should be able to go [sign in to GitLab.com with SAML]
1. From the list of apps, click on the "GitLab.com" app (The name is set by the administrator of the identity provider).
1. You are then signed in to GitLab.com and redirected to the group.
 
### Configure user settings from SAML response
GitLab allows setting certain user attributes based on values from the SAML response.
This affects users created on first sign-in via Group SAML. Existing users'
attributes are not affected regardless of the values sent in the SAML response.
#### Supported user attributes
- `can_create_group` - 'true' or 'false' to indicate whether the user can create
new groups. Default is `true`.
- `projects_limit` - The total number of personal projects a user can create.
A value of `0` means the user cannot create new projects in their personal
namespace. Default is `10000`.
#### Example SAML response
You can find SAML responses in the developer tools or console of your browser,
in base64-encoded format. Use the base64 decoding tool of your choice to
convert the information to XML. An example SAML response is shown here.
```xml
<saml2:AttributeStatement>
<saml2:Attribute Name="email" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">user.email</saml2:AttributeValue>
</saml2:Attribute>
<saml2:Attribute Name="first_name" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
<saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">user.firstName</saml2:AttributeValue>
</saml2:Attribute>
<saml2:Attribute Name="last_name" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
<saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">user.lastName</saml2:AttributeValue>
</saml2:Attribute>
<saml2:Attribute Name="can_create_group" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
<saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">true</saml2:AttributeValue>
</saml2:Attribute>
<saml2:Attribute Name="projects_limit" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
<saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">10</saml2:AttributeValue>
</saml2:Attribute>
</saml2:AttributeStatement>
```
### Role
 
Starting from [GitLab 13.3](https://gitlab.com/gitlab-org/gitlab/-/issues/214523), group owners can set a 'Default membership role' other than 'Guest'. To do so, [configure the SAML SSO for the group](#configuring-gitlab). That role becomes the starting access level of all users added to the group.
Loading
Loading
---
title: Allow SAML response to set certain user attributes on creation
merge_request: 49394
author:
type: added
Loading
Loading
@@ -4,9 +4,17 @@ module Gitlab
module Auth
module GroupSaml
class AuthHash < Gitlab::Auth::Saml::AuthHash
ALLOWED_USER_ATTRIBUTES = %w(can_create_group projects_limit).freeze
def groups
Array.wrap(get_raw('groups') || get_raw('Groups'))
end
ALLOWED_USER_ATTRIBUTES.each do |attribute|
define_method(attribute) do
Array(get_raw(attribute)).first
end
end
end
end
end
Loading
Loading
Loading
Loading
@@ -51,6 +51,14 @@ def identity
def build_new_user(skip_confirmation: false)
super.tap do |user|
user.provisioned_by_group_id = saml_provider.group_id
# rubocop:disable GitlabSecurity/PublicSend
AuthHash::ALLOWED_USER_ATTRIBUTES.each do |attribute|
next unless value = auth_hash.public_send(attribute)
user.public_send("#{attribute}=", value)
end
# rubocop:enable GitlabSecurity/PublicSend
end
end
 
Loading
Loading
Loading
Loading
@@ -35,4 +35,42 @@
end
end
end
describe 'allowed user attributes methods' do
context 'when the attributes are presented as an array' do
let(:raw_info_attr) { { 'can_create_group' => %w(true), 'projects_limit' => %w(20) } }
it 'returns the proper can_create_groups value' do
expect(saml_auth_hash.can_create_group).to eq "true"
end
it 'returns the proper projects_limit value' do
expect(saml_auth_hash.projects_limit).to eq "20"
end
end
context 'when the attributes are presented as a string' do
let(:raw_info_attr) { { 'can_create_group' => 'false', 'projects_limit' => '20' } }
it 'returns the proper can_create_groups value' do
expect(saml_auth_hash.can_create_group).to eq "false"
end
it 'returns the proper projects_limit value' do
expect(saml_auth_hash.projects_limit).to eq "20"
end
end
context 'when the attributes are not present in the SAML response' do
let(:raw_info_attr) { {} }
it 'returns nil for can_create_group' do
expect(saml_auth_hash.can_create_group).to eq nil
end
it 'returns nil for can_create_groups' do
expect(saml_auth_hash.projects_limit).to eq nil
end
end
end
end
Loading
Loading
@@ -7,7 +7,7 @@
let(:uid) { 1234 }
let(:saml_provider) { create(:saml_provider) }
let(:group) { saml_provider.group }
let(:auth_hash) { OmniAuth::AuthHash.new(uid: uid, provider: 'group_saml', info: info_hash) }
let(:auth_hash) { OmniAuth::AuthHash.new(uid: uid, provider: 'group_saml', info: info_hash, extra: { raw_info: OneLogin::RubySaml::Attributes.new }) }
let(:info_hash) do
{
name: generate(:name),
Loading
Loading
@@ -82,6 +82,23 @@ def create_existing_identity
it 'creates the user SAML identity' do
expect { find_and_update }.to change { Identity.count }.by(1)
end
context 'when user attributes are present' do
before do
auth_hash[:extra][:raw_info] =
OneLogin::RubySaml::Attributes.new(
'can_create_group' => %w(true), 'projects_limit' => %w(20)
)
end
it 'creates the user with correct can_create_group attribute' do
expect(find_and_update.can_create_group).to eq(true)
end
it 'creates the user with correct projects_limit attribute' do
expect(find_and_update.projects_limit).to eq(20)
end
end
end
 
context 'when a conflicting user already exists' do
Loading
Loading
Loading
Loading
@@ -138,13 +138,15 @@ def configure_mock_auth(provider, uid, email, response_object: nil, additional_i
secret: 'mock_secret'
},
extra: {
raw_info: {
info: {
name: 'mockuser',
email: email,
image: 'mock_user_thumbnail_url'
raw_info: OneLogin::RubySaml::Attributes.new(
{
info: {
name: 'mockuser',
email: email,
image: 'mock_user_thumbnail_url'
}
}
},
),
response_object: response_object
}
}).merge(additional_info) { |_, old_hash, new_hash| old_hash.merge(new_hash) }
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment