Commit 369c57fc authored by Max Woolf's avatar Max Woolf

Merge branch 'ajk-graphql-project-token' into 'master'

Support GraphQL authentication with project tokens

See merge request gitlab-org/gitlab!82316
parents 8a3533c3 16913601
......@@ -26,6 +26,9 @@ module SessionlessAuthentication
# for every request. If you want the token to work as a
# sign in token, you can simply remove store: false.
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
......
......@@ -42,3 +42,5 @@ Other examples of internal users:
- [Ghost User](../user/profile/account/delete_account.md#associated-records)
- [Support Bot](../user/project/service_desk.md#support-bot-user)
- 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
nil
end
def can_sign_in_bot?(user)
user&.project_bot? && api_request?
end
# To prevent Rack Attack from incorrectly rate limiting
# authenticated Git activity, we need to authenticate the user
# from other means (e.g. HTTP Basic Authentication) only if the
......
......@@ -139,8 +139,45 @@ RSpec.describe GraphqlController do
context 'when user uses an API token' do
let(:user) { create(:user, last_activity_on: Date.yesterday) }
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
expect { subject }.to change { user.reload.last_activity_on }
......
......@@ -44,6 +44,38 @@ RSpec.describe Gitlab::Auth::RequestAuthenticator do
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
let_it_be(:dependency_proxy_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