Commit 16913601 authored by Alex Kalderimis's avatar Alex Kalderimis

Support GraphQL authentication with project tokens

This enables the use of project and group access tokens for sessionless
API authentication. The primary effect of this is to allow the use of
project and group tokens in the GraphQL API.

The documentation is updated to explain a little how project tokens are
implemented, which is useful for future maintenance.

Changelog: changed
parent 55bc8d11
...@@ -26,6 +26,9 @@ module SessionlessAuthentication ...@@ -26,6 +26,9 @@ module SessionlessAuthentication
# for every request. If you want the token to work as a # for every request. If you want the token to work as a
# sign in token, you can simply remove store: false. # sign in token, you can simply remove store: false.
sign_in(user, store: false, message: :sessionless_sign_in) sign_in(user, store: false, message: :sessionless_sign_in)
elsif request_authenticator.can_sign_in_bot?(user)
# we suppress callbacks to avoid redirecting the bot
sign_in(user, store: false, message: :sessionless_sign_in, run_callbacks: false)
end end
end end
......
...@@ -42,3 +42,5 @@ Other examples of internal users: ...@@ -42,3 +42,5 @@ Other examples of internal users:
- [Ghost User](../user/profile/account/delete_account.md#associated-records) - [Ghost User](../user/profile/account/delete_account.md#associated-records)
- [Support Bot](../user/project/service_desk.md#support-bot-user) - [Support Bot](../user/project/service_desk.md#support-bot-user)
- Visual Review Bot - Visual Review Bot
- Resource access tokens (including [project access tokens](../user/project/settings/project_access_tokens.md)).
These are implemented as `project_bot` users with a `PersonalAccessToken`.
...@@ -42,6 +42,10 @@ module Gitlab ...@@ -42,6 +42,10 @@ module Gitlab
nil nil
end end
def can_sign_in_bot?(user)
user&.project_bot? && api_request?
end
# To prevent Rack Attack from incorrectly rate limiting # To prevent Rack Attack from incorrectly rate limiting
# authenticated Git activity, we need to authenticate the user # authenticated Git activity, we need to authenticate the user
# from other means (e.g. HTTP Basic Authentication) only if the # from other means (e.g. HTTP Basic Authentication) only if the
......
...@@ -139,8 +139,45 @@ RSpec.describe GraphqlController do ...@@ -139,8 +139,45 @@ RSpec.describe GraphqlController do
context 'when user uses an API token' do context 'when user uses an API token' do
let(:user) { create(:user, last_activity_on: Date.yesterday) } let(:user) { create(:user, last_activity_on: Date.yesterday) }
let(:token) { create(:personal_access_token, user: user, scopes: [:api]) } let(:token) { create(:personal_access_token, user: user, scopes: [:api]) }
let(:query) { '{ __typename }' }
subject { post :execute, params: { access_token: token.token } } subject { post :execute, params: { query: query, access_token: token.token } }
context 'when the user is a project bot' do
let(:user) { create(:user, :project_bot, last_activity_on: Date.yesterday) }
it 'updates the users last_activity_on field' do
expect { subject }.to change { user.reload.last_activity_on }
end
it "sets context's sessionless value as true" do
subject
expect(assigns(:context)[:is_sessionless_user]).to be true
end
it 'executes a simple query with no errors' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to eq({ 'data' => { '__typename' => 'Query' } })
end
it 'can access resources the project_bot has access to' do
project_a, project_b = create_list(:project, 2, :private)
project_a.add_developer(user)
post :execute, params: { query: <<~GQL, access_token: token.token }
query {
a: project(fullPath: "#{project_a.full_path}") { name }
b: project(fullPath: "#{project_b.full_path}") { name }
}
GQL
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to eq({ 'data' => { 'a' => { 'name' => project_a.name }, 'b' => nil } })
end
end
it 'updates the users last_activity_on field' do it 'updates the users last_activity_on field' do
expect { subject }.to change { user.reload.last_activity_on } expect { subject }.to change { user.reload.last_activity_on }
......
...@@ -44,6 +44,38 @@ RSpec.describe Gitlab::Auth::RequestAuthenticator do ...@@ -44,6 +44,38 @@ RSpec.describe Gitlab::Auth::RequestAuthenticator do
end end
end end
describe '#can_sign_in_bot?' do
context 'the user is nil' do
it { is_expected.not_to be_can_sign_in_bot(nil) }
end
context 'the user is a bot, but for a web request' do
let(:user) { build(:user, :project_bot) }
it { is_expected.not_to be_can_sign_in_bot(user) }
end
context 'the user is a regular user, for an API request' do
let(:user) { build(:user) }
before do
env['SCRIPT_NAME'] = '/api/some_resource'
end
it { is_expected.not_to be_can_sign_in_bot(user) }
end
context 'the user is a project bot, for an API request' do
let(:user) { build(:user, :project_bot) }
before do
env['SCRIPT_NAME'] = '/api/some_resource'
end
it { is_expected.to be_can_sign_in_bot(user) }
end
end
describe '#find_sessionless_user' do describe '#find_sessionless_user' do
let_it_be(:dependency_proxy_user) { build(:user) } let_it_be(:dependency_proxy_user) { build(:user) }
let_it_be(:access_token_user) { build(:user) } let_it_be(:access_token_user) { build(:user) }
......
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