Commit 6412a3e0 authored by Yorick Peterse's avatar Yorick Peterse

Merge branch 'security-kubernetes-google-login-csrf' into 'master'

Validate session key when authorizing with GCP to create a cluster

Closes #2805

See merge request gitlab/gitlabhq!2902
parents 9e4a9cda fc8c1a77
...@@ -2,6 +2,10 @@ ...@@ -2,6 +2,10 @@
module GoogleApi module GoogleApi
class AuthorizationsController < ApplicationController class AuthorizationsController < ApplicationController
include Gitlab::Utils::StrongMemoize
before_action :validate_session_key!
def callback def callback
token, expires_at = GoogleApi::CloudPlatform::Client token, expires_at = GoogleApi::CloudPlatform::Client
.new(nil, callback_google_api_auth_url) .new(nil, callback_google_api_auth_url)
...@@ -11,21 +15,27 @@ module GoogleApi ...@@ -11,21 +15,27 @@ module GoogleApi
session[GoogleApi::CloudPlatform::Client.session_key_for_expires_at] = session[GoogleApi::CloudPlatform::Client.session_key_for_expires_at] =
expires_at.to_s expires_at.to_s
state_redirect_uri = redirect_uri_from_session_key(params[:state]) redirect_to redirect_uri_from_session
end
private
def validate_session_key!
access_denied! unless redirect_uri_from_session.present?
end
if state_redirect_uri def redirect_uri_from_session
redirect_to state_redirect_uri strong_memoize(:redirect_uri_from_session) do
if params[:state].present?
session[session_key_for_redirect_uri(params[:state])]
else else
redirect_to root_path nil
end
end end
end end
private def session_key_for_redirect_uri(state)
GoogleApi::CloudPlatform::Client.session_key_for_redirect_uri(state)
def redirect_uri_from_session_key(state)
key = GoogleApi::CloudPlatform::Client
.session_key_for_redirect_uri(params[:state])
session[key] if key
end end
end end
end end
---
title: Validate session key when authorizing with GCP to create a cluster
merge_request:
author:
type: security
...@@ -6,7 +6,7 @@ describe GoogleApi::AuthorizationsController do ...@@ -6,7 +6,7 @@ describe GoogleApi::AuthorizationsController do
let(:token) { 'token' } let(:token) { 'token' }
let(:expires_at) { 1.hour.since.strftime('%s') } let(:expires_at) { 1.hour.since.strftime('%s') }
subject { get :callback, params: { code: 'xxx', state: @state } } subject { get :callback, params: { code: 'xxx', state: state } }
before do before do
sign_in(user) sign_in(user)
...@@ -15,24 +15,33 @@ describe GoogleApi::AuthorizationsController do ...@@ -15,24 +15,33 @@ describe GoogleApi::AuthorizationsController do
.to receive(:get_token).and_return([token, expires_at]) .to receive(:get_token).and_return([token, expires_at])
end end
it 'sets token and expires_at in session' do shared_examples_for 'access denied' do
it 'returns a 404' do
subject subject
expect(session[GoogleApi::CloudPlatform::Client.session_key_for_token]) expect(session[GoogleApi::CloudPlatform::Client.session_key_for_token]).to be_nil
.to eq(token) expect(response).to have_http_status(:not_found)
expect(session[GoogleApi::CloudPlatform::Client.session_key_for_expires_at]) end
.to eq(expires_at)
end end
context 'when redirect uri key is stored in state' do context 'session key is present' do
set(:project) { create(:project) } let(:session_key) { 'session-key' }
let(:redirect_uri) { project_clusters_url(project).to_s } let(:redirect_uri) { 'example.com' }
before do before do
@state = GoogleApi::CloudPlatform::Client session[GoogleApi::CloudPlatform::Client.session_key_for_redirect_uri(session_key)] = redirect_uri
.new_session_key_for_redirect_uri do |key|
session[key] = redirect_uri
end end
context 'session key matches state param' do
let(:state) { session_key }
it 'sets token and expires_at in session' do
subject
expect(session[GoogleApi::CloudPlatform::Client.session_key_for_token])
.to eq(token)
expect(session[GoogleApi::CloudPlatform::Client.session_key_for_expires_at])
.to eq(expires_at)
end end
it 'redirects to the URL stored in state param' do it 'redirects to the URL stored in state param' do
...@@ -40,10 +49,23 @@ describe GoogleApi::AuthorizationsController do ...@@ -40,10 +49,23 @@ describe GoogleApi::AuthorizationsController do
end end
end end
context 'when redirection url is not stored in state' do context 'session key does not match state param' do
it 'redirects to root_path' do let(:state) { 'bad-key' }
expect(subject).to redirect_to(root_path)
it_behaves_like 'access denied'
end end
context 'state param is blank' do
let(:state) { '' }
it_behaves_like 'access denied'
end
end
context 'state param is present, but session key is blank' do
let(:state) { 'session-key' }
it_behaves_like 'access denied'
end 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