PageRenderTime 55ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/spec/lib/gitlab/auth_spec.rb

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