PageRenderTime 23ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/spec/lib/gitlab/auth_spec.rb

https://gitlab.com/YarNayar/gitlab-ce
Ruby | 319 lines | 246 code | 73 blank | 0 comment | 1 complexity | 280f424b2414039531a3f5610c05c5a6 MD5 | raw file
  1. require 'spec_helper'
  2. describe Gitlab::Auth, lib: true do
  3. let(:gl_auth) { described_class }
  4. describe 'constants' do
  5. it 'API_SCOPES contains all scopes for API access' do
  6. expect(subject::API_SCOPES).to eq [:api, :read_user]
  7. end
  8. it 'OPENID_SCOPES contains all scopes for OpenID Connect' do
  9. expect(subject::OPENID_SCOPES).to eq [:openid]
  10. end
  11. it 'DEFAULT_SCOPES contains all default scopes' do
  12. expect(subject::DEFAULT_SCOPES).to eq [:api]
  13. end
  14. it 'OPTIONAL_SCOPES contains all non-default scopes' do
  15. expect(subject::OPTIONAL_SCOPES).to eq %i[read_user read_registry openid]
  16. end
  17. it 'REGISTRY_SCOPES contains all registry related scopes' do
  18. expect(subject::REGISTRY_SCOPES).to eq %i[read_registry]
  19. end
  20. end
  21. describe 'find_for_git_client' do
  22. context 'build token' do
  23. subject { gl_auth.find_for_git_client('gitlab-ci-token', build.token, project: project, ip: 'ip') }
  24. context 'for running build' do
  25. let!(:build) { create(:ci_build, :running) }
  26. let(:project) { build.project }
  27. before do
  28. expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: 'gitlab-ci-token')
  29. end
  30. it 'recognises user-less build' do
  31. expect(subject).to eq(Gitlab::Auth::Result.new(nil, build.project, :ci, build_authentication_abilities))
  32. end
  33. it 'recognises user token' do
  34. build.update(user: create(:user))
  35. expect(subject).to eq(Gitlab::Auth::Result.new(build.user, build.project, :build, build_authentication_abilities))
  36. end
  37. end
  38. (HasStatus::AVAILABLE_STATUSES - ['running']).each do |build_status|
  39. context "for #{build_status} build" do
  40. let!(:build) { create(:ci_build, status: build_status) }
  41. let(:project) { build.project }
  42. before do
  43. expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: 'gitlab-ci-token')
  44. end
  45. it 'denies authentication' do
  46. expect(subject).to eq(Gitlab::Auth::Result.new)
  47. end
  48. end
  49. end
  50. end
  51. it 'recognizes other ci services' do
  52. project = create(:empty_project)
  53. project.create_drone_ci_service(active: true)
  54. project.drone_ci_service.update(token: 'token')
  55. expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: 'drone-ci-token')
  56. expect(gl_auth.find_for_git_client('drone-ci-token', 'token', project: project, ip: 'ip')).to eq(Gitlab::Auth::Result.new(nil, project, :ci, build_authentication_abilities))
  57. end
  58. it 'recognizes master passwords' do
  59. user = create(:user, password: 'password')
  60. expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: user.username)
  61. expect(gl_auth.find_for_git_client(user.username, 'password', project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(user, nil, :gitlab_or_ldap, full_authentication_abilities))
  62. end
  63. include_examples 'user login operation with unique ip limit' do
  64. let(:user) { create(:user, password: 'password') }
  65. def operation
  66. expect(gl_auth.find_for_git_client(user.username, 'password', project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(user, nil, :gitlab_or_ldap, full_authentication_abilities))
  67. end
  68. end
  69. context 'while using LFS authenticate' do
  70. it 'recognizes user lfs tokens' do
  71. user = create(:user)
  72. token = Gitlab::LfsToken.new(user).token
  73. expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: user.username)
  74. expect(gl_auth.find_for_git_client(user.username, token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(user, nil, :lfs_token, full_authentication_abilities))
  75. end
  76. it 'recognizes deploy key lfs tokens' do
  77. key = create(:deploy_key)
  78. token = Gitlab::LfsToken.new(key).token
  79. expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: "lfs+deploy-key-#{key.id}")
  80. expect(gl_auth.find_for_git_client("lfs+deploy-key-#{key.id}", token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(key, nil, :lfs_deploy_token, read_authentication_abilities))
  81. end
  82. it 'does not try password auth before oauth' do
  83. user = create(:user)
  84. token = Gitlab::LfsToken.new(user).token
  85. expect(gl_auth).not_to receive(:find_with_user_password)
  86. gl_auth.find_for_git_client(user.username, token, project: nil, ip: 'ip')
  87. end
  88. end
  89. context 'while using OAuth tokens as passwords' do
  90. let(:user) { create(:user) }
  91. let(:token_w_api_scope) { Doorkeeper::AccessToken.create!(application_id: application.id, resource_owner_id: user.id, scopes: 'api') }
  92. let(:application) { Doorkeeper::Application.create!(name: 'MyApp', redirect_uri: 'https://app.com', owner: user) }
  93. it 'succeeds for OAuth tokens with the `api` scope' do
  94. expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: 'oauth2')
  95. expect(gl_auth.find_for_git_client("oauth2", token_w_api_scope.token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(user, nil, :oauth, full_authentication_abilities))
  96. end
  97. it 'fails for OAuth tokens with other scopes' do
  98. token = Doorkeeper::AccessToken.create!(application_id: application.id, resource_owner_id: user.id, scopes: 'read_user')
  99. expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: 'oauth2')
  100. expect(gl_auth.find_for_git_client("oauth2", token.token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(nil, nil))
  101. end
  102. it 'does not try password auth before oauth' do
  103. expect(gl_auth).not_to receive(:find_with_user_password)
  104. gl_auth.find_for_git_client("oauth2", token_w_api_scope.token, project: nil, ip: 'ip')
  105. end
  106. end
  107. context 'while using personal access tokens as passwords' do
  108. it 'succeeds for personal access tokens with the `api` scope' do
  109. personal_access_token = create(:personal_access_token, scopes: ['api'])
  110. expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: '')
  111. expect(gl_auth.find_for_git_client('', personal_access_token.token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(personal_access_token.user, nil, :personal_token, full_authentication_abilities))
  112. end
  113. it 'succeeds for personal access tokens with the `read_registry` scope' do
  114. personal_access_token = create(:personal_access_token, scopes: ['read_registry'])
  115. expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: '')
  116. expect(gl_auth.find_for_git_client('', personal_access_token.token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(personal_access_token.user, nil, :personal_token, [:read_container_image]))
  117. end
  118. it 'succeeds if it is an impersonation token' do
  119. impersonation_token = create(:personal_access_token, :impersonation, scopes: ['api'])
  120. expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: '')
  121. expect(gl_auth.find_for_git_client('', impersonation_token.token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(impersonation_token.user, nil, :personal_token, full_authentication_abilities))
  122. end
  123. it 'limits abilities based on scope' do
  124. personal_access_token = create(:personal_access_token, scopes: ['read_user'])
  125. expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: '')
  126. expect(gl_auth.find_for_git_client('', personal_access_token.token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(personal_access_token.user, nil, :personal_token, []))
  127. end
  128. it 'fails if password is nil' do
  129. expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: '')
  130. expect(gl_auth.find_for_git_client('', nil, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(nil, nil))
  131. end
  132. end
  133. context 'while using regular user and password' do
  134. it 'falls through lfs authentication' do
  135. user = create(
  136. :user,
  137. username: 'normal_user',
  138. password: 'my-secret'
  139. )
  140. expect(gl_auth.find_for_git_client(user.username, user.password, project: nil, ip: 'ip'))
  141. .to eq(Gitlab::Auth::Result.new(user, nil, :gitlab_or_ldap, full_authentication_abilities))
  142. end
  143. it 'falls through oauth authentication when the username is oauth2' do
  144. user = create(
  145. :user,
  146. username: 'oauth2',
  147. password: 'my-secret'
  148. )
  149. expect(gl_auth.find_for_git_client(user.username, user.password, project: nil, ip: 'ip'))
  150. .to eq(Gitlab::Auth::Result.new(user, nil, :gitlab_or_ldap, full_authentication_abilities))
  151. end
  152. end
  153. it 'returns double nil for invalid credentials' do
  154. login = 'foo'
  155. expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: login)
  156. expect(gl_auth.find_for_git_client(login, 'bar', project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new)
  157. end
  158. it 'throws an error suggesting user create a PAT when internal auth is disabled' do
  159. allow_any_instance_of(ApplicationSetting).to receive(:password_authentication_enabled?) { false }
  160. expect { gl_auth.find_for_git_client('foo', 'bar', project: nil, ip: 'ip') }.to raise_error(Gitlab::Auth::MissingPersonalTokenError)
  161. end
  162. end
  163. describe 'find_with_user_password' do
  164. let!(:user) do
  165. create(:user,
  166. username: username,
  167. password: password,
  168. password_confirmation: password)
  169. end
  170. let(:username) { 'John' } # username isn't lowercase, test this
  171. let(:password) { 'my-secret' }
  172. it "finds user by valid login/password" do
  173. expect( gl_auth.find_with_user_password(username, password) ).to eql user
  174. end
  175. it 'finds user by valid email/password with case-insensitive email' do
  176. expect(gl_auth.find_with_user_password(user.email.upcase, password)).to eql user
  177. end
  178. it 'finds user by valid username/password with case-insensitive username' do
  179. expect(gl_auth.find_with_user_password(username.upcase, password)).to eql user
  180. end
  181. it "does not find user with invalid password" do
  182. password = 'wrong'
  183. expect( gl_auth.find_with_user_password(username, password) ).not_to eql user
  184. end
  185. it "does not find user with invalid login" do
  186. user = 'wrong'
  187. expect( gl_auth.find_with_user_password(username, password) ).not_to eql user
  188. end
  189. include_examples 'user login operation with unique ip limit' do
  190. def operation
  191. expect(gl_auth.find_with_user_password(username, password)).to eq(user)
  192. end
  193. end
  194. it "does not find user in blocked state" do
  195. user.block
  196. expect( gl_auth.find_with_user_password(username, password) ).not_to eql user
  197. end
  198. it "does not find user in ldap_blocked state" do
  199. user.ldap_block
  200. expect( gl_auth.find_with_user_password(username, password) ).not_to eql user
  201. end
  202. context "with ldap enabled" do
  203. before do
  204. allow(Gitlab::LDAP::Config).to receive(:enabled?).and_return(true)
  205. end
  206. it "tries to autheticate with db before ldap" do
  207. expect(Gitlab::LDAP::Authentication).not_to receive(:login)
  208. gl_auth.find_with_user_password(username, password)
  209. end
  210. it "uses ldap as fallback to for authentication" do
  211. expect(Gitlab::LDAP::Authentication).to receive(:login)
  212. gl_auth.find_with_user_password('ldap_user', 'password')
  213. end
  214. end
  215. context "with sign-in disabled" do
  216. before do
  217. stub_application_setting(password_authentication_enabled: false)
  218. end
  219. it "does not find user by valid login/password" do
  220. expect(gl_auth.find_with_user_password(username, password)).to be_nil
  221. end
  222. end
  223. end
  224. private
  225. def build_authentication_abilities
  226. [
  227. :read_project,
  228. :build_download_code,
  229. :build_read_container_image,
  230. :build_create_container_image
  231. ]
  232. end
  233. def read_authentication_abilities
  234. [
  235. :read_project,
  236. :download_code,
  237. :read_container_image
  238. ]
  239. end
  240. def full_authentication_abilities
  241. read_authentication_abilities + [
  242. :push_code,
  243. :create_container_image
  244. ]
  245. end
  246. end