Commit 7637c145 authored by Adam Hegyi's avatar Adam Hegyi Committed by Thong Kuah

Redirect to the exact group after Group SAML login

This change initiates SSO login when an unauthanticated user tries
to access a group, subgroup or a project when the group_saml feature is
enabled.
parent bf9225b9
...@@ -20,7 +20,7 @@ module EE ...@@ -20,7 +20,7 @@ module EE
end end
def sso_redirect_url def sso_redirect_url
sso_group_saml_providers_url(root_group, token: root_group.saml_discovery_token) sso_group_saml_providers_url(root_group, url_params)
end end
module ControllerActions module ControllerActions
...@@ -59,6 +59,13 @@ module EE ...@@ -59,6 +59,13 @@ module EE
def root_group def root_group
@root_group ||= group.root_ancestor @root_group ||= group.root_ancestor
end end
def url_params
{
token: root_group.saml_discovery_token,
redirect: "/#{routable.full_path}"
}
end
end end
end end
end end
# frozen_string_literal: true # frozen_string_literal: true
class Groups::SsoController < Groups::ApplicationController class Groups::SsoController < Groups::ApplicationController
include InternalRedirect
skip_before_action :group skip_before_action :group
before_action :authenticate_user!, only: [:unlink] before_action :authenticate_user!, only: [:unlink]
...@@ -13,7 +14,8 @@ class Groups::SsoController < Groups::ApplicationController ...@@ -13,7 +14,8 @@ class Groups::SsoController < Groups::ApplicationController
layout 'devise' layout 'devise'
def saml def saml
@group_path = params[:group_id] @redirect_path = safe_redirect_path(params[:redirect]) || group_path(unauthenticated_group)
@group_path = unauthenticated_group.path
@group_name = unauthenticated_group.full_name @group_name = unauthenticated_group.full_name
@group_saml_identity = linked_identity @group_saml_identity = linked_identity
@idp_url = unauthenticated_group.saml_provider.sso_url @idp_url = unauthenticated_group.saml_provider.sso_url
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
- if @group_saml_identity || !user_signed_in? - if @group_saml_identity || !user_signed_in?
%p= _("This will redirect you to an external sign in page.") %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', redirect: @redirect_path
- else - else
.card.card-body.bs-callout-warning .card.card-body.bs-callout-warning
= _("Only proceed if you trust %{idp_url} to control your GitLab account sign in.") % { idp_url: @idp_url } = _("Only proceed if you trust %{idp_url} to control your GitLab account sign in.") % { idp_url: @idp_url }
......
---
title: Enforce SSO on subgroups and projects
merge_request: 14364
author:
type: fixed
...@@ -23,10 +23,11 @@ module Gitlab ...@@ -23,10 +23,11 @@ module Gitlab
end end
def self.group_access_restricted?(group) def self.group_access_restricted?(group)
return false unless ::Feature.enabled?(:enforced_sso_requires_session, group)
return false unless group return false unless group
return false unless group.root_ancestor
return false unless ::Feature.enabled?(:enforced_sso_requires_session, group.root_ancestor)
saml_provider = group&.root_ancestor&.saml_provider saml_provider = group.root_ancestor.saml_provider
return false unless saml_provider return false unless saml_provider
......
...@@ -81,7 +81,9 @@ describe EE::RoutableActions::SsoEnforcementRedirect do ...@@ -81,7 +81,9 @@ describe EE::RoutableActions::SsoEnforcementRedirect do
describe '#sso_redirect_url' do describe '#sso_redirect_url' do
shared_examples 'a routable SSO url' do shared_examples 'a routable SSO url' do
it 'returns the SSO url for the root group' do it 'returns the SSO url for the root group' do
expect(subject.sso_redirect_url).to match(/groups\/#{root_group.to_param}\/-\/saml\/sso\?token=/) redirect_url = CGI.escape("/#{subject.routable.full_path}")
expect(subject.sso_redirect_url).to match(/groups\/#{root_group.to_param}\/-\/saml\/sso\?redirect=#{redirect_url}&token=/)
end end
end end
......
...@@ -42,7 +42,7 @@ describe RoutableActions do ...@@ -42,7 +42,7 @@ describe RoutableActions do
get :show, params: request_params(routable) get :show, params: request_params(routable)
expect(response).to have_gitlab_http_status(302) expect(response).to have_gitlab_http_status(302)
expect(response.location).to match(/groups\/.*\/-\/saml\/sso\?token=/) expect(response.location).to match(/groups\/.*\/-\/saml\/sso\?redirect=.+&token=/)
end end
it 'does not redirect on POST requests' do it 'does not redirect on POST requests' do
......
...@@ -87,7 +87,7 @@ describe GroupsController do ...@@ -87,7 +87,7 @@ describe GroupsController do
get :show, params: { id: group } get :show, params: { id: group }
expect(response).to have_gitlab_http_status(302) expect(response).to have_gitlab_http_status(302)
expect(response.location).to match(/groups\/#{group.to_param}\/-\/saml\/sso\?token=/) expect(response.location).to match(/groups\/#{group.to_param}\/-\/saml\/sso\?redirect=.+&token=/)
end end
end end
......
...@@ -19,6 +19,13 @@ describe Groups::SsoController do ...@@ -19,6 +19,13 @@ describe Groups::SsoController do
expect(response).to have_gitlab_http_status(200) expect(response).to have_gitlab_http_status(200)
end end
it 'malicious redirect parameter falls back to group_path' do
get :saml, params: { group_id: group, redirect: '///malicious-url' }
expect(response).to have_gitlab_http_status(200)
expect(assigns[:redirect_path]).to eq(group_path(group))
end
it 'passes group name to the view' do it 'passes group name to the view' do
get :saml, params: { group_id: group } get :saml, params: { group_id: group }
......
...@@ -20,7 +20,7 @@ describe 'SAML access enforcement' do ...@@ -20,7 +20,7 @@ describe 'SAML access enforcement' do
visit group_path(group) visit group_path(group)
expect(page).to have_content("SAML SSO Sign in to \"#{group.name}\"") expect(page).to have_content("SAML SSO Sign in to \"#{group.name}\"")
expect(current_url).to match(/groups\/#{group.to_param}\/-\/saml\/sso\?token=/) expect(current_url).to match(/groups\/#{group.to_param}\/-\/saml\/sso\?redirect=.+&token=/)
end end
end end
......
...@@ -79,4 +79,45 @@ describe Gitlab::Auth::GroupSaml::SsoEnforcer do ...@@ -79,4 +79,45 @@ describe Gitlab::Auth::GroupSaml::SsoEnforcer do
expect(subject).not_to be_access_restricted expect(subject).not_to be_access_restricted
end end
end end
describe '.group_access_restricted?' do
let(:root_group) { create(:group, saml_provider: create(:saml_provider, enabled: true, enforced_sso: true)) }
context 'is restricted' do
before do
stub_feature_flags(enforced_sso_requires_session: { enabled: true, thing: root_group })
end
it 'for a group' do
expect(described_class).to be_group_access_restricted(root_group)
end
it 'for a subgroup' do
sub_group = create(:group, parent: root_group)
expect(described_class).to be_group_access_restricted(sub_group)
end
it 'for a project' do
project = create(:project, group: root_group)
expect(described_class).to be_group_access_restricted(project)
end
end
context 'is not restricted' do
it 'for the group without configured saml_provider' do
group = create(:group)
stub_feature_flags(enforced_sso_requires_session: { enabled: true, thing: group })
expect(described_class).not_to be_group_access_restricted(group)
end
it 'for the group without the feature flag' do
stub_feature_flags(enforced_sso_requires_session: { enabled: false, thing: root_group })
expect(described_class).not_to be_group_access_restricted(root_group)
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