Skip to content
代码片段 群组 项目
未验证 提交 38f00dea 编辑于 作者: Andrew Evans's avatar Andrew Evans 提交者: GitLab
浏览文件

Only allow documented token types for GraphQL authentication

Currently, some token types besides those documented in [the Token
authentication section of the GraphQL docs][1] can be used to
authenticate a user for GraphQL. This MR ensures that only the
documented token types are usable, once the associated feature flag is
enabled.

[1]: https://docs.gitlab.com/ee/api/graphql/#token-authentication

Changelog: fixed
上级 8fc6cc52
No related branches found
No related tags found
无相关合并请求
......@@ -30,7 +30,13 @@ class GraphqlController < ApplicationController
protect_from_forgery with: :null_session, only: :execute
# must come first: current_user is set up here
before_action(only: [:execute]) { authenticate_sessionless_user!(:api) }
before_action(only: [:execute]) do
if Feature.enabled? :graphql_minimal_auth_methods
authenticate_graphql
else
authenticate_sessionless_user!(:api)
end
end
before_action :authorize_access_api!
before_action :set_user_last_activity
......@@ -121,6 +127,18 @@ def feature_category
private
# unwound from SessionlessAuthentication concern
# use a minimal subset of Gitlab::Auth::RequestAuthenticator.find_sessionless_user
# so only token types allowed for GraphQL can authenticate users
# CI_JOB_TOKENs are not allowed for now, since their access is too broad
def authenticate_graphql
user = request_authenticator.find_user_from_web_access_token(:api, scopes: [:api, :read_api])
user ||= request_authenticator.find_user_from_personal_access_token_for_api_or_git
sessionless_sign_in(user) if user
rescue Gitlab::Auth::AuthenticationError
nil
end
def permitted_multiplex_params
params.permit(_json: [:query, :operationName, { variables: {} }])
end
......
---
name: graphql_minimal_auth_methods
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/438462
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/150407
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/444929
milestone: '17.0'
group: group::authentication
type: gitlab_com_derisk
default_enabled: false
......@@ -262,6 +262,215 @@
expect(graphql_data['echo']).to eq("\"#{token.user.username}\" says: Hello world")
end
shared_examples 'valid token' do
it 'accepts from header' do
post_graphql(query, headers: { 'Authorization' => "Bearer #{token}" })
expect(graphql_data['echo']).to eq("\"#{user.username}\" says: Hello world")
end
it 'accepts from access_token parameter' do
post "/api/graphql?access_token=#{token}", params: { query: query }
expect(graphql_data['echo']).to eq("\"#{user.username}\" says: Hello world")
end
it 'accepts from private_token parameter' do
post "/api/graphql?private_token=#{token}", params: { query: query }
expect(graphql_data['echo']).to eq("\"#{user.username}\" says: Hello world")
end
end
context 'with oAuth user access token' do
let(:oauth_application) do
create(
:oauth_application,
scopes: 'api read_user',
redirect_uri: 'http://example.com',
confidential: true
)
end
let(:oauth_access_token) do
create(
:oauth_access_token,
application: oauth_application,
resource_owner: user,
scopes: 'api'
)
end
let(:token) { oauth_access_token.plaintext_token }
# Doorkeeper does not support the private_token=? param
# https://github.com/doorkeeper-gem/doorkeeper/blob/960f1501131683b16c2704d1b6f9597b9583b49d/lib/doorkeeper/oauth/token.rb#L26
# so we cannot use shared examples here
it 'accepts from header' do
post_graphql(query, headers: { 'Authorization' => "Bearer #{token}" })
expect(graphql_data['echo']).to eq("\"#{user.username}\" says: Hello world")
end
it 'accepts from access_token parameter' do
post "/api/graphql?access_token=#{token}", params: { query: query }
expect(graphql_data['echo']).to eq("\"#{user.username}\" says: Hello world")
end
end
context 'with personal access token' do
let(:personal_access_token) { create(:personal_access_token, user: user) }
let(:token) { personal_access_token.token }
it_behaves_like 'valid token'
end
context 'with group or project access token' do
let_it_be(:user) { create(:user, :project_bot) }
let_it_be(:project_access_token) { create(:personal_access_token, user: user) }
let(:token) { project_access_token.token }
it_behaves_like 'valid token'
end
describe 'invalid authentication types' do
let(:query) { 'query { currentUser { id, username } }' }
describe 'with git-lfs token' do
let(:lfs_token) { Gitlab::LfsToken.new(user).token }
let(:header_token) { Base64.encode64("#{user.username}:#{lfs_token}") }
let(:headers) do
{ 'Authorization' => "Basic #{header_token}" }
end
it 'does not authenticate users with an LFS token' do
post '/api/graphql.git', params: { query: query }, headers: headers
expect(graphql_data['currentUser']).to be_nil
end
context 'when graphql_minimal_auth_methods FF is disabled' do
before do
stub_feature_flags(graphql_minimal_auth_methods: false)
end
it 'authenticates users with an LFS token' do
post '/api/graphql.git', params: { query: query }, headers: headers
expect(graphql_data['currentUser']['username']).to eq(user.username)
end
end
end
describe 'with job token' do
let(:project) do
create(:project).tap do |proj|
proj.add_owner(user)
end
end
let(:job) { create(:ci_build, :running, project: project, user: user) }
let(:job_token) { job.token }
it 'raises "Invalid token" error' do
post '/api/graphql', params: { query: query, job_token: job_token }
expect_graphql_errors_to_include(/Invalid token/)
end
context 'when graphql_minimal_auth_methods FF is disabled' do
before do
stub_feature_flags(graphql_minimal_auth_methods: false)
end
it 'authenticates as the user' do
post '/api/graphql', params: { query: query, job_token: job_token }
expect(graphql_data['currentUser']['username']).to eq(user.username)
end
end
end
describe 'with static object token' do
let(:headers) do
{ 'X-Gitlab-Static-Object-Token' => user.static_object_token }
end
it 'does not authenticate user from header' do
post '/api/graphql', params: { query: query }, headers: headers
expect(graphql_data['currentUser']).to be_nil
end
it 'does not authenticate user from parameter' do
post "/api/graphql?token=#{user.static_object_token}", params: { query: query }
expect_graphql_errors_to_include(/Invalid token/)
end
# context is included to demonstrate that the FF code is not changing this behavior
context 'when graphql_minimal_auth_methods FF is disabled' do
before do
stub_feature_flags(graphql_minimal_auth_methods: false)
end
# expect(graphql_data['currentUser']).to be_nil
it 'does not authenticate user from header' do
post '/api/graphql', params: { query: query }, headers: headers
expect(graphql_data['currentUser']).to be_nil
end
it 'does not authenticate user from parameter' do
post "/api/graphql?token=#{user.static_object_token}", params: { query: query }
expect_graphql_errors_to_include(/Invalid token/)
end
end
end
describe 'with dependency proxy token' do
include DependencyProxyHelpers
let(:token) { build_jwt(user).encoded }
let(:headers) do
{ 'Authorization' => "Bearer #{token}" }
end
it 'does not authenticate user from dependency proxy token in headers' do
post '/api/graphql', params: { query: query }, headers: headers
expect_graphql_errors_to_include(/Invalid token/)
end
it 'does not authenticate user from dependency proxy token in parameter' do
post "/api/graphql?access_token=#{token}", params: { query: query }
expect_graphql_errors_to_include(/Invalid token/)
end
# context is included to demonstrate that the FF code is not changing this behavior
context 'when graphql_minimal_auth_methods FF is disabled' do
before do
stub_feature_flags(graphql_minimal_auth_methods: false)
end
it 'does not authenticate user from dependency proxy token in headers' do
post '/api/graphql', params: { query: query }, headers: headers
expect_graphql_errors_to_include(/Invalid token/)
end
it 'does not authenticate user from dependency proxy token in parameter' do
post "/api/graphql?access_token=#{token}", params: { query: query }
expect_graphql_errors_to_include(/Invalid token/)
end
end
end
end
it 'prevents access by deactived users' do
token.user.deactivate!
......
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册