2019-12-26 22:10:19 +05:30
# frozen_string_literal: true
2016-06-02 11:05:42 +05:30
require 'spec_helper'
2020-07-28 23:09:34 +05:30
RSpec . describe JwtController do
2020-07-01 16:08:20 +05:30
include_context 'parsed logs'
2021-02-22 17:27:13 +05:30
let ( :service ) { double ( execute : { } ) }
let ( :service_class ) { Auth :: ContainerRegistryAuthenticationService }
let ( :service_name ) { 'container_registry' }
2016-06-02 11:05:42 +05:30
let ( :parameters ) { { service : service_name } }
2017-09-10 17:25:29 +05:30
before do
2021-02-22 17:27:13 +05:30
allow ( service_class ) . to receive ( :new ) . and_return ( service )
2017-09-10 17:25:29 +05:30
end
2016-06-02 11:05:42 +05:30
2020-05-24 23:13:21 +05:30
shared_examples 'user logging' do
it 'logs username and ID' do
expect ( log_data [ 'username' ] ) . to eq ( user . username )
expect ( log_data [ 'user_id' ] ) . to eq ( user . id )
expect ( log_data [ 'meta.user' ] ) . to eq ( user . username )
end
end
2022-08-27 11:52:29 +05:30
shared_examples 'a token that expires today' do
let ( :pat ) { create ( :personal_access_token , user : user , scopes : [ 'api' ] , expires_at : Date . today ) }
let ( :headers ) { { authorization : credentials ( 'personal_access_token' , pat . token ) } }
it 'fails authentication' do
2022-10-11 01:57:18 +05:30
expect ( :: Gitlab :: AuthLogger ) . to receive ( :warn ) . with (
hash_including ( message : 'JWT authentication failed' ,
http_user : 'personal_access_token' ) ) . and_call_original
2022-08-27 11:52:29 +05:30
get '/jwt/auth' , params : parameters , headers : headers
expect ( response ) . to have_gitlab_http_status ( :unauthorized )
end
end
2022-09-01 20:07:04 +05:30
shared_examples " with invalid credentials " do
it " returns a generic error message " do
subject
expect ( response ) . to have_gitlab_http_status ( :unauthorized )
expect ( json_response ) . to eq (
{
" errors " = > [ {
" code " = > " UNAUTHORIZED " ,
" message " = > " HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See http://www.example.com/help/user/profile/account/two_factor_authentication # troubleshooting "
} ]
}
)
end
end
2021-02-22 17:27:13 +05:30
context 'authenticating against container registry' do
context 'existing service' do
subject! { get '/jwt/auth' , params : parameters }
2016-06-02 11:05:42 +05:30
2021-02-22 17:27:13 +05:30
it { expect ( response ) . to have_gitlab_http_status ( :ok ) }
2016-06-02 11:05:42 +05:30
2021-02-22 17:27:13 +05:30
context 'returning custom http code' do
let ( :service ) { double ( execute : { http_status : 505 } ) }
2016-06-02 11:05:42 +05:30
2021-02-22 17:27:13 +05:30
it { expect ( response ) . to have_gitlab_http_status ( :http_version_not_supported ) }
end
2016-06-02 11:05:42 +05:30
end
2021-02-22 17:27:13 +05:30
context 'when using authenticated request' do
shared_examples 'rejecting a blocked user' do
context 'with blocked user' do
let ( :user ) { create ( :user , :blocked ) }
2020-03-28 13:19:24 +05:30
2022-09-01 20:07:04 +05:30
it_behaves_like 'with invalid credentials'
2020-03-28 13:19:24 +05:30
end
end
2021-02-22 17:27:13 +05:30
context 'using CI token' do
let ( :user ) { create ( :user ) }
let ( :build ) { create ( :ci_build , :running , user : user ) }
let ( :project ) { build . project }
let ( :headers ) { { authorization : credentials ( 'gitlab-ci-token' , build . token ) } }
2016-06-02 11:05:42 +05:30
2021-02-22 17:27:13 +05:30
context 'project with enabled CI' do
subject! { get '/jwt/auth' , params : parameters , headers : headers }
2020-05-24 23:13:21 +05:30
2022-10-11 01:57:18 +05:30
it { expect ( service_class ) . to have_received ( :new ) . with ( project , user , ActionController :: Parameters . new ( parameters . merge ( auth_type : :build ) ) . permit! ) }
2016-06-02 11:05:42 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'user logging'
2016-09-29 09:46:39 +05:30
end
2021-02-22 17:27:13 +05:30
context 'project with disabled CI' do
before do
project . project_feature . update_attribute ( :builds_access_level , ProjectFeature :: DISABLED )
end
2016-06-02 11:05:42 +05:30
2021-02-22 17:27:13 +05:30
subject! { get '/jwt/auth' , params : parameters , headers : headers }
2017-09-10 17:25:29 +05:30
2021-02-22 17:27:13 +05:30
it { expect ( response ) . to have_gitlab_http_status ( :unauthorized ) }
end
2020-05-24 23:13:21 +05:30
2021-02-22 17:27:13 +05:30
context 'using deploy tokens' do
let ( :deploy_token ) { create ( :deploy_token , read_registry : true , projects : [ project ] ) }
let ( :headers ) { { authorization : credentials ( deploy_token . username , deploy_token . token ) } }
2020-05-24 23:13:21 +05:30
2021-02-22 17:27:13 +05:30
subject! { get '/jwt/auth' , params : parameters , headers : headers }
2020-05-24 23:13:21 +05:30
2021-02-22 17:27:13 +05:30
it 'authenticates correctly' do
expect ( response ) . to have_gitlab_http_status ( :ok )
2022-10-11 01:57:18 +05:30
expect ( service_class ) . to have_received ( :new )
. with (
nil ,
nil ,
ActionController :: Parameters . new ( parameters . merge ( deploy_token : deploy_token , auth_type : :deploy_token ) ) . permit!
)
2021-02-22 17:27:13 +05:30
end
it 'does not log a user' do
expect ( log_data . keys ) . not_to include ( %w( username user_id ) )
end
2020-05-24 23:13:21 +05:30
end
2021-02-22 17:27:13 +05:30
context 'using personal access tokens' do
let ( :pat ) { create ( :personal_access_token , user : user , scopes : [ 'read_registry' ] ) }
let ( :headers ) { { authorization : credentials ( 'personal_access_token' , pat . token ) } }
2017-09-10 17:25:29 +05:30
2021-02-22 17:27:13 +05:30
before do
stub_container_registry_config ( enabled : true )
end
subject! { get '/jwt/auth' , params : parameters , headers : headers }
it 'authenticates correctly' do
expect ( response ) . to have_gitlab_http_status ( :ok )
2022-10-11 01:57:18 +05:30
expect ( service_class ) . to have_received ( :new )
. with (
nil ,
user ,
ActionController :: Parameters . new ( parameters . merge ( auth_type : :personal_access_token ) ) . permit!
)
2021-02-22 17:27:13 +05:30
end
it_behaves_like 'rejecting a blocked user'
it_behaves_like 'user logging'
2022-08-27 11:52:29 +05:30
it_behaves_like 'a token that expires today'
2018-03-17 18:26:18 +05:30
end
2021-02-22 17:27:13 +05:30
end
context 'using User login' do
let ( :user ) { create ( :user ) }
let ( :headers ) { { authorization : credentials ( user . username , user . password ) } }
2018-03-17 18:26:18 +05:30
2019-02-15 15:39:39 +05:30
subject! { get '/jwt/auth' , params : parameters , headers : headers }
2017-09-10 17:25:29 +05:30
2022-10-11 01:57:18 +05:30
it { expect ( service_class ) . to have_received ( :new ) . with ( nil , user , ActionController :: Parameters . new ( parameters . merge ( auth_type : :gitlab_or_ldap ) ) . permit! ) }
2020-03-28 13:19:24 +05:30
it_behaves_like 'rejecting a blocked user'
2016-06-02 11:05:42 +05:30
2021-02-22 17:27:13 +05:30
context 'when passing a flat array of scopes' do
# We use this trick to make rails to generate a query_string:
# scope=scope1&scope=scope2
# It works because :scope and 'scope' are the same as string, but different objects
let ( :parameters ) do
{
:service = > service_name ,
:scope = > 'scope1' ,
'scope' = > 'scope2'
}
end
2016-06-02 11:05:42 +05:30
2021-02-22 17:27:13 +05:30
let ( :service_parameters ) do
ActionController :: Parameters . new ( { service : service_name , scopes : %w( scope1 scope2 ) } ) . permit!
end
2016-09-29 09:46:39 +05:30
2022-10-11 01:57:18 +05:30
it { expect ( service_class ) . to have_received ( :new ) . with ( nil , user , service_parameters . merge ( auth_type : :gitlab_or_ldap ) ) }
2020-03-28 13:19:24 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'user logging'
2018-11-18 11:00:15 +05:30
end
2021-02-22 17:27:13 +05:30
context 'when user has 2FA enabled' do
let ( :user ) { create ( :user , :two_factor ) }
context 'without personal token' do
2022-09-01 20:07:04 +05:30
it_behaves_like 'with invalid credentials'
2021-02-22 17:27:13 +05:30
end
context 'with personal token' do
let ( :access_token ) { create ( :personal_access_token , user : user ) }
let ( :headers ) { { authorization : credentials ( user . username , access_token . token ) } }
it 'accepts the authorization attempt' do
expect ( response ) . to have_gitlab_http_status ( :ok )
end
end
2018-11-18 11:00:15 +05:30
end
2021-02-22 17:27:13 +05:30
it 'does not cause session based checks to be activated' do
expect ( Gitlab :: Session ) . not_to receive ( :with_session )
get '/jwt/auth' , params : parameters , headers : headers
2020-05-24 23:13:21 +05:30
2021-02-22 17:27:13 +05:30
expect ( response ) . to have_gitlab_http_status ( :ok )
end
2018-11-18 11:00:15 +05:30
end
2021-02-22 17:27:13 +05:30
context 'using invalid login' do
let ( :headers ) { { authorization : credentials ( 'invalid' , 'password' ) } }
2022-09-01 20:07:04 +05:30
let ( :subject ) { get '/jwt/auth' , params : parameters , headers : headers }
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
context 'when internal auth is enabled' do
2022-09-01 20:07:04 +05:30
it_behaves_like 'with invalid credentials'
2016-09-29 09:46:39 +05:30
end
2021-02-22 17:27:13 +05:30
context 'when internal auth is disabled' do
2021-04-29 21:17:54 +05:30
before do
stub_application_setting ( password_authentication_enabled_for_git : false )
end
2022-09-01 20:07:04 +05:30
it_behaves_like 'with invalid credentials'
2016-09-29 09:46:39 +05:30
end
end
2021-02-22 17:27:13 +05:30
end
2019-12-04 20:38:33 +05:30
2021-02-22 17:27:13 +05:30
context 'when using unauthenticated request' do
it 'accepts the authorization attempt' do
get '/jwt/auth' , params : parameters
2019-12-04 20:38:33 +05:30
2020-03-13 15:44:24 +05:30
expect ( response ) . to have_gitlab_http_status ( :ok )
2019-12-04 20:38:33 +05:30
end
2021-02-22 17:27:13 +05:30
it 'allows read access' do
expect ( service ) . to receive ( :execute ) . with ( authentication_abilities : Gitlab :: Auth . read_only_authentication_abilities )
get '/jwt/auth' , params : parameters
end
2016-06-02 11:05:42 +05:30
end
2021-02-22 17:27:13 +05:30
context 'unknown service' do
subject! { get '/jwt/auth' , params : { service : 'unknown' } }
2016-06-02 11:05:42 +05:30
2021-02-22 17:27:13 +05:30
it { expect ( response ) . to have_gitlab_http_status ( :not_found ) }
end
2017-09-10 17:25:29 +05:30
2021-02-22 17:27:13 +05:30
def credentials ( login , password )
ActionController :: HttpAuthentication :: Basic . encode_credentials ( login , password )
end
end
2016-06-02 11:05:42 +05:30
2021-02-22 17:27:13 +05:30
context 'authenticating against dependency proxy' do
let_it_be ( :user ) { create ( :user ) }
let_it_be ( :personal_access_token ) { create ( :personal_access_token , user : user ) }
let_it_be ( :group ) { create ( :group ) }
let_it_be ( :project ) { create ( :project , :private , group : group ) }
2021-10-27 15:23:28 +05:30
let_it_be ( :group_deploy_token ) { create ( :deploy_token , :group , :dependency_proxy_scopes ) }
let_it_be ( :gdeploy_token ) { create ( :group_deploy_token , deploy_token : group_deploy_token , group : group ) }
let_it_be ( :project_deploy_token ) { create ( :deploy_token , :project , :dependency_proxy_scopes ) }
let_it_be ( :pdeploy_token ) { create ( :project_deploy_token , deploy_token : project_deploy_token , project : project ) }
2021-02-22 17:27:13 +05:30
let_it_be ( :service_name ) { 'dependency_proxy' }
2021-09-30 23:02:18 +05:30
2021-02-22 17:27:13 +05:30
let ( :headers ) { { authorization : credentials ( credential_user , credential_password ) } }
let ( :params ) { { account : credential_user , client_id : 'docker' , offline_token : true , service : service_name } }
before do
stub_config ( dependency_proxy : { enabled : true } )
end
2017-09-10 17:25:29 +05:30
2021-02-22 17:27:13 +05:30
subject { get '/jwt/auth' , params : params , headers : headers }
shared_examples 'with valid credentials' do
it 'returns token successfully' do
subject
expect ( response ) . to have_gitlab_http_status ( :success )
expect ( json_response [ 'token' ] ) . to be_present
2017-09-10 17:25:29 +05:30
end
2016-06-02 11:05:42 +05:30
end
2021-02-22 17:27:13 +05:30
context 'with personal access token' do
let ( :credential_user ) { nil }
let ( :credential_password ) { personal_access_token . token }
2016-11-24 13:41:30 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'with valid credentials'
2022-08-27 11:52:29 +05:30
it_behaves_like 'a token that expires today'
2016-11-24 13:41:30 +05:30
end
2021-02-22 17:27:13 +05:30
context 'with user credentials token' do
let ( :credential_user ) { user . username }
let ( :credential_password ) { user . password }
2016-11-24 13:41:30 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'with valid credentials'
2016-11-24 13:41:30 +05:30
end
2021-02-22 17:27:13 +05:30
context 'with group deploy token' do
let ( :credential_user ) { group_deploy_token . username }
let ( :credential_password ) { group_deploy_token . token }
2016-06-02 11:05:42 +05:30
2021-10-27 15:23:28 +05:30
it_behaves_like 'with valid credentials'
2021-02-22 17:27:13 +05:30
end
context 'with project deploy token' do
let ( :credential_user ) { project_deploy_token . username }
let ( :credential_password ) { project_deploy_token . token }
2021-04-28 17:22:55 +05:30
it_behaves_like 'returning response status' , :forbidden
2021-02-22 17:27:13 +05:30
end
2021-10-27 15:23:28 +05:30
context 'with revoked group deploy token' do
let ( :credential_user ) { group_deploy_token . username }
let ( :credential_password ) { project_deploy_token . token }
before do
group_deploy_token . update_column ( :revoked , true )
end
it_behaves_like 'returning response status' , :unauthorized
end
context 'with group deploy token with insufficient scopes' do
let ( :credential_user ) { group_deploy_token . username }
let ( :credential_password ) { project_deploy_token . token }
before do
group_deploy_token . update_column ( :write_registry , false )
end
it_behaves_like 'returning response status' , :unauthorized
end
2021-02-22 17:27:13 +05:30
context 'with invalid credentials' do
let ( :credential_user ) { 'foo' }
let ( :credential_password ) { 'bar' }
2021-04-28 17:22:55 +05:30
it_behaves_like 'returning response status' , :unauthorized
2021-02-22 17:27:13 +05:30
end
2016-06-02 11:05:42 +05:30
end
def credentials ( login , password )
ActionController :: HttpAuthentication :: Basic . encode_credentials ( login , password )
end
end