1# frozen_string_literal: true 2 3require 'spec_helper' 4 5RSpec.describe DeployToken do 6 subject(:deploy_token) { create(:deploy_token) } 7 8 it { is_expected.to have_many :project_deploy_tokens } 9 it { is_expected.to have_many(:projects).through(:project_deploy_tokens) } 10 it { is_expected.to have_many :group_deploy_tokens } 11 it { is_expected.to have_many(:groups).through(:group_deploy_tokens) } 12 13 it_behaves_like 'having unique enum values' 14 15 describe 'validations' do 16 let(:username_format_message) { "can contain only letters, digits, '_', '-', '+', and '.'" } 17 18 it { is_expected.to validate_length_of(:username).is_at_most(255) } 19 it { is_expected.to allow_value('GitLab+deploy_token-3.14').for(:username) } 20 it { is_expected.not_to allow_value('<script>').for(:username).with_message(username_format_message) } 21 it { is_expected.not_to allow_value('').for(:username).with_message(username_format_message) } 22 it { is_expected.to validate_presence_of(:deploy_token_type) } 23 end 24 25 shared_examples 'invalid group deploy token' do 26 context 'revoked' do 27 before do 28 deploy_token.update_column(:revoked, true) 29 end 30 31 it { is_expected.to eq(false) } 32 end 33 34 context 'expired' do 35 before do 36 deploy_token.update!(expires_at: Date.today - 1.month) 37 end 38 39 it { is_expected.to eq(false) } 40 end 41 42 context 'project type' do 43 before do 44 deploy_token.update_column(:deploy_token_type, 2) 45 end 46 47 it { is_expected.to eq(false) } 48 end 49 end 50 51 describe 'deploy_token_type validations' do 52 context 'when a deploy token is associated to a group' do 53 it 'does not allow setting a project to it' do 54 group_token = create(:deploy_token, :group) 55 group_token.projects << build(:project) 56 57 expect(group_token).not_to be_valid 58 expect(group_token.errors.full_messages).to include('Deploy token cannot have projects assigned') 59 end 60 end 61 62 context 'when a deploy token is associated to a project' do 63 it 'does not allow setting a group to it' do 64 project_token = create(:deploy_token) 65 project_token.groups << build(:group) 66 67 expect(project_token).not_to be_valid 68 expect(project_token.errors.full_messages).to include('Deploy token cannot have groups assigned') 69 end 70 end 71 end 72 73 describe '#ensure_token' do 74 it 'ensures a token' do 75 deploy_token.token = nil 76 deploy_token.save! 77 78 expect(deploy_token.token).not_to be_empty 79 end 80 end 81 82 describe '#ensure_at_least_one_scope' do 83 context 'with at least one scope' do 84 it 'is valid' do 85 is_expected.to be_valid 86 end 87 end 88 89 context 'with no scopes' do 90 it 'is invalid' do 91 deploy_token = build(:deploy_token, read_repository: false, read_registry: false, write_registry: false) 92 93 expect(deploy_token).not_to be_valid 94 expect(deploy_token.errors[:base].first).to eq("Scopes can't be blank") 95 end 96 end 97 end 98 99 describe '#valid_for_dependency_proxy?' do 100 let_it_be_with_reload(:deploy_token) { create(:deploy_token, :group, :dependency_proxy_scopes) } 101 102 subject { deploy_token.valid_for_dependency_proxy? } 103 104 it { is_expected.to eq(true) } 105 106 it_behaves_like 'invalid group deploy token' 107 108 context 'insufficient scopes' do 109 before do 110 deploy_token.update_column(:write_registry, false) 111 end 112 113 it { is_expected.to eq(false) } 114 end 115 end 116 117 describe '#has_access_to_group?' do 118 let_it_be(:group) { create(:group) } 119 let_it_be_with_reload(:deploy_token) { create(:deploy_token, :group) } 120 let_it_be(:group_deploy_token) { create(:group_deploy_token, group: group, deploy_token: deploy_token) } 121 122 let(:test_group) { group } 123 124 subject { deploy_token.has_access_to_group?(test_group) } 125 126 it { is_expected.to eq(true) } 127 128 it_behaves_like 'invalid group deploy token' 129 130 context 'for a sub group' do 131 let(:test_group) { create(:group, parent: group) } 132 133 it { is_expected.to eq(true) } 134 end 135 136 context 'for a different group' do 137 let(:test_group) { create(:group) } 138 139 it { is_expected.to eq(false) } 140 end 141 end 142 143 describe '#scopes' do 144 context 'with all the scopes' do 145 let_it_be(:deploy_token) { create(:deploy_token, :all_scopes) } 146 147 it 'returns scopes assigned to DeployToken' do 148 expect(deploy_token.scopes).to eq(DeployToken::AVAILABLE_SCOPES) 149 end 150 end 151 152 context 'with only one scope' do 153 it 'returns scopes assigned to DeployToken' do 154 deploy_token = create(:deploy_token, read_registry: false, write_registry: false) 155 expect(deploy_token.scopes).to eq([:read_repository]) 156 end 157 end 158 end 159 160 describe '#revoke!' do 161 it 'updates revoke attribute' do 162 deploy_token.revoke! 163 expect(deploy_token.revoked?).to be_truthy 164 end 165 end 166 167 describe "#active?" do 168 context "when it has been revoked" do 169 it 'returns false' do 170 deploy_token.revoke! 171 expect(deploy_token.active?).to be_falsy 172 end 173 end 174 175 context "when it hasn't been revoked and is not expired" do 176 it 'returns true' do 177 expect(deploy_token.active?).to be_truthy 178 end 179 end 180 181 context "when it hasn't been revoked and is expired" do 182 it 'returns true' do 183 deploy_token.update_attribute(:expires_at, Date.today - 5.days) 184 expect(deploy_token.active?).to be_falsy 185 end 186 end 187 188 context "when it hasn't been revoked and has no expiry" do 189 let(:deploy_token) { create(:deploy_token, expires_at: nil) } 190 191 it 'returns true' do 192 expect(deploy_token.active?).to be_truthy 193 end 194 end 195 end 196 197 # override the default PolicyActor implementation that always returns false 198 describe "#deactivated?" do 199 context "when it has been revoked" do 200 it 'returns true' do 201 deploy_token.revoke! 202 203 expect(deploy_token.deactivated?).to be_truthy 204 end 205 end 206 207 context "when it hasn't been revoked and is not expired" do 208 it 'returns false' do 209 expect(deploy_token.deactivated?).to be_falsy 210 end 211 end 212 213 context "when it hasn't been revoked and is expired" do 214 it 'returns false' do 215 deploy_token.update_attribute(:expires_at, Date.today - 5.days) 216 217 expect(deploy_token.deactivated?).to be_truthy 218 end 219 end 220 221 context "when it hasn't been revoked and has no expiry" do 222 let(:deploy_token) { create(:deploy_token, expires_at: nil) } 223 224 it 'returns false' do 225 expect(deploy_token.deactivated?).to be_falsy 226 end 227 end 228 end 229 230 describe '#username' do 231 context 'persisted records' do 232 it 'returns a default username if none is set' do 233 expect(deploy_token.username).to eq("gitlab+deploy-token-#{deploy_token.id}") 234 end 235 236 it 'returns the username provided if one is set' do 237 deploy_token = create(:deploy_token, username: 'deployer') 238 239 expect(deploy_token.username).to eq('deployer') 240 end 241 end 242 243 context 'new records' do 244 it 'returns nil if no username is set' do 245 deploy_token = build(:deploy_token) 246 247 expect(deploy_token.username).to be_nil 248 end 249 250 it 'returns the username provided if one is set' do 251 deploy_token = build(:deploy_token, username: 'deployer') 252 253 expect(deploy_token.username).to eq('deployer') 254 end 255 end 256 end 257 258 describe '#holder' do 259 subject { deploy_token.holder } 260 261 context 'when the token is of project type' do 262 it 'returns the relevant holder token' do 263 expect(subject).to eq(deploy_token.project_deploy_tokens.first) 264 end 265 end 266 267 context 'when the token is of group type' do 268 let(:group) { create(:group) } 269 let(:deploy_token) { create(:deploy_token, :group) } 270 271 it 'returns the relevant holder token' do 272 expect(subject).to eq(deploy_token.group_deploy_tokens.first) 273 end 274 end 275 end 276 277 describe '#has_access_to?' do 278 let(:project) { create(:project) } 279 280 subject { deploy_token.has_access_to?(project) } 281 282 context 'when a project is not passed in' do 283 let(:project) { nil } 284 285 it { is_expected.to be_falsy } 286 end 287 288 context 'when a project is passed in' do 289 context 'when deploy token is active and related to project' do 290 let(:deploy_token) { create(:deploy_token, projects: [project]) } 291 292 it { is_expected.to be_truthy } 293 end 294 295 context 'when deploy token is active but not related to project' do 296 let(:deploy_token) { create(:deploy_token) } 297 298 it { is_expected.to be_falsy } 299 end 300 301 context 'when deploy token is revoked and related to project' do 302 let(:deploy_token) { create(:deploy_token, :revoked, projects: [project]) } 303 304 it { is_expected.to be_falsy } 305 end 306 307 context 'when deploy token is revoked and not related to the project' do 308 let(:deploy_token) { create(:deploy_token, :revoked) } 309 310 it { is_expected.to be_falsy } 311 end 312 313 context 'and when the token is of group type' do 314 let_it_be(:group) { create(:group) } 315 316 let(:deploy_token) { create(:deploy_token, :group) } 317 318 before do 319 deploy_token.groups << group 320 end 321 322 context 'and the passed-in project does not belong to any group' do 323 it { is_expected.to be_falsy } 324 end 325 326 context 'and the passed-in project belongs to the token group' do 327 it 'is true' do 328 group.projects << project 329 330 is_expected.to be_truthy 331 end 332 end 333 334 context 'and the passed-in project belongs to a subgroup' do 335 let(:child_group) { create(:group, parent_id: group.id) } 336 let(:grandchild_group) { create(:group, parent_id: child_group.id) } 337 338 before do 339 grandchild_group.projects << project 340 end 341 342 context 'and the token group is an ancestor (grand-parent) of this group' do 343 it { is_expected.to be_truthy } 344 end 345 346 context 'and the token group is not ancestor of this group' do 347 let(:child2_group) { create(:group, parent_id: group.id) } 348 349 it 'is false' do 350 deploy_token.groups = [child2_group] 351 352 is_expected.to be_falsey 353 end 354 end 355 end 356 357 context 'and the passed-in project does not belong to the token group' do 358 it { is_expected.to be_falsy } 359 end 360 361 context 'and the project belongs to a group that is parent of the token group' do 362 let(:super_group) { create(:group) } 363 let(:deploy_token) { create(:deploy_token, :group) } 364 let(:group) { create(:group, parent_id: super_group.id) } 365 366 it 'is false' do 367 super_group.projects << project 368 369 is_expected.to be_falsey 370 end 371 end 372 end 373 374 context 'and the token is of project type' do 375 let(:deploy_token) { create(:deploy_token, projects: [project]) } 376 377 context 'and the passed-in project is the same as the token project' do 378 it { is_expected.to be_truthy } 379 end 380 381 context 'and the passed-in project is not the same as the token project' do 382 subject { deploy_token.has_access_to?(create(:project)) } 383 384 it { is_expected.to be_falsey } 385 end 386 end 387 end 388 end 389 390 describe '#expires_at' do 391 context 'when using Forever.date' do 392 let(:deploy_token) { create(:deploy_token, expires_at: nil) } 393 394 it 'returns nil' do 395 expect(deploy_token.expires_at).to be_nil 396 end 397 end 398 399 context 'when using a personalized date' do 400 let(:expires_at) { Date.today + 5.months } 401 let(:deploy_token) { create(:deploy_token, expires_at: expires_at) } 402 403 it 'returns the personalized date' do 404 expect(deploy_token.expires_at).to eq(expires_at) 405 end 406 end 407 end 408 409 describe '#expires_at=' do 410 context 'when passing nil' do 411 let(:deploy_token) { create(:deploy_token, expires_at: nil) } 412 413 it 'assigns Forever.date' do 414 expect(deploy_token.read_attribute(:expires_at)).to eq(Forever.date) 415 end 416 end 417 418 context 'when passing a value' do 419 let(:expires_at) { Date.today + 5.months } 420 let(:deploy_token) { create(:deploy_token, expires_at: expires_at) } 421 422 it 'respects the value' do 423 expect(deploy_token.read_attribute(:expires_at)).to eq(expires_at) 424 end 425 end 426 end 427 428 describe '.gitlab_deploy_token' do 429 let(:project) { create(:project ) } 430 431 subject { project.deploy_tokens.gitlab_deploy_token } 432 433 context 'with a gitlab deploy token associated' do 434 it 'returns the gitlab deploy token' do 435 deploy_token = create(:deploy_token, :gitlab_deploy_token, projects: [project]) 436 is_expected.to eq(deploy_token) 437 end 438 end 439 440 context 'with no gitlab deploy token associated' do 441 it 'returns nil' do 442 is_expected.to be_nil 443 end 444 end 445 end 446 447 describe '#accessible_projects' do 448 subject { deploy_token.accessible_projects } 449 450 context 'when a deploy token is associated to a project' do 451 let_it_be(:deploy_token) { create(:deploy_token, :project) } 452 453 it 'returns only projects directly associated with the token' do 454 expect(deploy_token).to receive(:projects) 455 456 subject 457 end 458 end 459 460 context 'when a deploy token is associated to a group' do 461 let_it_be(:group) { create(:group) } 462 let_it_be(:deploy_token) { create(:deploy_token, :group, groups: [group]) } 463 464 it 'returns all projects from the group' do 465 expect(group).to receive(:all_projects) 466 467 subject 468 end 469 end 470 end 471end 472