2019-10-28 20:06:10 -04:00
# frozen_string_literal: true
2016-05-14 20:45:48 -04:00
require 'spec_helper'
2020-06-24 02:09:01 -04:00
RSpec . describe JwtController do
2020-06-23 02:08:52 -04:00
include_context 'parsed logs'
2020-12-03 13:10:10 -05:00
let ( :service ) { double ( execute : { } ) }
let ( :service_class ) { Auth :: ContainerRegistryAuthenticationService }
let ( :service_name ) { 'container_registry' }
2016-05-15 01:33:06 -04:00
let ( :parameters ) { { service : service_name } }
2016-05-14 20:45:48 -04:00
2017-06-14 14:18:56 -04:00
before do
2020-12-03 13:10:10 -05:00
allow ( service_class ) . to receive ( :new ) . and_return ( service )
2017-06-14 14:18:56 -04:00
end
2016-05-14 20:45:48 -04:00
2020-05-11 11:09:37 -04:00
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 )
2020-05-15 14:07:52 -04:00
expect ( log_data [ 'meta.user' ] ) . to eq ( user . username )
2020-05-11 11:09:37 -04:00
end
end
2022-08-16 17:12:07 -04:00
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-08-17 17:09:50 -04:00
expect ( :: Gitlab :: AuthLogger ) . to receive ( :warn ) . with (
hash_including ( message : 'JWT authentication failed' ,
http_user : 'personal_access_token' ) ) . and_call_original
2022-08-16 17:12:07 -04:00
get '/jwt/auth' , params : parameters , headers : headers
expect ( response ) . to have_gitlab_http_status ( :unauthorized )
end
end
2022-08-30 14:09:50 -04:00
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
2020-12-03 13:10:10 -05:00
context 'authenticating against container registry' do
context 'existing service' do
subject! { get '/jwt/auth' , params : parameters }
2016-05-14 20:45:48 -04:00
2020-12-03 13:10:10 -05:00
it { expect ( response ) . to have_gitlab_http_status ( :ok ) }
2016-05-15 01:33:06 -04:00
2020-12-03 13:10:10 -05:00
context 'returning custom http code' do
let ( :service ) { double ( execute : { http_status : 505 } ) }
2016-05-15 01:33:06 -04:00
2020-12-03 13:10:10 -05:00
it { expect ( response ) . to have_gitlab_http_status ( :http_version_not_supported ) }
end
2016-05-15 01:33:06 -04:00
end
2016-05-14 20:45:48 -04:00
2020-12-03 13:10:10 -05:00
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-26 14:08:03 -04:00
2022-08-30 14:09:50 -04:00
it_behaves_like 'with invalid credentials'
2020-03-26 14:08:03 -04:00
end
end
2020-12-03 13:10:10 -05:00
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-05-15 01:33:06 -04:00
2020-12-03 13:10:10 -05:00
context 'project with enabled CI' do
subject! { get '/jwt/auth' , params : parameters , headers : headers }
2020-05-11 11:09:37 -04:00
2022-09-14 20:14:10 -04:00
it { expect ( service_class ) . to have_received ( :new ) . with ( project , user , ActionController :: Parameters . new ( parameters . merge ( auth_type : :build ) ) . permit! ) }
2016-05-14 20:45:48 -04:00
2020-12-03 13:10:10 -05:00
it_behaves_like 'user logging'
2016-08-01 18:31:21 -04:00
end
2020-12-03 13:10:10 -05:00
context 'project with disabled CI' do
before do
project . project_feature . update_attribute ( :builds_access_level , ProjectFeature :: DISABLED )
end
2016-05-14 20:45:48 -04:00
2020-12-03 13:10:10 -05:00
subject! { get '/jwt/auth' , params : parameters , headers : headers }
2017-05-31 09:55:12 -04:00
2020-12-03 13:10:10 -05:00
it { expect ( response ) . to have_gitlab_http_status ( :unauthorized ) }
end
2020-05-11 11:09:37 -04:00
2020-12-03 13:10:10 -05:00
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-11 11:09:37 -04:00
2020-12-03 13:10:10 -05:00
subject! { get '/jwt/auth' , params : parameters , headers : headers }
2020-05-11 11:09:37 -04:00
2020-12-03 13:10:10 -05:00
it 'authenticates correctly' do
expect ( response ) . to have_gitlab_http_status ( :ok )
2022-09-14 20:14:10 -04:00
expect ( service_class ) . to have_received ( :new )
. with (
nil ,
nil ,
ActionController :: Parameters . new ( parameters . merge ( deploy_token : deploy_token , auth_type : :deploy_token ) ) . permit!
)
2020-12-03 13:10:10 -05:00
end
it 'does not log a user' do
expect ( log_data . keys ) . not_to include ( %w( username user_id ) )
end
2020-05-11 11:09:37 -04:00
end
2020-12-03 13:10:10 -05:00
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-05-31 09:55:12 -04:00
2020-12-03 13:10:10 -05:00
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-09-14 20:14:10 -04:00
expect ( service_class ) . to have_received ( :new )
. with (
nil ,
user ,
ActionController :: Parameters . new ( parameters . merge ( auth_type : :personal_access_token ) ) . permit!
)
2020-12-03 13:10:10 -05:00
end
it_behaves_like 'rejecting a blocked user'
it_behaves_like 'user logging'
2022-08-16 17:12:07 -04:00
it_behaves_like 'a token that expires today'
2017-08-04 09:17:20 -04:00
end
2020-12-03 13:10:10 -05:00
end
context 'using User login' do
let ( :user ) { create ( :user ) }
let ( :headers ) { { authorization : credentials ( user . username , user . password ) } }
2017-08-04 09:17:20 -04:00
2018-12-17 17:52:17 -05:00
subject! { get '/jwt/auth' , params : parameters , headers : headers }
2017-05-31 09:55:12 -04:00
2022-09-14 20:14:10 -04:00
it { expect ( service_class ) . to have_received ( :new ) . with ( nil , user , ActionController :: Parameters . new ( parameters . merge ( auth_type : :gitlab_or_ldap ) ) . permit! ) }
2020-03-26 14:08:03 -04:00
it_behaves_like 'rejecting a blocked user'
2016-05-14 20:45:48 -04:00
2020-12-03 13:10:10 -05:00
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-05-14 20:45:48 -04:00
2020-12-03 13:10:10 -05:00
let ( :service_parameters ) do
ActionController :: Parameters . new ( { service : service_name , scopes : %w( scope1 scope2 ) } ) . permit!
end
2016-09-15 16:17:12 -04:00
2022-09-14 20:14:10 -04:00
it { expect ( service_class ) . to have_received ( :new ) . with ( nil , user , service_parameters . merge ( auth_type : :gitlab_or_ldap ) ) }
2020-03-26 14:08:03 -04:00
2020-12-03 13:10:10 -05:00
it_behaves_like 'user logging'
2018-07-13 13:45:07 -04:00
end
2020-12-03 13:10:10 -05:00
context 'when user has 2FA enabled' do
let ( :user ) { create ( :user , :two_factor ) }
context 'without personal token' do
2022-08-30 14:09:50 -04:00
it_behaves_like 'with invalid credentials'
2020-12-03 13:10:10 -05:00
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-07-13 13:45:07 -04:00
end
2020-12-03 13:10:10 -05:00
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-11 11:09:37 -04:00
2020-12-03 13:10:10 -05:00
expect ( response ) . to have_gitlab_http_status ( :ok )
end
2018-07-13 13:45:07 -04:00
end
2020-12-03 13:10:10 -05:00
context 'using invalid login' do
let ( :headers ) { { authorization : credentials ( 'invalid' , 'password' ) } }
2022-08-30 14:09:50 -04:00
let ( :subject ) { get '/jwt/auth' , params : parameters , headers : headers }
2016-09-15 16:17:12 -04:00
2020-12-03 13:10:10 -05:00
context 'when internal auth is enabled' do
2022-08-30 14:09:50 -04:00
it_behaves_like 'with invalid credentials'
2016-09-15 16:17:12 -04:00
end
2020-12-03 13:10:10 -05:00
context 'when internal auth is disabled' do
2021-03-17 20:08:58 -04:00
before do
stub_application_setting ( password_authentication_enabled_for_git : false )
end
2022-08-30 14:09:50 -04:00
it_behaves_like 'with invalid credentials'
2016-09-15 16:17:12 -04:00
end
end
2020-12-03 13:10:10 -05:00
end
2019-07-20 09:30:26 -04:00
2020-12-03 13:10:10 -05:00
context 'when using unauthenticated request' do
it 'accepts the authorization attempt' do
get '/jwt/auth' , params : parameters
2019-07-20 09:30:26 -04:00
2020-01-27 10:08:51 -05:00
expect ( response ) . to have_gitlab_http_status ( :ok )
2019-07-20 09:30:26 -04:00
end
2020-12-03 13:10:10 -05:00
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-05-14 20:45:48 -04:00
end
2020-12-03 13:10:10 -05:00
context 'unknown service' do
subject! { get '/jwt/auth' , params : { service : 'unknown' } }
2016-05-14 20:45:48 -04:00
2020-12-03 13:10:10 -05:00
it { expect ( response ) . to have_gitlab_http_status ( :not_found ) }
end
2017-06-07 15:49:45 -04:00
2020-12-03 13:10:10 -05:00
def credentials ( login , password )
ActionController :: HttpAuthentication :: Basic . encode_credentials ( login , password )
end
end
2016-05-14 20:45:48 -04:00
2020-12-03 13:10:10 -05:00
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-08-04 05:08:21 -04:00
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 ) }
2020-12-03 13:10:10 -05:00
let_it_be ( :service_name ) { 'dependency_proxy' }
2021-06-28 23:07:32 -04:00
2020-12-03 13:10:10 -05:00
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-06-07 15:49:45 -04:00
2020-12-03 13:10:10 -05:00
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-06-07 15:49:45 -04:00
end
2016-05-14 20:45:48 -04:00
end
2020-12-03 13:10:10 -05:00
context 'with personal access token' do
let ( :credential_user ) { nil }
let ( :credential_password ) { personal_access_token . token }
2016-11-08 13:37:15 -05:00
2020-12-03 13:10:10 -05:00
it_behaves_like 'with valid credentials'
2022-08-16 17:12:07 -04:00
it_behaves_like 'a token that expires today'
2016-11-08 13:37:15 -05:00
end
2020-12-03 13:10:10 -05:00
context 'with user credentials token' do
let ( :credential_user ) { user . username }
let ( :credential_password ) { user . password }
2016-11-08 13:37:15 -05:00
2020-12-03 13:10:10 -05:00
it_behaves_like 'with valid credentials'
2016-11-08 13:37:15 -05:00
end
2020-12-03 13:10:10 -05:00
context 'with group deploy token' do
let ( :credential_user ) { group_deploy_token . username }
let ( :credential_password ) { group_deploy_token . token }
2016-05-14 20:45:48 -04:00
2021-08-04 05:08:21 -04:00
it_behaves_like 'with valid credentials'
2020-12-03 13:10:10 -05:00
end
context 'with project deploy token' do
let ( :credential_user ) { project_deploy_token . username }
let ( :credential_password ) { project_deploy_token . token }
2021-04-28 11:09:35 -04:00
it_behaves_like 'returning response status' , :forbidden
2020-12-03 13:10:10 -05:00
end
2021-08-04 05:08:21 -04:00
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
2020-12-03 13:10:10 -05:00
context 'with invalid credentials' do
let ( :credential_user ) { 'foo' }
let ( :credential_password ) { 'bar' }
2021-04-28 11:09:35 -04:00
it_behaves_like 'returning response status' , :unauthorized
2020-12-03 13:10:10 -05:00
end
2016-05-14 20:45:48 -04:00
end
2016-05-15 01:33:06 -04:00
def credentials ( login , password )
2016-05-14 20:45:48 -04:00
ActionController :: HttpAuthentication :: Basic . encode_credentials ( login , password )
end
end