summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorCindy Pallares <cindy@gitlab.com>2018-11-28 19:06:02 +0000
committerCindy Pallares <cindy@gitlab.com>2018-11-28 19:13:59 -0500
commitfe5f75930e781ef854b458fafa307ebb90a8ed2e (patch)
tree7160814e28d056568685e8fe84456755ce02fecd /spec
parente122e14ac6a25c7813ca888a97bd4a3298e78d9d (diff)
downloadgitlab-ce-fe5f75930e781ef854b458fafa307ebb90a8ed2e.tar.gz
Merge branch 'security-fix-pat-web-access' into 'master'
[master] Resolve "Personal access token with only `read_user` scope can be used to authenticate any web request" See merge request gitlab/gitlabhq!2583
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/application_controller_spec.rb151
-rw-r--r--spec/controllers/dashboard/projects_controller_spec.rb5
-rw-r--r--spec/controllers/dashboard_controller_spec.rb31
-rw-r--r--spec/controllers/graphql_controller_spec.rb47
-rw-r--r--spec/controllers/groups_controller_spec.rb20
-rw-r--r--spec/controllers/projects/commits_controller_spec.rb138
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb36
-rw-r--r--spec/controllers/projects/tags_controller_spec.rb22
-rw-r--r--spec/controllers/projects_controller_spec.rb22
-rw-r--r--spec/controllers/users_controller_spec.rb8
-rw-r--r--spec/lib/gitlab/auth/request_authenticator_spec.rb18
-rw-r--r--spec/lib/gitlab/auth/user_auth_finders_spec.rb79
-rw-r--r--spec/support/controllers/sessionless_auth_controller_shared_examples.rb92
13 files changed, 429 insertions, 240 deletions
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index efc3ce74627..1b585bcd4c6 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -107,59 +107,6 @@ describe ApplicationController do
end
end
- describe "#authenticate_user_from_personal_access_token!" do
- before do
- stub_authentication_activity_metrics(debug: false)
- end
-
- controller(described_class) do
- def index
- render text: 'authenticated'
- end
- end
-
- let(:personal_access_token) { create(:personal_access_token, user: user) }
-
- context "when the 'personal_access_token' param is populated with the personal access token" do
- it "logs the user in" do
- expect(authentication_metrics)
- .to increment(:user_authenticated_counter)
- .and increment(:user_session_override_counter)
- .and increment(:user_sessionless_authentication_counter)
-
- get :index, private_token: personal_access_token.token
-
- expect(response).to have_gitlab_http_status(200)
- expect(response.body).to eq('authenticated')
- end
- end
-
- context "when the 'PERSONAL_ACCESS_TOKEN' header is populated with the personal access token" do
- it "logs the user in" do
- expect(authentication_metrics)
- .to increment(:user_authenticated_counter)
- .and increment(:user_session_override_counter)
- .and increment(:user_sessionless_authentication_counter)
-
- @request.headers["PRIVATE-TOKEN"] = personal_access_token.token
- get :index
-
- expect(response).to have_gitlab_http_status(200)
- expect(response.body).to eq('authenticated')
- end
- end
-
- it "doesn't log the user in otherwise" do
- expect(authentication_metrics)
- .to increment(:user_unauthenticated_counter)
-
- get :index, private_token: "token"
-
- expect(response.status).not_to eq(200)
- expect(response.body).not_to eq('authenticated')
- end
- end
-
describe 'session expiration' do
controller(described_class) do
# The anonymous controller will report 401 and fail to run any actions.
@@ -224,74 +171,6 @@ describe ApplicationController do
end
end
- describe '#authenticate_sessionless_user!' do
- before do
- stub_authentication_activity_metrics(debug: false)
- end
-
- describe 'authenticating a user from a feed token' do
- controller(described_class) do
- def index
- render text: 'authenticated'
- end
- end
-
- context "when the 'feed_token' param is populated with the feed token" do
- context 'when the request format is atom' do
- it "logs the user in" do
- expect(authentication_metrics)
- .to increment(:user_authenticated_counter)
- .and increment(:user_session_override_counter)
- .and increment(:user_sessionless_authentication_counter)
-
- get :index, feed_token: user.feed_token, format: :atom
-
- expect(response).to have_gitlab_http_status 200
- expect(response.body).to eq 'authenticated'
- end
- end
-
- context 'when the request format is ics' do
- it "logs the user in" do
- expect(authentication_metrics)
- .to increment(:user_authenticated_counter)
- .and increment(:user_session_override_counter)
- .and increment(:user_sessionless_authentication_counter)
-
- get :index, feed_token: user.feed_token, format: :ics
-
- expect(response).to have_gitlab_http_status 200
- expect(response.body).to eq 'authenticated'
- end
- end
-
- context 'when the request format is neither atom nor ics' do
- it "doesn't log the user in" do
- expect(authentication_metrics)
- .to increment(:user_unauthenticated_counter)
-
- get :index, feed_token: user.feed_token
-
- expect(response.status).not_to have_gitlab_http_status 200
- expect(response.body).not_to eq 'authenticated'
- end
- end
- end
-
- context "when the 'feed_token' param is populated with an invalid feed token" do
- it "doesn't log the user" do
- expect(authentication_metrics)
- .to increment(:user_unauthenticated_counter)
-
- get :index, feed_token: 'token', format: :atom
-
- expect(response.status).not_to eq 200
- expect(response.body).not_to eq 'authenticated'
- end
- end
- end
- end
-
describe '#route_not_found' do
it 'renders 404 if authenticated' do
allow(controller).to receive(:current_user).and_return(user)
@@ -557,36 +436,6 @@ describe ApplicationController do
expect(response).to have_gitlab_http_status(200)
end
-
- context 'for sessionless users' do
- render_views
-
- before do
- sign_out user
- end
-
- it 'renders a 403 when the sessionless user did not accept the terms' do
- get :index, feed_token: user.feed_token, format: :atom
-
- expect(response).to have_gitlab_http_status(403)
- end
-
- it 'renders the error message when the format was html' do
- get :index,
- private_token: create(:personal_access_token, user: user).token,
- format: :html
-
- expect(response.body).to have_content /accept the terms of service/i
- end
-
- it 'renders a 200 when the sessionless user accepted the terms' do
- accept_terms(user)
-
- get :index, feed_token: user.feed_token, format: :atom
-
- expect(response).to have_gitlab_http_status(200)
- end
- end
end
end
diff --git a/spec/controllers/dashboard/projects_controller_spec.rb b/spec/controllers/dashboard/projects_controller_spec.rb
new file mode 100644
index 00000000000..2975205e09c
--- /dev/null
+++ b/spec/controllers/dashboard/projects_controller_spec.rb
@@ -0,0 +1,5 @@
+require 'spec_helper'
+
+describe Dashboard::ProjectsController do
+ it_behaves_like 'authenticates sessionless user', :index, :atom
+end
diff --git a/spec/controllers/dashboard_controller_spec.rb b/spec/controllers/dashboard_controller_spec.rb
index 187542ba30c..c857a78d5e8 100644
--- a/spec/controllers/dashboard_controller_spec.rb
+++ b/spec/controllers/dashboard_controller_spec.rb
@@ -1,21 +1,26 @@
require 'spec_helper'
describe DashboardController do
- let(:user) { create(:user) }
- let(:project) { create(:project) }
+ context 'signed in' do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
- before do
- project.add_maintainer(user)
- sign_in(user)
- end
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+ end
- describe 'GET issues' do
- it_behaves_like 'issuables list meta-data', :issue, :issues
- it_behaves_like 'issuables requiring filter', :issues
- end
+ describe 'GET issues' do
+ it_behaves_like 'issuables list meta-data', :issue, :issues
+ it_behaves_like 'issuables requiring filter', :issues
+ end
- describe 'GET merge requests' do
- it_behaves_like 'issuables list meta-data', :merge_request, :merge_requests
- it_behaves_like 'issuables requiring filter', :merge_requests
+ describe 'GET merge requests' do
+ it_behaves_like 'issuables list meta-data', :merge_request, :merge_requests
+ it_behaves_like 'issuables requiring filter', :merge_requests
+ end
end
+
+ it_behaves_like 'authenticates sessionless user', :issues, :atom, author_id: User.first
+ it_behaves_like 'authenticates sessionless user', :issues_calendar, :ics
end
diff --git a/spec/controllers/graphql_controller_spec.rb b/spec/controllers/graphql_controller_spec.rb
index 1449036e148..949ad532365 100644
--- a/spec/controllers/graphql_controller_spec.rb
+++ b/spec/controllers/graphql_controller_spec.rb
@@ -52,15 +52,58 @@ describe GraphqlController do
end
end
+ context 'token authentication' do
+ before do
+ stub_authentication_activity_metrics(debug: false)
+ end
+
+ let(:user) { create(:user, username: 'Simon') }
+ let(:personal_access_token) { create(:personal_access_token, user: user) }
+
+ context "when the 'personal_access_token' param is populated with the personal access token" do
+ it 'logs the user in' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+ .and increment(:user_session_override_counter)
+ .and increment(:user_sessionless_authentication_counter)
+
+ run_test_query!(private_token: personal_access_token.token)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(query_response).to eq('echo' => '"Simon" says: test success')
+ end
+ end
+
+ context 'when the personal access token has no api scope' do
+ it 'does not log the user in' do
+ personal_access_token.update(scopes: [:read_user])
+
+ run_test_query!(private_token: personal_access_token.token)
+
+ expect(response).to have_gitlab_http_status(200)
+
+ expect(query_response).to eq('echo' => 'nil says: test success')
+ end
+ end
+
+ context 'without token' do
+ it 'shows public data' do
+ run_test_query!
+
+ expect(query_response).to eq('echo' => 'nil says: test success')
+ end
+ end
+ end
+
# Chosen to exercise all the moving parts in GraphqlController#execute
- def run_test_query!(variables: { 'text' => 'test success' })
+ def run_test_query!(variables: { 'text' => 'test success' }, private_token: nil)
query = <<~QUERY
query Echo($text: String) {
echo(text: $text)
}
QUERY
- post :execute, query: query, operationName: 'Echo', variables: variables
+ post :execute, query: query, operationName: 'Echo', variables: variables, private_token: private_token
end
def query_response
diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb
index 4de61b65f71..f6c85102830 100644
--- a/spec/controllers/groups_controller_spec.rb
+++ b/spec/controllers/groups_controller_spec.rb
@@ -606,4 +606,24 @@ describe GroupsController do
end
end
end
+
+ context 'token authentication' do
+ it_behaves_like 'authenticates sessionless user', :show, :atom, public: true do
+ before do
+ default_params.merge!(id: group)
+ end
+ end
+
+ it_behaves_like 'authenticates sessionless user', :issues, :atom, public: true do
+ before do
+ default_params.merge!(id: group, author_id: user.id)
+ end
+ end
+
+ it_behaves_like 'authenticates sessionless user', :issues_calendar, :ics, public: true do
+ before do
+ default_params.merge!(id: group)
+ end
+ end
+ end
end
diff --git a/spec/controllers/projects/commits_controller_spec.rb b/spec/controllers/projects/commits_controller_spec.rb
index a43bdd3ea80..5c72dab698c 100644
--- a/spec/controllers/projects/commits_controller_spec.rb
+++ b/spec/controllers/projects/commits_controller_spec.rb
@@ -5,87 +5,115 @@ describe Projects::CommitsController do
let(:user) { create(:user) }
before do
- sign_in(user)
project.add_maintainer(user)
end
- describe "GET commits_root" do
- context "no ref is provided" do
- it 'should redirect to the default branch of the project' do
- get(:commits_root,
- namespace_id: project.namespace,
- project_id: project)
+ context 'signed in' do
+ before do
+ sign_in(user)
+ end
+
+ describe "GET commits_root" do
+ context "no ref is provided" do
+ it 'should redirect to the default branch of the project' do
+ get(:commits_root,
+ namespace_id: project.namespace,
+ project_id: project)
- expect(response).to redirect_to project_commits_path(project)
+ expect(response).to redirect_to project_commits_path(project)
+ end
end
end
- end
- describe "GET show" do
- render_views
+ describe "GET show" do
+ render_views
- context 'with file path' do
- before do
- get(:show,
- namespace_id: project.namespace,
- project_id: project,
- id: id)
- end
+ context 'with file path' do
+ before do
+ get(:show,
+ namespace_id: project.namespace,
+ project_id: project,
+ id: id)
+ end
- context "valid branch, valid file" do
- let(:id) { 'master/README.md' }
+ context "valid branch, valid file" do
+ let(:id) { 'master/README.md' }
- it { is_expected.to respond_with(:success) }
- end
+ it { is_expected.to respond_with(:success) }
+ end
- context "valid branch, invalid file" do
- let(:id) { 'master/invalid-path.rb' }
+ context "valid branch, invalid file" do
+ let(:id) { 'master/invalid-path.rb' }
- it { is_expected.to respond_with(:not_found) }
- end
+ it { is_expected.to respond_with(:not_found) }
+ end
- context "invalid branch, valid file" do
- let(:id) { 'invalid-branch/README.md' }
+ context "invalid branch, valid file" do
+ let(:id) { 'invalid-branch/README.md' }
- it { is_expected.to respond_with(:not_found) }
+ it { is_expected.to respond_with(:not_found) }
+ end
end
- end
- context "when the ref name ends in .atom" do
- context "when the ref does not exist with the suffix" do
- before do
- get(:show,
- namespace_id: project.namespace,
- project_id: project,
- id: "master.atom")
+ context "when the ref name ends in .atom" do
+ context "when the ref does not exist with the suffix" do
+ before do
+ get(:show,
+ namespace_id: project.namespace,
+ project_id: project,
+ id: "master.atom")
+ end
+
+ it "renders as atom" do
+ expect(response).to be_success
+ expect(response.content_type).to eq('application/atom+xml')
+ end
+
+ it 'renders summary with type=html' do
+ expect(response.body).to include('<summary type="html">')
+ end
end
- it "renders as atom" do
- expect(response).to be_success
- expect(response.content_type).to eq('application/atom+xml')
- end
+ context "when the ref exists with the suffix" do
+ before do
+ commit = project.repository.commit('master')
- it 'renders summary with type=html' do
- expect(response.body).to include('<summary type="html">')
+ allow_any_instance_of(Repository).to receive(:commit).and_call_original
+ allow_any_instance_of(Repository).to receive(:commit).with('master.atom').and_return(commit)
+
+ get(:show,
+ namespace_id: project.namespace,
+ project_id: project,
+ id: "master.atom")
+ end
+
+ it "renders as HTML" do
+ expect(response).to be_success
+ expect(response.content_type).to eq('text/html')
+ end
end
end
+ end
+ end
- context "when the ref exists with the suffix" do
+ context 'token authentication' do
+ context 'public project' do
+ it_behaves_like 'authenticates sessionless user', :show, :atom, public: true do
before do
- commit = project.repository.commit('master')
+ public_project = create(:project, :repository, :public)
- allow_any_instance_of(Repository).to receive(:commit).and_call_original
- allow_any_instance_of(Repository).to receive(:commit).with('master.atom').and_return(commit)
-
- get(:show,
- namespace_id: project.namespace,
- project_id: project,
- id: "master.atom")
+ default_params.merge!(namespace_id: public_project.namespace, project_id: public_project, id: "master.atom")
end
+ end
+ end
+
+ context 'private project' do
+ it_behaves_like 'authenticates sessionless user', :show, :atom, public: false do
+ before do
+ private_project = create(:project, :repository, :private)
+ private_project.add_maintainer(user)
- it "renders as HTML" do
- expect(response).to be_success
- expect(response.content_type).to eq('text/html')
+ default_params.merge!(namespace_id: private_project.namespace, project_id: private_project, id: "master.atom")
end
end
end
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index 80138183c07..02930edbf72 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -1068,4 +1068,40 @@ describe Projects::IssuesController do
end
end
end
+
+ context 'private project with token authentication' do
+ let(:private_project) { create(:project, :private) }
+
+ it_behaves_like 'authenticates sessionless user', :index, :atom do
+ before do
+ default_params.merge!(project_id: private_project, namespace_id: private_project.namespace)
+
+ private_project.add_maintainer(user)
+ end
+ end
+
+ it_behaves_like 'authenticates sessionless user', :calendar, :ics do
+ before do
+ default_params.merge!(project_id: private_project, namespace_id: private_project.namespace)
+
+ private_project.add_maintainer(user)
+ end
+ end
+ end
+
+ context 'public project with token authentication' do
+ let(:public_project) { create(:project, :public) }
+
+ it_behaves_like 'authenticates sessionless user', :index, :atom, public: true do
+ before do
+ default_params.merge!(project_id: public_project, namespace_id: public_project.namespace)
+ end
+ end
+
+ it_behaves_like 'authenticates sessionless user', :calendar, :ics, public: true do
+ before do
+ default_params.merge!(project_id: public_project, namespace_id: public_project.namespace)
+ end
+ end
+ end
end
diff --git a/spec/controllers/projects/tags_controller_spec.rb b/spec/controllers/projects/tags_controller_spec.rb
index c48f41ca12e..6fbf75d0259 100644
--- a/spec/controllers/projects/tags_controller_spec.rb
+++ b/spec/controllers/projects/tags_controller_spec.rb
@@ -35,4 +35,26 @@ describe Projects::TagsController do
it { is_expected.to respond_with(:not_found) }
end
end
+
+ context 'private project with token authentication' do
+ let(:private_project) { create(:project, :repository, :private) }
+
+ it_behaves_like 'authenticates sessionless user', :index, :atom do
+ before do
+ default_params.merge!(project_id: private_project, namespace_id: private_project.namespace)
+
+ private_project.add_maintainer(user)
+ end
+ end
+ end
+
+ context 'public project with token authentication' do
+ let(:public_project) { create(:project, :repository, :public) }
+
+ it_behaves_like 'authenticates sessionless user', :index, :atom, public: true do
+ before do
+ default_params.merge!(project_id: public_project, namespace_id: public_project.namespace)
+ end
+ end
+ end
end
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index 3bc9cbe64c5..7849bec4762 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -882,6 +882,28 @@ describe ProjectsController do
end
end
+ context 'private project with token authentication' do
+ let(:private_project) { create(:project, :private) }
+
+ it_behaves_like 'authenticates sessionless user', :show, :atom do
+ before do
+ default_params.merge!(id: private_project, namespace_id: private_project.namespace)
+
+ private_project.add_maintainer(user)
+ end
+ end
+ end
+
+ context 'public project with token authentication' do
+ let(:public_project) { create(:project, :public) }
+
+ it_behaves_like 'authenticates sessionless user', :show, :atom, public: true do
+ before do
+ default_params.merge!(id: public_project, namespace_id: public_project.namespace)
+ end
+ end
+ end
+
def project_moved_message(redirect_route, project)
"Project '#{redirect_route.path}' was moved to '#{project.full_path}'. Please update any links and bookmarks that may still have the old path."
end
diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb
index 071f96a729e..fe438e71e9e 100644
--- a/spec/controllers/users_controller_spec.rb
+++ b/spec/controllers/users_controller_spec.rb
@@ -395,6 +395,14 @@ describe UsersController do
end
end
+ context 'token authentication' do
+ it_behaves_like 'authenticates sessionless user', :show, :atom, public: true do
+ before do
+ default_params.merge!(username: user.username)
+ end
+ end
+ end
+
def user_moved_message(redirect_route, user)
"User '#{redirect_route.path}' was moved to '#{user.full_path}'. Please update any links and bookmarks that may still have the old path."
end
diff --git a/spec/lib/gitlab/auth/request_authenticator_spec.rb b/spec/lib/gitlab/auth/request_authenticator_spec.rb
index 242ab4a91dd..3d979132880 100644
--- a/spec/lib/gitlab/auth/request_authenticator_spec.rb
+++ b/spec/lib/gitlab/auth/request_authenticator_spec.rb
@@ -19,17 +19,17 @@ describe Gitlab::Auth::RequestAuthenticator do
allow_any_instance_of(described_class).to receive(:find_sessionless_user).and_return(sessionless_user)
allow_any_instance_of(described_class).to receive(:find_user_from_warden).and_return(session_user)
- expect(subject.user).to eq sessionless_user
+ expect(subject.user([:api])).to eq sessionless_user
end
it 'returns session user if no sessionless user found' do
allow_any_instance_of(described_class).to receive(:find_user_from_warden).and_return(session_user)
- expect(subject.user).to eq session_user
+ expect(subject.user([:api])).to eq session_user
end
it 'returns nil if no user found' do
- expect(subject.user).to be_blank
+ expect(subject.user([:api])).to be_blank
end
it 'bubbles up exceptions' do
@@ -42,26 +42,26 @@ describe Gitlab::Auth::RequestAuthenticator do
let!(:feed_token_user) { build(:user) }
it 'returns access_token user first' do
- allow_any_instance_of(described_class).to receive(:find_user_from_access_token).and_return(access_token_user)
+ allow_any_instance_of(described_class).to receive(:find_user_from_web_access_token).and_return(access_token_user)
allow_any_instance_of(described_class).to receive(:find_user_from_feed_token).and_return(feed_token_user)
- expect(subject.find_sessionless_user).to eq access_token_user
+ expect(subject.find_sessionless_user([:api])).to eq access_token_user
end
it 'returns feed_token user if no access_token user found' do
allow_any_instance_of(described_class).to receive(:find_user_from_feed_token).and_return(feed_token_user)
- expect(subject.find_sessionless_user).to eq feed_token_user
+ expect(subject.find_sessionless_user([:api])).to eq feed_token_user
end
it 'returns nil if no user found' do
- expect(subject.find_sessionless_user).to be_blank
+ expect(subject.find_sessionless_user([:api])).to be_blank
end
it 'rescue Gitlab::Auth::AuthenticationError exceptions' do
- allow_any_instance_of(described_class).to receive(:find_user_from_access_token).and_raise(Gitlab::Auth::UnauthorizedError)
+ allow_any_instance_of(described_class).to receive(:find_user_from_web_access_token).and_raise(Gitlab::Auth::UnauthorizedError)
- expect(subject.find_sessionless_user).to be_blank
+ expect(subject.find_sessionless_user([:api])).to be_blank
end
end
end
diff --git a/spec/lib/gitlab/auth/user_auth_finders_spec.rb b/spec/lib/gitlab/auth/user_auth_finders_spec.rb
index 454ad1589b9..5d3fbba264f 100644
--- a/spec/lib/gitlab/auth/user_auth_finders_spec.rb
+++ b/spec/lib/gitlab/auth/user_auth_finders_spec.rb
@@ -9,7 +9,7 @@ describe Gitlab::Auth::UserAuthFinders do
'rack.input' => ''
}
end
- let(:request) { Rack::Request.new(env)}
+ let(:request) { Rack::Request.new(env) }
def set_param(key, value)
request.update_param(key, value)
@@ -49,6 +49,7 @@ describe Gitlab::Auth::UserAuthFinders do
describe '#find_user_from_feed_token' do
context 'when the request format is atom' do
before do
+ env['SCRIPT_NAME'] = 'url.atom'
env['HTTP_ACCEPT'] = 'application/atom+xml'
end
@@ -56,17 +57,17 @@ describe Gitlab::Auth::UserAuthFinders do
it 'returns user if valid feed_token' do
set_param(:feed_token, user.feed_token)
- expect(find_user_from_feed_token).to eq user
+ expect(find_user_from_feed_token(:rss)).to eq user
end
it 'returns nil if feed_token is blank' do
- expect(find_user_from_feed_token).to be_nil
+ expect(find_user_from_feed_token(:rss)).to be_nil
end
it 'returns exception if invalid feed_token' do
set_param(:feed_token, 'invalid_token')
- expect { find_user_from_feed_token }.to raise_error(Gitlab::Auth::UnauthorizedError)
+ expect { find_user_from_feed_token(:rss) }.to raise_error(Gitlab::Auth::UnauthorizedError)
end
end
@@ -74,34 +75,38 @@ describe Gitlab::Auth::UserAuthFinders do
it 'returns user if valid rssd_token' do
set_param(:rss_token, user.feed_token)
- expect(find_user_from_feed_token).to eq user
+ expect(find_user_from_feed_token(:rss)).to eq user
end
it 'returns nil if rss_token is blank' do
- expect(find_user_from_feed_token).to be_nil
+ expect(find_user_from_feed_token(:rss)).to be_nil
end
it 'returns exception if invalid rss_token' do
set_param(:rss_token, 'invalid_token')
- expect { find_user_from_feed_token }.to raise_error(Gitlab::Auth::UnauthorizedError)
+ expect { find_user_from_feed_token(:rss) }.to raise_error(Gitlab::Auth::UnauthorizedError)
end
end
end
context 'when the request format is not atom' do
it 'returns nil' do
+ env['SCRIPT_NAME'] = 'json'
+
set_param(:feed_token, user.feed_token)
- expect(find_user_from_feed_token).to be_nil
+ expect(find_user_from_feed_token(:rss)).to be_nil
end
end
context 'when the request format is empty' do
it 'the method call does not modify the original value' do
+ env['SCRIPT_NAME'] = 'url.atom'
+
env.delete('action_dispatch.request.formats')
- find_user_from_feed_token
+ find_user_from_feed_token(:rss)
expect(env['action_dispatch.request.formats']).to be_nil
end
@@ -111,8 +116,12 @@ describe Gitlab::Auth::UserAuthFinders do
describe '#find_user_from_access_token' do
let(:personal_access_token) { create(:personal_access_token, user: user) }
+ before do
+ env['SCRIPT_NAME'] = 'url.atom'
+ end
+
it 'returns nil if no access_token present' do
- expect(find_personal_access_token).to be_nil
+ expect(find_user_from_access_token).to be_nil
end
context 'when validate_access_token! returns valid' do
@@ -131,9 +140,59 @@ describe Gitlab::Auth::UserAuthFinders do
end
end
+ describe '#find_user_from_web_access_token' do
+ let(:personal_access_token) { create(:personal_access_token, user: user) }
+
+ before do
+ env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
+ end
+
+ it 'returns exception if token has no user' do
+ allow_any_instance_of(PersonalAccessToken).to receive(:user).and_return(nil)
+
+ expect { find_user_from_access_token }.to raise_error(Gitlab::Auth::UnauthorizedError)
+ end
+
+ context 'no feed or API requests' do
+ it 'returns nil if the request is not RSS' do
+ expect(find_user_from_web_access_token(:rss)).to be_nil
+ end
+
+ it 'returns nil if the request is not ICS' do
+ expect(find_user_from_web_access_token(:ics)).to be_nil
+ end
+
+ it 'returns nil if the request is not API' do
+ expect(find_user_from_web_access_token(:api)).to be_nil
+ end
+ end
+
+ it 'returns the user for RSS requests' do
+ env['SCRIPT_NAME'] = 'url.atom'
+
+ expect(find_user_from_web_access_token(:rss)).to eq(user)
+ end
+
+ it 'returns the user for ICS requests' do
+ env['SCRIPT_NAME'] = 'url.ics'
+
+ expect(find_user_from_web_access_token(:ics)).to eq(user)
+ end
+
+ it 'returns the user for API requests' do
+ env['SCRIPT_NAME'] = '/api/endpoint'
+
+ expect(find_user_from_web_access_token(:api)).to eq(user)
+ end
+ end
+
describe '#find_personal_access_token' do
let(:personal_access_token) { create(:personal_access_token, user: user) }
+ before do
+ env['SCRIPT_NAME'] = 'url.atom'
+ end
+
context 'passed as header' do
it 'returns token if valid personal_access_token' do
env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
diff --git a/spec/support/controllers/sessionless_auth_controller_shared_examples.rb b/spec/support/controllers/sessionless_auth_controller_shared_examples.rb
new file mode 100644
index 00000000000..7e4958f177a
--- /dev/null
+++ b/spec/support/controllers/sessionless_auth_controller_shared_examples.rb
@@ -0,0 +1,92 @@
+shared_examples 'authenticates sessionless user' do |path, format, params|
+ params ||= {}
+
+ before do
+ stub_authentication_activity_metrics(debug: false)
+ end
+
+ let(:user) { create(:user) }
+ let(:personal_access_token) { create(:personal_access_token, user: user) }
+ let(:default_params) { { format: format }.merge(params.except(:public) || {}) }
+
+ context "when the 'personal_access_token' param is populated with the personal access token" do
+ it 'logs the user in' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+ .and increment(:user_session_override_counter)
+ .and increment(:user_sessionless_authentication_counter)
+
+ get path, default_params.merge(private_token: personal_access_token.token)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(controller.current_user).to eq(user)
+ end
+
+ it 'does not log the user in if page is public', if: params[:public] do
+ get path, default_params
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(controller.current_user).to be_nil
+ end
+ end
+
+ context 'when the personal access token has no api scope', unless: params[:public] do
+ it 'does not log the user in' do
+ expect(authentication_metrics)
+ .to increment(:user_unauthenticated_counter)
+
+ personal_access_token.update(scopes: [:read_user])
+
+ get path, default_params.merge(private_token: personal_access_token.token)
+
+ expect(response).not_to have_gitlab_http_status(200)
+ end
+ end
+
+ context "when the 'PERSONAL_ACCESS_TOKEN' header is populated with the personal access token" do
+ it 'logs the user in' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+ .and increment(:user_session_override_counter)
+ .and increment(:user_sessionless_authentication_counter)
+
+ @request.headers['PRIVATE-TOKEN'] = personal_access_token.token
+ get path, default_params
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+ end
+
+ context "when the 'feed_token' param is populated with the feed token", if: format == :rss do
+ it "logs the user in" do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+ .and increment(:user_session_override_counter)
+ .and increment(:user_sessionless_authentication_counter)
+
+ get path, default_params.merge(feed_token: user.feed_token)
+
+ expect(response).to have_gitlab_http_status 200
+ end
+ end
+
+ context "when the 'feed_token' param is populated with an invalid feed token", if: format == :rss, unless: params[:public] do
+ it "logs the user" do
+ expect(authentication_metrics)
+ .to increment(:user_unauthenticated_counter)
+
+ get path, default_params.merge(feed_token: 'token')
+
+ expect(response.status).not_to eq 200
+ end
+ end
+
+ it "doesn't log the user in otherwise", unless: params[:public] do
+ expect(authentication_metrics)
+ .to increment(:user_unauthenticated_counter)
+
+ get path, default_params.merge(private_token: 'token')
+
+ expect(response.status).not_to eq(200)
+ end
+end