Commit e26d96d3 authored by Clement Ho's avatar Clement Ho

Merge branch 'jej/group-saml-sso-button-link-description' into 'master'

Group SAML SSO page warns when linking account

Closes #8260

See merge request gitlab-org/gitlab-ee!8295
parents 646de768 b0e640ce
...@@ -69,6 +69,7 @@ class UrlValidator < ActiveModel::EachValidator ...@@ -69,6 +69,7 @@ class UrlValidator < ActiveModel::EachValidator
ports: [], ports: [],
allow_localhost: true, allow_localhost: true,
allow_local_network: true, allow_local_network: true,
ascii_only: false,
enforce_user: false enforce_user: false
} }
end end
......
...@@ -15,6 +15,8 @@ class Groups::SsoController < Groups::ApplicationController ...@@ -15,6 +15,8 @@ class Groups::SsoController < Groups::ApplicationController
def saml def saml
@group_path = params[:group_id] @group_path = params[:group_id]
@group_name = @unauthenticated_group.full_name @group_name = @unauthenticated_group.full_name
@group_saml_identity = linked_identity
@idp_url = @unauthenticated_group.saml_provider.sso_url
end end
def unlink def unlink
......
...@@ -5,7 +5,7 @@ class SamlProvider < ActiveRecord::Base ...@@ -5,7 +5,7 @@ class SamlProvider < ActiveRecord::Base
has_many :identities has_many :identities
validates :group, presence: true, top_level_group: true validates :group, presence: true, top_level_group: true
validates :sso_url, presence: true, url: { protocols: %w(https) } validates :sso_url, presence: true, url: { protocols: %w(https), ascii_only: true }
validates :certificate_fingerprint, presence: true, certificate_fingerprint: true validates :certificate_fingerprint, presence: true, certificate_fingerprint: true
after_initialize :set_defaults, if: :new_record? after_initialize :set_defaults, if: :new_record?
......
...@@ -3,8 +3,19 @@ ...@@ -3,8 +3,19 @@
= render 'devise/shared/tab_single', tab_title: _('SAML SSO') = render 'devise/shared/tab_single', tab_title: _('SAML SSO')
.login-box .login-box
.login-body .login-body
%h4= _("Sign in to %{group_name}") % { group_name: @group_name } - if @group_saml_identity
%h4= _('Sign in to "%{group_name}"') % { group_name: @group_name }
- else
%h4= _('Allow "%{group_name}" to sign you in') % { group_name: @group_name }
%p= _("This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page.") % { group_name: @group_name } %p= _('The "%{group_path}" group allows you to sign in with your Single Sign-On Account') % { group_path: @group_path }
- if @group_saml_identity
%p= _("This will redirect you to an external sign in page.")
= saml_link _('Sign in with Single Sign-On'), @group_path, html_class: 'btn btn-success btn-block qa-saml-sso-signin-button' = saml_link _('Sign in with Single Sign-On'), @group_path, html_class: 'btn btn-success btn-block qa-saml-sso-signin-button'
- else
.card.card-body.bs-callout-warning
= _("Only proceed if you trust %{idp_url} to control your GitLab account sign in.") % { idp_url: @idp_url }
= saml_link _('Authorize'), @group_path, html_class: 'btn btn-success btn-block qa-saml-sso-signin-button'
---
title: Group SAML SSO page warns when linking account
merge_request: 8295
author:
type: changed
...@@ -154,16 +154,38 @@ describe 'SAML provider settings' do ...@@ -154,16 +154,38 @@ describe 'SAML provider settings' do
context 'when signed in' do context 'when signed in' do
before do before do
sign_in(user) sign_in(user)
end
it 'shows warning that linking accounts authorizes control over sign in' do
visit sso_group_saml_providers_path(group) visit sso_group_saml_providers_path(group)
expect(page).to have_content(/Allow .* to sign you in/)
expect(page).to have_content(saml_provider.sso_url)
expect(page).to have_content('Authorize')
end
it 'Authorize/link button redirects to auth flow' do
visit sso_group_saml_providers_path(group)
click_link 'Authorize'
expect(current_path).to eq callback_path
end
context 'with linked account' do
before do
create(:group_saml_identity, saml_provider: saml_provider, user: user)
end end
it 'Sign in button redirects to auth flow and back to group' do it 'Sign in button redirects to auth flow' do
visit sso_group_saml_providers_path(group)
click_link 'Sign in with Single Sign-On' click_link 'Sign in with Single Sign-On'
expect(current_path).to eq callback_path expect(current_path).to eq callback_path
end end
end end
end
context 'for a private group' do context 'for a private group' do
let(:group) { create(:group, :private) } let(:group) { create(:group, :private) }
...@@ -187,7 +209,7 @@ describe 'SAML provider settings' do ...@@ -187,7 +209,7 @@ describe 'SAML provider settings' do
expect(current_path).to eq sso_group_saml_providers_path(group) expect(current_path).to eq sso_group_saml_providers_path(group)
within '.login-box' do within '.login-box' do
expect(page).to have_link 'Sign in with Single Sign-On' expect(page).to have_link 'Authorize'
end end
end end
......
...@@ -16,6 +16,15 @@ describe SamlProvider do ...@@ -16,6 +16,15 @@ describe SamlProvider do
expect(subject).not_to allow_value('http://example.com').for(:sso_url) expect(subject).not_to allow_value('http://example.com').for(:sso_url)
end end
it 'prevents homoglyph phishing attacks by only allowing ascii URLs' do
expect(subject).to allow_value('https://gitlab.com/adfs/ls').for(:sso_url)
expect(subject).not_to allow_value('https://𝕘itⅼaƄ.ᴄοm/adfs/ls').for(:sso_url)
end
it 'allows unicode domain names when encoded as ascii punycode' do
expect(subject).to allow_value('https://xn--gitl-ocb944a.xn--m-rmb025q/adfs/ls').for(:sso_url)
end
it 'expects certificate_fingerprint to be in an accepted format' do it 'expects certificate_fingerprint to be in an accepted format' do
expect(subject).to allow_value('000030EDC285E01D6B5EA33010A79ADD142F5004').for(:certificate_fingerprint) expect(subject).to allow_value('000030EDC285E01D6B5EA33010A79ADD142F5004').for(:certificate_fingerprint)
expect(subject).to allow_value('00:00:30:ED:C2:85:E0:1D:6B:5E:A3:30:10:A7:9A:DD:14:2F:50:04').for(:certificate_fingerprint) expect(subject).to allow_value('00:00:30:ED:C2:85:E0:1D:6B:5E:A3:30:10:A7:9A:DD:14:2F:50:04').for(:certificate_fingerprint)
......
...@@ -8,7 +8,7 @@ module Gitlab ...@@ -8,7 +8,7 @@ module Gitlab
BlockedUrlError = Class.new(StandardError) BlockedUrlError = Class.new(StandardError)
class << self class << self
def validate!(url, allow_localhost: false, allow_local_network: true, enforce_user: false, ports: [], protocols: []) def validate!(url, ports: [], protocols: [], allow_localhost: false, allow_local_network: true, ascii_only: false, enforce_user: false)
return true if url.nil? return true if url.nil?
# Param url can be a string, URI or Addressable::URI # Param url can be a string, URI or Addressable::URI
...@@ -22,6 +22,7 @@ module Gitlab ...@@ -22,6 +22,7 @@ module Gitlab
validate_port!(port, ports) if ports.any? validate_port!(port, ports) if ports.any?
validate_user!(uri.user) if enforce_user validate_user!(uri.user) if enforce_user
validate_hostname!(uri.hostname) validate_hostname!(uri.hostname)
validate_unicode_restriction!(uri) if ascii_only
begin begin
addrs_info = Addrinfo.getaddrinfo(uri.hostname, port, nil, :STREAM).map do |addr| addrs_info = Addrinfo.getaddrinfo(uri.hostname, port, nil, :STREAM).map do |addr|
...@@ -91,6 +92,12 @@ module Gitlab ...@@ -91,6 +92,12 @@ module Gitlab
raise BlockedUrlError, "Hostname or IP address invalid" raise BlockedUrlError, "Hostname or IP address invalid"
end end
def validate_unicode_restriction!(uri)
return if uri.to_s.ascii_only?
raise BlockedUrlError, "URI must be ascii only #{uri.to_s.dump}"
end
def validate_localhost!(addrs_info) def validate_localhost!(addrs_info)
local_ips = ["::", "0.0.0.0"] local_ips = ["::", "0.0.0.0"]
local_ips.concat(Socket.ip_address_list.map(&:ip_address)) local_ips.concat(Socket.ip_address_list.map(&:ip_address))
......
...@@ -578,6 +578,9 @@ msgstr "" ...@@ -578,6 +578,9 @@ msgstr ""
msgid "All users" msgid "All users"
msgstr "" msgstr ""
msgid "Allow \"%{group_name}\" to sign you in"
msgstr ""
msgid "Allow commits from members who can merge to the target branch." msgid "Allow commits from members who can merge to the target branch."
msgstr "" msgstr ""
...@@ -6003,6 +6006,9 @@ msgstr "" ...@@ -6003,6 +6006,9 @@ msgstr ""
msgid "Only mirror protected branches" msgid "Only mirror protected branches"
msgstr "" msgstr ""
msgid "Only proceed if you trust %{idp_url} to control your GitLab account sign in."
msgstr ""
msgid "Only project members can comment." msgid "Only project members can comment."
msgstr "" msgstr ""
...@@ -7875,7 +7881,7 @@ msgstr "" ...@@ -7875,7 +7881,7 @@ msgstr ""
msgid "Sign in / Register" msgid "Sign in / Register"
msgstr "" msgstr ""
msgid "Sign in to %{group_name}" msgid "Sign in to \"%{group_name}\""
msgstr "" msgstr ""
msgid "Sign in via 2FA code" msgid "Sign in via 2FA code"
...@@ -8454,6 +8460,9 @@ msgstr "" ...@@ -8454,6 +8460,9 @@ msgstr ""
msgid "Thanks! Don't show me this again" msgid "Thanks! Don't show me this again"
msgstr "" msgstr ""
msgid "The \"%{group_path}\" group allows you to sign in with your Single Sign-On Account"
msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project." msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr "" msgstr ""
...@@ -8673,9 +8682,6 @@ msgstr "" ...@@ -8673,9 +8682,6 @@ msgstr ""
msgid "This group" msgid "This group"
msgstr "" msgstr ""
msgid "This group allows you to sign in with your %{group_name} Single Sign-On account. This will redirect you to an external sign in page."
msgstr ""
msgid "This group does not provide any group Runners yet." msgid "This group does not provide any group Runners yet."
msgstr "" msgstr ""
...@@ -8811,6 +8817,9 @@ msgstr "" ...@@ -8811,6 +8817,9 @@ msgstr ""
msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user." msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches. Upon creation or when reassigning you can only assign yourself to be the mirror user."
msgstr "" msgstr ""
msgid "This will redirect you to an external sign in page."
msgstr ""
msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here." msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
msgstr "" msgstr ""
......
...@@ -249,6 +249,27 @@ describe Gitlab::UrlBlocker do ...@@ -249,6 +249,27 @@ describe Gitlab::UrlBlocker do
end end
end end
end end
context 'when ascii_only is true' do
it 'returns true for unicode domain' do
expect(described_class.blocked_url?('https://𝕘itⅼαƄ.com/foo/foo.bar', ascii_only: true)).to be true
end
it 'returns true for unicode tld' do
expect(described_class.blocked_url?('https://gitlab.ᴄοm/foo/foo.bar', ascii_only: true)).to be true
end
it 'returns true for unicode path' do
expect(described_class.blocked_url?('https://gitlab.com/𝒇οο/𝒇οο.Ƅαꮁ', ascii_only: true)).to be true
end
it 'returns true for IDNA deviations' do
expect(described_class.blocked_url?('https://mißile.com/foo/foo.bar', ascii_only: true)).to be true
expect(described_class.blocked_url?('https://miςςile.com/foo/foo.bar', ascii_only: true)).to be true
expect(described_class.blocked_url?('https://git‍lab.com/foo/foo.bar', ascii_only: true)).to be true
expect(described_class.blocked_url?('https://git‌lab.com/foo/foo.bar', ascii_only: true)).to be true
end
end
end end
describe '#validate_hostname!' do describe '#validate_hostname!' do
......
...@@ -143,4 +143,33 @@ describe UrlValidator do ...@@ -143,4 +143,33 @@ describe UrlValidator do
end end
end end
end end
context 'when ascii_only is' do
let(:url) { 'https://𝕘itⅼαƄ.com/foo/foo.bar'}
let(:validator) { described_class.new(attributes: [:link_url], ascii_only: ascii_only) }
context 'true' do
let(:ascii_only) { true }
it 'prevents unicode characters' do
badge.link_url = url
subject
expect(badge.errors.empty?).to be false
end
end
context 'false (default)' do
let(:ascii_only) { false }
it 'does not prevent unicode characters' do
badge.link_url = url
subject
expect(badge.errors.empty?).to be true
end
end
end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment