Merge tag 'debian/12.6.7-1' into buster-fasttrack
gitlab Debian release 12.6.7-1
This commit is contained in:
commit
b399c68f5c
102 changed files with 1905 additions and 335 deletions
|
@ -1,5 +1,17 @@
|
|||
Please view this file on the master branch, on stable branches it's out of date.
|
||||
|
||||
## 12.6.6
|
||||
|
||||
- No changes.
|
||||
|
||||
## 12.6.5
|
||||
|
||||
- No changes.
|
||||
|
||||
## 12.6.4
|
||||
|
||||
- No changes.
|
||||
|
||||
## 12.6.3
|
||||
|
||||
- No changes.
|
||||
|
|
39
CHANGELOG.md
39
CHANGELOG.md
|
@ -2,6 +2,45 @@
|
|||
documentation](doc/development/changelog.md) for instructions on adding your own
|
||||
entry.
|
||||
|
||||
## 12.6.7
|
||||
|
||||
### Security (1 change)
|
||||
|
||||
- Fix ProjectAuthorization calculation for shared groups.
|
||||
|
||||
|
||||
## 12.6.6
|
||||
|
||||
### Security (1 change)
|
||||
|
||||
- Update workhorse to v8.20.0.
|
||||
|
||||
|
||||
## 12.6.5
|
||||
|
||||
### Security (19 changes, 1 of them is from the community)
|
||||
|
||||
- Update rack-cors to 1.0.6.
|
||||
- Update rdoc to 6.1.2.
|
||||
- Bump rubyzip to 2.0.0. (Utkarsh Gupta)
|
||||
- Cleanup todos for users from a removed linked group.
|
||||
- Disable access to last_pipeline in commits API for users without read permissions.
|
||||
- Add constraint to group dependency proxy endpoint param.
|
||||
- Limit number of AsciiDoc includes per document.
|
||||
- Prevent API access for unconfirmed users.
|
||||
- Enforce permission check when counting activity events.
|
||||
- Prevent gafana integration token from being displayed as a plain text to other project maintainers, by only displaying a masked version of it.
|
||||
- Fix xss on frequent groups dropdown.
|
||||
- Fix XSS vulnerability on custom project templates form.
|
||||
- Protect internal CI builds from external overrides.
|
||||
- ImportExport::ExportService to require admin_project permission.
|
||||
- Make sure that only system notes where all references are visible to user are exposed in GraphQL API.
|
||||
- Disable caching of repository/files/:file_path/raw API endpoint.
|
||||
- Make cross-repository comparisons happen in the source repository.
|
||||
- Update excon to 0.71.1 to fix CVE-2019-16779.
|
||||
- Add workhorse request verification to package upload endpoints.
|
||||
|
||||
|
||||
## 12.6.4
|
||||
|
||||
### Security (1 change)
|
||||
|
|
|
@ -1 +1 @@
|
|||
8.18.0
|
||||
8.20.0
|
||||
|
|
4
Gemfile
4
Gemfile
|
@ -65,7 +65,7 @@ gem 'u2f', '~> 0.2.1'
|
|||
|
||||
# GitLab Pages
|
||||
gem 'validates_hostname', '~> 1.0.6'
|
||||
gem 'rubyzip', '~> 1.3.0', require: 'zip'
|
||||
gem 'rubyzip', '~> 2.0.0', require: 'zip'
|
||||
# GitLab Pages letsencrypt support
|
||||
gem 'acme-client', '~> 2.0.2'
|
||||
|
||||
|
@ -142,7 +142,7 @@ gem 'gitlab-markup', '~> 1.7.0'
|
|||
gem 'github-markup', '~> 1.7.0', require: 'github/markup'
|
||||
gem 'commonmarker', '~> 0.20'
|
||||
gem 'RedCloth', '~> 4.3.2'
|
||||
gem 'rdoc', '~> 6.0'
|
||||
gem 'rdoc', '~> 6.1.2'
|
||||
gem 'org-ruby', '~> 0.9.12'
|
||||
gem 'creole', '~> 0.5.0'
|
||||
gem 'wikicloth', '0.8.1'
|
||||
|
|
13
Gemfile.lock
13
Gemfile.lock
|
@ -260,7 +260,7 @@ GEM
|
|||
et-orbi (1.2.1)
|
||||
tzinfo
|
||||
eventmachine (1.2.7)
|
||||
excon (0.62.0)
|
||||
excon (0.71.1)
|
||||
execjs (2.6.0)
|
||||
expression_parser (0.9.0)
|
||||
extended-markdown-filter (0.6.0)
|
||||
|
@ -763,7 +763,8 @@ GEM
|
|||
rack (>= 0.4)
|
||||
rack-attack (6.2.0)
|
||||
rack (>= 1.0, < 3)
|
||||
rack-cors (1.0.2)
|
||||
rack-cors (1.0.6)
|
||||
rack (>= 1.6.0)
|
||||
rack-oauth2 (1.9.3)
|
||||
activesupport
|
||||
attr_required
|
||||
|
@ -820,7 +821,7 @@ GEM
|
|||
ffi (>= 1.0.6)
|
||||
msgpack (>= 0.4.3)
|
||||
optimist (>= 3.0.0)
|
||||
rdoc (6.0.4)
|
||||
rdoc (6.1.2)
|
||||
re2 (1.1.1)
|
||||
recaptcha (4.13.1)
|
||||
json
|
||||
|
@ -929,7 +930,7 @@ GEM
|
|||
sexp_processor (~> 4.9)
|
||||
rubyntlm (0.6.2)
|
||||
rubypants (0.2.0)
|
||||
rubyzip (1.3.0)
|
||||
rubyzip (2.0.0)
|
||||
rugged (0.28.4.1)
|
||||
safe_yaml (1.0.4)
|
||||
sanitize (4.6.6)
|
||||
|
@ -1299,7 +1300,7 @@ DEPENDENCIES
|
|||
raindrops (~> 0.18)
|
||||
rblineprof (~> 0.3.6)
|
||||
rbtrace (~> 0.4)
|
||||
rdoc (~> 6.0)
|
||||
rdoc (~> 6.1.2)
|
||||
re2 (~> 1.1.1)
|
||||
recaptcha (~> 4.11)
|
||||
redis (~> 4.0)
|
||||
|
@ -1323,7 +1324,7 @@ DEPENDENCIES
|
|||
ruby-prof (~> 1.0.0)
|
||||
ruby-progressbar
|
||||
ruby_parser (~> 3.8)
|
||||
rubyzip (~> 1.3.0)
|
||||
rubyzip (~> 2.0.0)
|
||||
rugged (~> 0.28)
|
||||
sanitize (~> 4.6)
|
||||
sassc-rails (~> 2.1.0)
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
12.6.4
|
||||
12.6.7
|
||||
|
|
|
@ -5,7 +5,7 @@ import AccessorUtilities from '~/lib/utils/accessor';
|
|||
import eventHub from '../event_hub';
|
||||
import store from '../store/';
|
||||
import { FREQUENT_ITEMS, STORAGE_KEY } from '../constants';
|
||||
import { isMobile, updateExistingFrequentItem } from '../utils';
|
||||
import { isMobile, updateExistingFrequentItem, sanitizeItem } from '../utils';
|
||||
import FrequentItemsSearchInput from './frequent_items_search_input.vue';
|
||||
import FrequentItemsList from './frequent_items_list.vue';
|
||||
import frequentItemsMixin from './frequent_items_mixin';
|
||||
|
@ -64,7 +64,9 @@ export default {
|
|||
this.fetchFrequentItems();
|
||||
}
|
||||
},
|
||||
logItemAccess(storageKey, item) {
|
||||
logItemAccess(storageKey, unsanitizedItem) {
|
||||
const item = sanitizeItem(unsanitizedItem);
|
||||
|
||||
if (!AccessorUtilities.isLocalStorageAccessSafe()) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<script>
|
||||
import FrequentItemsListItem from './frequent_items_list_item.vue';
|
||||
import frequentItemsMixin from './frequent_items_mixin';
|
||||
import { sanitizeItem } from '../utils';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -48,6 +49,9 @@ export default {
|
|||
? this.translations.itemListErrorMessage
|
||||
: this.translations.itemListEmptyMessage;
|
||||
},
|
||||
sanitizedItems() {
|
||||
return this.items.map(sanitizeItem);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -59,7 +63,7 @@ export default {
|
|||
{{ listEmptyMessage }}
|
||||
</li>
|
||||
<frequent-items-list-item
|
||||
v-for="item in items"
|
||||
v-for="item in sanitizedItems"
|
||||
v-else
|
||||
:key="item.id"
|
||||
:item-id="item.id"
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import _ from 'underscore';
|
||||
import bp from '~/breakpoints';
|
||||
import sanitize from 'sanitize-html';
|
||||
import { FREQUENT_ITEMS, HOUR_IN_MS } from './constants';
|
||||
|
||||
export const isMobile = () => {
|
||||
|
@ -47,3 +48,9 @@ export const updateExistingFrequentItem = (frequentItem, item) => {
|
|||
lastAccessedOn: accessedOverHourAgo ? Date.now() : frequentItem.lastAccessedOn,
|
||||
};
|
||||
};
|
||||
|
||||
export const sanitizeItem = item => ({
|
||||
...item,
|
||||
name: sanitize(item.name.toString(), { allowedTags: [] }),
|
||||
namespace: sanitize(item.namespace.toString(), { allowedTags: [] }),
|
||||
});
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import $ from 'jquery';
|
||||
import axios from './lib/utils/axios_utils';
|
||||
import Api from './api';
|
||||
import { escape } from 'lodash';
|
||||
import { normalizeHeaders } from './lib/utils/common_utils';
|
||||
import { __ } from '~/locale';
|
||||
|
||||
|
@ -75,10 +76,12 @@ const groupsSelect = () => {
|
|||
}
|
||||
},
|
||||
formatResult(object) {
|
||||
return `<div class='group-result'> <div class='group-name'>${object.full_name}</div> <div class='group-path'>${object.full_path}</div> </div>`;
|
||||
return `<div class='group-result'> <div class='group-name'>${escape(
|
||||
object.full_name,
|
||||
)}</div> <div class='group-path'>${object.full_path}</div> </div>`;
|
||||
},
|
||||
formatSelection(object) {
|
||||
return object.full_name;
|
||||
return escape(object.full_name);
|
||||
},
|
||||
dropdownCssClass: 'ajax-groups-dropdown select2-infinite',
|
||||
// we do not want to escape markup since we are displaying html in results
|
||||
|
|
|
@ -5,6 +5,7 @@ require 'fogbugz'
|
|||
|
||||
class ApplicationController < ActionController::Base
|
||||
include Gitlab::GonHelper
|
||||
include Gitlab::NoCacheHeaders
|
||||
include GitlabRoutingHelper
|
||||
include PageLayoutHelper
|
||||
include SafeParamsHelper
|
||||
|
@ -54,7 +55,6 @@ class ApplicationController < ActionController::Base
|
|||
# Adds `no-store` to the DEFAULT_CACHE_CONTROL, to prevent security
|
||||
# concerns due to caching private data.
|
||||
DEFAULT_GITLAB_CACHE_CONTROL = "#{ActionDispatch::Http::Cache::Response::DEFAULT_CACHE_CONTROL}, no-store"
|
||||
DEFAULT_GITLAB_CONTROL_NO_CACHE = "#{DEFAULT_GITLAB_CACHE_CONTROL}, no-cache"
|
||||
|
||||
rescue_from Encoding::CompatibilityError do |exception|
|
||||
log_exception(exception)
|
||||
|
@ -246,9 +246,9 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
|
||||
def no_cache_headers
|
||||
headers['Cache-Control'] = DEFAULT_GITLAB_CONTROL_NO_CACHE
|
||||
headers['Pragma'] = 'no-cache' # HTTP 1.0 compatibility
|
||||
headers['Expires'] = 'Fri, 01 Jan 1990 00:00:00 GMT'
|
||||
DEFAULT_GITLAB_NO_CACHE_HEADERS.each do |k, v|
|
||||
headers[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
def default_headers
|
||||
|
|
|
@ -19,7 +19,7 @@ class DashboardController < Dashboard::ApplicationController
|
|||
|
||||
format.json do
|
||||
load_events
|
||||
pager_json("events/_events", @events.count)
|
||||
pager_json('events/_events', @events.count { |event| event.visible_to_user?(current_user) })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -37,6 +37,7 @@ class DashboardController < Dashboard::ApplicationController
|
|||
@events = EventCollection
|
||||
.new(projects, offset: params[:offset].to_i, filter: event_filter)
|
||||
.to_a
|
||||
.map(&:present)
|
||||
|
||||
Events::RenderService.new(current_user).execute(@events)
|
||||
end
|
||||
|
|
|
@ -91,7 +91,7 @@ class GroupsController < Groups::ApplicationController
|
|||
|
||||
format.json do
|
||||
load_events
|
||||
pager_json("events/_events", @events.count)
|
||||
pager_json("events/_events", @events.count { |event| event.visible_to_user?(current_user) })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -209,8 +209,9 @@ class GroupsController < Groups::ApplicationController
|
|||
.includes(:namespace)
|
||||
|
||||
@events = EventCollection
|
||||
.new(projects, offset: params[:offset].to_i, filter: event_filter, groups: groups)
|
||||
.to_a
|
||||
.new(projects, offset: params[:offset].to_i, filter: event_filter, groups: groups)
|
||||
.to_a
|
||||
.map(&:present)
|
||||
|
||||
Events::RenderService
|
||||
.new(current_user)
|
||||
|
|
|
@ -119,7 +119,7 @@ class ProjectsController < Projects::ApplicationController
|
|||
format.html
|
||||
format.json do
|
||||
load_events
|
||||
pager_json('events/_events', @events.count)
|
||||
pager_json('events/_events', @events.count { |event| event.visible_to_user?(current_user) })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -340,6 +340,7 @@ class ProjectsController < Projects::ApplicationController
|
|||
@events = EventCollection
|
||||
.new(projects, offset: params[:offset].to_i, filter: event_filter)
|
||||
.to_a
|
||||
.map(&:present)
|
||||
|
||||
Events::RenderService.new(current_user).execute(@events, atom_request: request.format.atom?)
|
||||
end
|
||||
|
|
|
@ -368,8 +368,8 @@ module ProjectsHelper
|
|||
@project.grafana_integration&.grafana_url
|
||||
end
|
||||
|
||||
def grafana_integration_token
|
||||
@project.grafana_integration&.token
|
||||
def grafana_integration_masked_token
|
||||
@project.grafana_integration&.masked_token
|
||||
end
|
||||
|
||||
def grafana_integration_enabled?
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class GenericCommitStatus < CommitStatus
|
||||
EXTERNAL_STAGE_IDX = 1_000_000
|
||||
|
||||
before_validation :set_default_values
|
||||
|
||||
validates :target_url, addressable_url: true,
|
||||
length: { maximum: 255 },
|
||||
allow_nil: true
|
||||
validate :name_uniqueness_across_types, unless: :importing?
|
||||
|
||||
# GitHub compatible API
|
||||
alias_attribute :context, :name
|
||||
|
@ -13,7 +16,7 @@ class GenericCommitStatus < CommitStatus
|
|||
def set_default_values
|
||||
self.context ||= 'default'
|
||||
self.stage ||= 'external'
|
||||
self.stage_idx ||= 1000000
|
||||
self.stage_idx ||= EXTERNAL_STAGE_IDX
|
||||
end
|
||||
|
||||
def tags
|
||||
|
@ -25,4 +28,14 @@ class GenericCommitStatus < CommitStatus
|
|||
.new(self, current_user)
|
||||
.fabricate!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def name_uniqueness_across_types
|
||||
return if !pipeline || name.blank?
|
||||
|
||||
if pipeline.statuses.by_name(name).where.not(type: type).exists?
|
||||
errors.add(:name, :taken)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,11 +8,13 @@ class GrafanaIntegration < ApplicationRecord
|
|||
algorithm: 'aes-256-gcm',
|
||||
key: Settings.attr_encrypted_db_key_base_32
|
||||
|
||||
before_validation :check_token_changes
|
||||
|
||||
validates :grafana_url,
|
||||
length: { maximum: 1024 },
|
||||
addressable_url: { enforce_sanitization: true, ascii_only: true }
|
||||
|
||||
validates :token, :project, presence: true
|
||||
validates :encrypted_token, :project, presence: true
|
||||
|
||||
validates :enabled, inclusion: { in: [true, false] }
|
||||
|
||||
|
@ -23,4 +25,28 @@ class GrafanaIntegration < ApplicationRecord
|
|||
|
||||
@client ||= ::Grafana::Client.new(api_url: grafana_url.chomp('/'), token: token)
|
||||
end
|
||||
|
||||
def masked_token
|
||||
mask(encrypted_token)
|
||||
end
|
||||
|
||||
def masked_token_was
|
||||
mask(encrypted_token_was)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def token
|
||||
decrypt(:token, encrypted_token)
|
||||
end
|
||||
|
||||
def check_token_changes
|
||||
return unless [encrypted_token_was, masked_token_was].include?(token)
|
||||
|
||||
clear_attribute_changes [:token, :encrypted_token, :encrypted_token_iv]
|
||||
end
|
||||
|
||||
def mask(token)
|
||||
token&.squish&.gsub(/./, '*')
|
||||
end
|
||||
end
|
||||
|
|
|
@ -551,7 +551,8 @@ class Note < ApplicationRecord
|
|||
# if they are not equal, then there are private/confidential references as well
|
||||
user_visible_reference_count > 0 && user_visible_reference_count == total_reference_count
|
||||
else
|
||||
referenced_mentionables(user).any?
|
||||
refs = all_references(user)
|
||||
refs.all.any? && refs.stateful_not_visible_counter == 0
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -2333,6 +2333,10 @@ class Project < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
def template_source?
|
||||
false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def closest_namespace_setting(name)
|
||||
|
|
|
@ -21,6 +21,14 @@ class BasePolicy < DeclarativePolicy::Base
|
|||
with_options scope: :user, score: 0
|
||||
condition(:deactivated) { @user&.deactivated? }
|
||||
|
||||
desc "User email is unconfirmed or user account is locked"
|
||||
with_options scope: :user, score: 0
|
||||
condition(:inactive) do
|
||||
Feature.enabled?(:inactive_policy_condition, default_enabled: true) &&
|
||||
@user &&
|
||||
!@user&.active_for_authentication?
|
||||
end
|
||||
|
||||
with_options scope: :user, score: 0
|
||||
condition(:external_user) { @user.nil? || @user.external? }
|
||||
|
||||
|
|
|
@ -36,6 +36,13 @@ class GlobalPolicy < BasePolicy
|
|||
enable :use_slash_commands
|
||||
end
|
||||
|
||||
rule { inactive }.policy do
|
||||
prevent :log_in
|
||||
prevent :access_api
|
||||
prevent :access_git
|
||||
prevent :use_slash_commands
|
||||
end
|
||||
|
||||
rule { blocked | internal }.policy do
|
||||
prevent :log_in
|
||||
prevent :access_api
|
||||
|
|
|
@ -3,6 +3,18 @@
|
|||
class EventPresenter < Gitlab::View::Presenter::Delegated
|
||||
presents :event
|
||||
|
||||
def initialize(subject, **attributes)
|
||||
super
|
||||
|
||||
@visible_to_user_cache = ActiveSupport::Cache::MemoryStore.new
|
||||
end
|
||||
|
||||
# Caching `visible_to_user?` method in the presenter beause it might be called multiple times.
|
||||
def visible_to_user?(user = nil)
|
||||
@visible_to_user_cache.fetch(user&.id) { super(user) }
|
||||
end
|
||||
|
||||
# implement cache here
|
||||
def resource_parent_name
|
||||
resource_parent&.full_name || ''
|
||||
end
|
||||
|
|
|
@ -18,7 +18,7 @@ class CompareService
|
|||
return unless raw_compare && raw_compare.base && raw_compare.head
|
||||
|
||||
Compare.new(raw_compare,
|
||||
target_project,
|
||||
start_project,
|
||||
base_sha: base_sha,
|
||||
straight: straight)
|
||||
end
|
||||
|
|
|
@ -6,6 +6,12 @@ module Projects
|
|||
def execute(group_link)
|
||||
return false unless group_link
|
||||
|
||||
if group_link.project.private?
|
||||
TodosDestroyer::ProjectPrivateWorker.perform_in(Todo::WAIT_FOR_DELETE, project.id)
|
||||
else
|
||||
TodosDestroyer::ConfidentialIssueWorker.perform_in(Todo::WAIT_FOR_DELETE, nil, project.id)
|
||||
end
|
||||
|
||||
group_link.destroy
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,6 +4,12 @@ module Projects
|
|||
module ImportExport
|
||||
class ExportService < BaseService
|
||||
def execute(after_export_strategy = nil, options = {})
|
||||
unless project.template_source? || can?(current_user, :admin_project, project)
|
||||
raise ::Gitlab::ImportExport::Error.new(
|
||||
"User with ID: %s does not have permission to Project %s with ID: %s." %
|
||||
[current_user.id, project.name, project.id])
|
||||
end
|
||||
|
||||
@shared = project.import_export_shared
|
||||
|
||||
save_all!
|
||||
|
|
|
@ -19,8 +19,10 @@ module Users
|
|||
LEASE_TIMEOUT = 1.minute.to_i
|
||||
|
||||
# user - The User for which to refresh the authorized projects.
|
||||
def initialize(user)
|
||||
def initialize(user, incorrect_auth_found_callback: nil, missing_auth_found_callback: nil)
|
||||
@user = user
|
||||
@incorrect_auth_found_callback = incorrect_auth_found_callback
|
||||
@missing_auth_found_callback = missing_auth_found_callback
|
||||
|
||||
# We need an up to date User object that has access to all relations that
|
||||
# may have been created earlier. The only way to ensure this is to reload
|
||||
|
@ -55,6 +57,10 @@ module Users
|
|||
# rows not in the new list or with a different access level should be
|
||||
# removed.
|
||||
if !fresh[project_id] || fresh[project_id] != row.access_level
|
||||
if incorrect_auth_found_callback
|
||||
incorrect_auth_found_callback.call(project_id, row.access_level)
|
||||
end
|
||||
|
||||
array << row.project_id
|
||||
end
|
||||
end
|
||||
|
@ -63,6 +69,10 @@ module Users
|
|||
# rows not in the old list or with a different access level should be
|
||||
# added.
|
||||
if !current[project_id] || current[project_id].access_level != level
|
||||
if missing_auth_found_callback
|
||||
missing_auth_found_callback.call(project_id, level)
|
||||
end
|
||||
|
||||
array << [user.id, project_id, level]
|
||||
end
|
||||
end
|
||||
|
@ -104,5 +114,9 @@ module Users
|
|||
def fresh_authorizations
|
||||
Gitlab::ProjectAuthorizations.new(user).calculate
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :incorrect_auth_found_callback, :missing_auth_found_callback
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
.js-grafana-integration{ data: { operations_settings_endpoint: project_settings_operations_path(@project),
|
||||
grafana_integration: { url: grafana_integration_url, token: grafana_integration_token, enabled: grafana_integration_enabled?.to_s } } }
|
||||
grafana_integration: { url: grafana_integration_url, token: grafana_integration_masked_token, enabled: grafana_integration_enabled?.to_s } } }
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ScheduleRecalculateProjectAuthorizations < ActiveRecord::Migration[5.1]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
MIGRATION = 'RecalculateProjectAuthorizations'
|
||||
BATCH_SIZE = 2_500
|
||||
DELAY_INTERVAL = 2.minutes.to_i
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
class Namespace < ActiveRecord::Base
|
||||
include ::EachBatch
|
||||
|
||||
self.table_name = 'namespaces'
|
||||
end
|
||||
|
||||
class ProjectAuthorization < ActiveRecord::Base
|
||||
include ::EachBatch
|
||||
|
||||
self.table_name = 'project_authorizations'
|
||||
end
|
||||
|
||||
def up
|
||||
say "Scheduling #{MIGRATION} jobs"
|
||||
|
||||
max_group_id = Namespace.where(type: 'Group').maximum(:id)
|
||||
project_authorizations = ProjectAuthorization.where('project_id <= ?', max_group_id)
|
||||
.select(:user_id)
|
||||
.distinct
|
||||
|
||||
project_authorizations.each_batch(of: BATCH_SIZE, column: :user_id) do |authorizations, index|
|
||||
delay = index * DELAY_INTERVAL
|
||||
user_ids = authorizations.map(&:user_id)
|
||||
BackgroundMigrationWorker.perform_in(delay, MIGRATION, [user_ids])
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
end
|
||||
end
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2019_12_16_183532) do
|
||||
ActiveRecord::Schema.define(version: 2020_02_04_113223) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "pg_trgm"
|
||||
|
|
17
debian/changelog
vendored
17
debian/changelog
vendored
|
@ -1,3 +1,20 @@
|
|||
gitlab (12.6.7-1) experimental; urgency=medium
|
||||
|
||||
[ Abhijith PA ]
|
||||
* New upstream version 12.6.6 (Fixes: CVE-2020-7973, CVE-2020-7968)
|
||||
* Remove patch 0760-bump-rubyzip.patch
|
||||
* Refresh patches.
|
||||
|
||||
[ Pirate Praveen ]
|
||||
* Update minimum version of ruby-excon and ruby-rack-cors
|
||||
* New upstream version 12.6.7
|
||||
* Relax ruby-rack-cors version in Gemfile
|
||||
* Add lines removed by mistake in an earlier commit
|
||||
* Relax dependency on rdoc (ruby 2.5 comes with rdoc 6.0)
|
||||
* Refresh patches again to relax all stable gems
|
||||
|
||||
-- Pirate Praveen <praveen@debian.org> Sat, 15 Feb 2020 14:12:32 +0100
|
||||
|
||||
gitlab (12.6.4-1+fto10+1) buster-fasttrack; urgency=medium
|
||||
|
||||
* Rebuild for buster-fasttrack.
|
||||
|
|
4
debian/control
vendored
4
debian/control
vendored
|
@ -97,7 +97,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends},
|
|||
# API
|
||||
ruby-grape (>= 1.1~),
|
||||
ruby-grape-entity (>= 0.7.1~),
|
||||
ruby-rack-cors (>= 1.0~),
|
||||
ruby-rack-cors (>= 1.0.6~),
|
||||
# GraphQL API
|
||||
ruby-graphql (>= 1.9.11~),
|
||||
ruby-graphiql-rails (>= 1.4.10~),
|
||||
|
@ -114,7 +114,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends},
|
|||
# for backups
|
||||
ruby-fog-aws (>= 3.5~),
|
||||
ruby-fog-core (>= 2.1~),
|
||||
ruby-excon (>= 0.60~),
|
||||
ruby-excon (>= 0.72~),
|
||||
ruby-fog-google (>= 1.9~),
|
||||
ruby-fog-local (>= 0.6~),
|
||||
ruby-fog-openstack (>= 1.0~),
|
||||
|
|
40
debian/patches/0050-relax-stable-libs.patch
vendored
40
debian/patches/0050-relax-stable-libs.patch
vendored
|
@ -16,15 +16,24 @@ gitlab Gemfile
|
|||
|
||||
# Supported DBs
|
||||
gem 'pg', '~> 1.1'
|
||||
@@ -29,25 +29,25 @@
|
||||
@@ -22,32 +22,32 @@
|
||||
gem 'grape-path-helpers', '~> 1.1'
|
||||
|
||||
gem 'faraday', '~> 0.12'
|
||||
-gem 'marginalia', '~> 1.8.0'
|
||||
+gem 'marginalia', '~> 1.8'
|
||||
|
||||
# Authentication libraries
|
||||
gem 'devise', '~> 4.6'
|
||||
gem 'doorkeeper', '~> 4.3'
|
||||
gem 'doorkeeper-openid_connect', '~> 1.5'
|
||||
gem 'omniauth', '~> 1.8'
|
||||
-gem 'omniauth-auth0', '~> 2.0.0'
|
||||
+gem 'omniauth-auth0', '~> 2.0'
|
||||
gem 'omniauth-azure-oauth2', '~> 0.0.9'
|
||||
gem 'omniauth-cas3', '~> 1.1.4'
|
||||
-gem 'omniauth-cas3', '~> 1.1.4'
|
||||
-gem 'omniauth-facebook', '~> 4.0.0'
|
||||
+gem 'omniauth-cas3', '~> 1.1', '>= 1.1.4'
|
||||
+gem 'omniauth-facebook', '~> 4.0'
|
||||
gem 'omniauth-github', '~> 1.3'
|
||||
-gem 'omniauth-gitlab', '~> 1.0.2'
|
||||
|
@ -50,7 +59,7 @@ gitlab Gemfile
|
|||
|
||||
# Kerberos authentication. EE-only
|
||||
gem 'gssapi', group: :kerberos
|
||||
@@ -58,41 +58,41 @@
|
||||
@@ -58,42 +58,42 @@
|
||||
gem 'invisible_captcha', '~> 0.12.1'
|
||||
|
||||
# Two-factor authentication
|
||||
|
@ -63,9 +72,9 @@ gitlab Gemfile
|
|||
|
||||
# GitLab Pages
|
||||
-gem 'validates_hostname', '~> 1.0.6'
|
||||
-gem 'rubyzip', '~> 1.3.0', require: 'zip'
|
||||
-gem 'rubyzip', '~> 2.0.0', require: 'zip'
|
||||
+gem 'validates_hostname', '~> 1.0', '>= 1.0.6'
|
||||
+gem 'rubyzip', '~> 1.3', require: 'zip'
|
||||
+gem 'rubyzip', '~> 2.0', require: 'zip'
|
||||
# GitLab Pages letsencrypt support
|
||||
-gem 'acme-client', '~> 2.0.2'
|
||||
+gem 'acme-client', '~> 2.0', '>= 2.0.2'
|
||||
|
@ -98,12 +107,13 @@ gitlab Gemfile
|
|||
# TODO: remove app/views/graphiql/rails/editors/show.html.erb when https://github.com/rmosolgo/graphiql-rails/pull/71 is released:
|
||||
# https://gitlab.com/gitlab-org/gitlab/issues/31747
|
||||
-gem 'graphiql-rails', '~> 1.4.10'
|
||||
-gem 'apollo_upload_server', '~> 2.0.0.beta3'
|
||||
+gem 'graphiql-rails', '~> 1.4', '>= 1.4.10'
|
||||
+gem 'apollo_upload_server', '>= 2.0.0.beta3'
|
||||
gem 'graphql-docs', '~> 1.6.0', group: [:development, :test]
|
||||
gem 'apollo_upload_server', '~> 2.0.0.beta3'
|
||||
-gem 'graphql-docs', '~> 1.6.0', group: [:development, :test]
|
||||
+gem 'graphql-docs', '~> 1.6', group: [:development, :test]
|
||||
|
||||
# Disable strong_params so that Mash does not respond to :permitted?
|
||||
gem 'hashie-forbidden_attributes'
|
||||
@@ -102,7 +102,7 @@
|
||||
gem 'kaminari', '~> 1.0'
|
||||
|
||||
|
@ -139,16 +149,16 @@ gitlab Gemfile
|
|||
|
||||
# Markdown and HTML processing
|
||||
gem 'html-pipeline', '~> 2.12'
|
||||
-gem 'deckar01-task_list', '2.2.1'
|
||||
gem 'deckar01-task_list', '2.2.1'
|
||||
-gem 'gitlab-markup', '~> 1.7.0'
|
||||
-gem 'github-markup', '~> 1.7.0', require: 'github/markup'
|
||||
+gem 'deckar01-task_list', '~> 2.2', '>= 2.2.1'
|
||||
+gem 'gitlab-markup', '~> 1.7'
|
||||
+gem 'github-markup', '~> 1.7', require: 'github/markup'
|
||||
gem 'commonmarker', '~> 0.20'
|
||||
-gem 'RedCloth', '~> 4.3.2'
|
||||
-gem 'rdoc', '~> 6.1.2'
|
||||
+gem 'RedCloth', '~> 4.3', '>= 4.3.2'
|
||||
gem 'rdoc', '~> 6.0'
|
||||
+gem 'rdoc', '~> 6.1'
|
||||
gem 'org-ruby', '~> 0.9.12'
|
||||
gem 'creole', '~> 0.5.0'
|
||||
gem 'wikicloth', '0.8.1'
|
||||
|
@ -161,8 +171,8 @@ gitlab Gemfile
|
|||
gem 'truncato', '~> 0.7.11'
|
||||
-gem 'bootstrap_form', '~> 4.2.0'
|
||||
-gem 'nokogiri', '~> 1.10.5'
|
||||
+gem 'bootstrap_form', '~> 4.2', '>= 4.2.0'
|
||||
+gem 'nokogiri', '~> 1.10', '>= 1.10.5'
|
||||
+gem 'bootstrap_form', '~> 4.2'
|
||||
+gem 'nokogiri', '~> 1.0', '>= 1.10.5'
|
||||
gem 'escape_utils', '~> 1.1'
|
||||
|
||||
# Calendar rendering
|
||||
|
@ -273,10 +283,10 @@ gitlab Gemfile
|
|||
-gem 'sassc-rails', '~> 2.1.0'
|
||||
-gem 'uglifier', '~> 2.7.2'
|
||||
+gem 'sassc-rails', '~> 2.1'
|
||||
+gem 'uglifier', '~> 2.7', '>=2.7.2'
|
||||
+gem 'uglifier', '~> 2.7', '>= 2.7.2'
|
||||
|
||||
-gem 'addressable', '~> 2.5.2'
|
||||
+gem 'addressable', '~> 2.5', '>=2.5.2'
|
||||
+gem 'addressable', '~> 2.5', '>= 2.5.2'
|
||||
gem 'font-awesome-rails', '~> 4.7'
|
||||
gem 'gemojione', '~> 3.3'
|
||||
gem 'gon', '~> 6.2'
|
||||
|
|
|
@ -5,8 +5,8 @@ Bundler will fail when it can't find these locally
|
|||
@@ -93,7 +93,6 @@
|
||||
# https://gitlab.com/gitlab-org/gitlab/issues/31747
|
||||
gem 'graphiql-rails', '~> 1.4', '>= 1.4.10'
|
||||
gem 'apollo_upload_server', '>= 2.0.0.beta3'
|
||||
-gem 'graphql-docs', '~> 1.6.0', group: [:development, :test]
|
||||
gem 'apollo_upload_server', '~> 2.0.0.beta3'
|
||||
-gem 'graphql-docs', '~> 1.6', group: [:development, :test]
|
||||
|
||||
# Disable strong_params so that Mash does not respond to :permitted?
|
||||
gem 'hashie-forbidden_attributes'
|
||||
|
@ -18,7 +18,7 @@ Bundler will fail when it can't find these locally
|
|||
|
||||
gem 'batch-loader', '~> 1.4'
|
||||
|
||||
@@ -331,21 +329,6 @@
|
||||
@@ -331,68 +329,6 @@
|
||||
gem 'raindrops', '~> 0.18'
|
||||
end
|
||||
|
||||
|
@ -37,6 +37,53 @@ Bundler will fail when it can't find these locally
|
|||
- gem 'thin', '~> 1.7.0'
|
||||
-end
|
||||
-
|
||||
group :development, :test do
|
||||
gem 'bullet', '~> 6.0.2', require: !!ENV['ENABLE_BULLET']
|
||||
gem 'pry-byebug', '~> 3.5.1', platform: :mri
|
||||
-group :development, :test do
|
||||
- gem 'bullet', '~> 6.0.2', require: !!ENV['ENABLE_BULLET']
|
||||
- gem 'pry-byebug', '~> 3.5.1', platform: :mri
|
||||
- gem 'pry-rails', '~> 0.3.4'
|
||||
-
|
||||
- gem 'awesome_print', require: false
|
||||
-
|
||||
- gem 'database_cleaner', '~> 1.7.0'
|
||||
- gem 'factory_bot_rails', '~> 5.1.0'
|
||||
- gem 'rspec-rails', '~> 4.0.0.beta3'
|
||||
-
|
||||
- # Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826)
|
||||
- gem 'minitest', '~> 5.11.0'
|
||||
-
|
||||
- # Generate Fake data
|
||||
- gem 'ffaker', '~> 2.10'
|
||||
-
|
||||
- gem 'spring', '~> 2.0.0'
|
||||
- gem 'spring-commands-rspec', '~> 1.0.4'
|
||||
-
|
||||
- gem 'gitlab-styles', '~> 2.7', require: false
|
||||
- # Pin these dependencies, otherwise a new rule could break the CI pipelines
|
||||
- gem 'rubocop', '~> 0.69.0'
|
||||
- gem 'rubocop-performance', '~> 1.1.0'
|
||||
- gem 'rubocop-rspec', '~> 1.22.1'
|
||||
-
|
||||
- gem 'scss_lint', '~> 0.56.0', require: false
|
||||
- gem 'haml_lint', '~> 0.34.0', require: false
|
||||
- gem 'simplecov', '~> 0.16.1', require: false
|
||||
- gem 'bundler-audit', '~> 0.5.0', require: false
|
||||
-
|
||||
- gem 'benchmark-ips', '~> 2.3.0', require: false
|
||||
-
|
||||
- gem 'knapsack', '~> 1.17'
|
||||
-
|
||||
- gem 'stackprof', '~> 0.2.13', require: false
|
||||
-
|
||||
- gem 'simple_po_parser', '~> 1.1.2', require: false
|
||||
-
|
||||
- gem 'timecop', '~> 0.8.0'
|
||||
-end
|
||||
-
|
||||
-# Gems required in omnibus-gitlab pipeline
|
||||
-group :development, :test, :omnibus do
|
||||
- gem 'license_finder', '~> 5.4', require: false
|
||||
-end
|
||||
-
|
||||
group :test do
|
||||
gem 'fuubar', '~> 2.2.0'
|
||||
gem 'rspec-retry', '~> 0.6.1'
|
||||
|
|
|
@ -4,23 +4,8 @@
|
|||
gem 'raindrops', '~> 0.18'
|
||||
end
|
||||
|
||||
-group :development, :test do
|
||||
+if ENV["INCLUDE_TEST_DEPENDS"] == "true"
|
||||
gem 'bullet', '~> 6.0.2', require: !!ENV['ENABLE_BULLET']
|
||||
gem 'pry-byebug', '~> 3.5.1', platform: :mri
|
||||
gem 'pry-rails', '~> 0.3.4'
|
||||
@@ -369,14 +369,10 @@
|
||||
gem 'simple_po_parser', '~> 1.1.2', require: false
|
||||
|
||||
gem 'timecop', '~> 0.8.0'
|
||||
-end
|
||||
|
||||
# Gems required in omnibus-gitlab pipeline
|
||||
-group :development, :test, :omnibus do
|
||||
gem 'license_finder', '~> 5.4', require: false
|
||||
-end
|
||||
|
||||
-group :test do
|
||||
+if ENV["INCLUDE_TEST_DEPENDS"] == "true"
|
||||
gem 'fuubar', '~> 2.2.0'
|
||||
gem 'rspec-retry', '~> 0.6.1'
|
||||
gem 'rspec_profiling', '~> 0.0.5'
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
@@ -137,7 +137,6 @@
|
||||
# Markdown and HTML processing
|
||||
gem 'html-pipeline', '~> 2.12'
|
||||
gem 'deckar01-task_list', '~> 2.2', '>= 2.2.1'
|
||||
gem 'deckar01-task_list', '2.2.1'
|
||||
-gem 'gitlab-markup', '~> 1.7'
|
||||
gem 'github-markup', '~> 1.7', require: 'github/markup'
|
||||
gem 'commonmarker', '~> 0.20'
|
||||
|
|
10
debian/patches/0450-remove-bullet.patch
vendored
10
debian/patches/0450-remove-bullet.patch
vendored
|
@ -1,10 +0,0 @@
|
|||
--- a/Gemfile
|
||||
+++ b/Gemfile
|
||||
@@ -323,7 +323,6 @@
|
||||
end
|
||||
|
||||
if ENV["INCLUDE_TEST_DEPENDS"] == "true"
|
||||
- gem 'bullet', '~> 6.0.2', require: !!ENV['ENABLE_BULLET']
|
||||
gem 'pry-byebug', '~> 3.5.1', platform: :mri
|
||||
gem 'pry-rails', '~> 0.3.4'
|
||||
|
|
@ -18,7 +18,7 @@ Subject: [PATCH 1/2] Update asciidoctor-plantuml 0.0.9 -> 0.0.10
|
|||
+gem 'asciidoctor-plantuml', '~> 0.0.10'
|
||||
gem 'rouge', '~> 3.11'
|
||||
gem 'truncato', '~> 0.7.11'
|
||||
gem 'bootstrap_form', '~> 4.2', '>= 4.2.0'
|
||||
gem 'bootstrap_form', '~> 4.2'
|
||||
--- a/Gemfile.lock
|
||||
+++ b/Gemfile.lock
|
||||
@@ -71,7 +71,7 @@
|
||||
|
@ -30,7 +30,7 @@ Subject: [PATCH 1/2] Update asciidoctor-plantuml 0.0.9 -> 0.0.10
|
|||
asciidoctor (>= 1.5.6, < 3.0.0)
|
||||
ast (2.4.0)
|
||||
atlassian-jwt (0.2.0)
|
||||
@@ -1125,7 +1125,7 @@
|
||||
@@ -1126,7 +1126,7 @@
|
||||
asana (~> 0.9)
|
||||
asciidoctor (~> 2.0.10)
|
||||
asciidoctor-include-ext (~> 0.3.1)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
--- a/Gemfile
|
||||
+++ b/Gemfile
|
||||
@@ -421,9 +421,9 @@
|
||||
@@ -379,9 +379,9 @@
|
||||
# Gitaly GRPC protocol definitions
|
||||
gem 'gitaly', '~> 1.73'
|
||||
|
||||
|
|
11
debian/patches/0488-relax-rdoc.patch
vendored
Normal file
11
debian/patches/0488-relax-rdoc.patch
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
--- a/Gemfile
|
||||
+++ b/Gemfile
|
||||
@@ -137,7 +137,7 @@
|
||||
gem 'github-markup', '~> 1.7', require: 'github/markup'
|
||||
gem 'commonmarker', '~> 0.20'
|
||||
gem 'RedCloth', '~> 4.3', '>= 4.3.2'
|
||||
-gem 'rdoc', '~> 6.1'
|
||||
+gem 'rdoc', '~> 6.0'
|
||||
gem 'org-ruby', '~> 0.9.12'
|
||||
gem 'creole', '~> 0.5.0'
|
||||
gem 'wikicloth', '0.8.1'
|
|
@ -1,6 +1,6 @@
|
|||
--- a/package.json
|
||||
+++ b/package.json
|
||||
@@ -142,62 +142,6 @@
|
||||
@@ -143,62 +143,6 @@
|
||||
"xterm": "^3.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -3,7 +3,7 @@ Author: Utkarsh Gupta <guptautkarsh2102@gmail.com>
|
|||
|
||||
--- a/package.json
|
||||
+++ b/package.json
|
||||
@@ -88,6 +88,7 @@
|
||||
@@ -82,6 +82,7 @@
|
||||
"fuzzaldrin-plus": "^0.5.0",
|
||||
"glob": "^7.1.2",
|
||||
"graphql": "^14.0.2",
|
||||
|
|
15
debian/patches/0740-use-packaged-modules.patch
vendored
15
debian/patches/0740-use-packaged-modules.patch
vendored
|
@ -1,6 +1,6 @@
|
|||
--- a/config/webpack.config.js
|
||||
+++ b/config/webpack.config.js
|
||||
@@ -6,7 +6,6 @@
|
||||
@@ -6,7 +6,6 @@ const VueLoaderPlugin = require('vue-loa
|
||||
const StatsWriterPlugin = require('webpack-stats-plugin').StatsWriterPlugin;
|
||||
const CompressionPlugin = require('compression-webpack-plugin');
|
||||
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
|
||||
|
@ -8,7 +8,7 @@
|
|||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
const vendorDllHash = require('./helpers/vendor_dll_hash');
|
||||
|
||||
@@ -151,12 +150,12 @@
|
||||
@@ -151,12 +150,12 @@ module.exports = {
|
||||
|
||||
resolve: {
|
||||
extensions: ['.js', '.gql', '.graphql'],
|
||||
|
@ -23,7 +23,7 @@
|
|||
},
|
||||
|
||||
module: {
|
||||
@@ -431,19 +430,6 @@
|
||||
@@ -431,19 +430,6 @@ module.exports = {
|
||||
// enable HMR only in webpack-dev-server
|
||||
DEV_SERVER_LIVERELOAD && new webpack.HotModuleReplacementPlugin(),
|
||||
|
||||
|
@ -43,7 +43,7 @@
|
|||
new webpack.DefinePlugin({
|
||||
// This one is used to define window.gon.ee and other things properly in tests:
|
||||
'process.env.IS_EE': JSON.stringify(IS_EE),
|
||||
@@ -469,6 +455,7 @@
|
||||
@@ -469,6 +455,7 @@ module.exports = {
|
||||
|
||||
node: {
|
||||
fs: 'empty', // sqljs requires fs
|
||||
|
@ -53,7 +53,7 @@
|
|||
};
|
||||
--- a/package.json
|
||||
+++ b/package.json
|
||||
@@ -50,58 +50,33 @@
|
||||
@@ -50,59 +50,34 @@
|
||||
"apollo-link-batch-http": "^1.2.11",
|
||||
"apollo-upload-client": "^10.0.0",
|
||||
"at.js": "^1.5.4",
|
||||
|
@ -101,6 +101,7 @@
|
|||
- "jszip": "^3.1.3",
|
||||
- "jszip-utils": "^0.0.2",
|
||||
"katex": "^0.10.0",
|
||||
"lodash": "^4.17.15",
|
||||
- "marked": "^0.3.12",
|
||||
"mermaid": "^8.4.2",
|
||||
"monaco-editor": "^0.15.6",
|
||||
|
@ -112,7 +113,7 @@
|
|||
"prismjs": "^1.6.0",
|
||||
"prosemirror-markdown": "^1.3.0",
|
||||
"prosemirror-model": "^1.6.4",
|
||||
@@ -118,13 +93,9 @@
|
||||
@@ -119,13 +94,9 @@
|
||||
"svg4everybody": "2.1.9",
|
||||
"swagger-ui-dist": "^3.24.3",
|
||||
"three": "^0.84.0",
|
||||
|
@ -126,7 +127,7 @@
|
|||
"url-loader": "^2.1.0",
|
||||
"visibilityjs": "^1.2.4",
|
||||
"vue": "^2.6.10",
|
||||
@@ -135,10 +106,6 @@
|
||||
@@ -136,10 +107,6 @@
|
||||
"vue-virtual-scroll-list": "^1.3.1",
|
||||
"vuedraggable": "^2.23.0",
|
||||
"vuex": "^3.1.0",
|
||||
|
|
3
debian/patches/series
vendored
3
debian/patches/series
vendored
|
@ -5,7 +5,6 @@
|
|||
#0400-Relax-recaptcha-version.patch
|
||||
0430-remove-gitlab-markup.patch
|
||||
0440-remove-puma.patch
|
||||
0450-remove-bullet.patch
|
||||
0460-embed-derailed-benchmarks.patch
|
||||
0470-relax-bootsnap.patch
|
||||
0480-embed-elasticsearch-model.patch
|
||||
|
@ -15,6 +14,7 @@
|
|||
0484-relax-asciidoctor-plantuml.patch
|
||||
0485-relax-responders.patch
|
||||
0486-relax-grpc-protobuf.patch
|
||||
0488-relax-rdoc.patch
|
||||
0500-set-webpack-root.patch
|
||||
0510-remove-dev-dependencies.patch
|
||||
0520-add-system-lib-path-for-webpack.patch
|
||||
|
@ -30,6 +30,5 @@
|
|||
0730-install-graphql-tag.patch
|
||||
0740-use-packaged-modules.patch
|
||||
0750-fix-relative-paths.patch
|
||||
0760-bump-rubyzip.patch
|
||||
0770-bump-node-d3.patch
|
||||
0780-embed-doorkeeper.patch
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
## master (unreleased)
|
||||
|
||||
## 1.6.0
|
||||
|
||||
- Added the `perf:app` command to compare commits within the same application. (https://github.com/schneems/derailed_benchmarks/pull/157)
|
||||
- Allow Rails < 7 and 1.0 <= Thor < 2 (https://github.com/schneems/derailed_benchmarks/pull/168)
|
||||
|
||||
## 1.5.0
|
||||
|
||||
- Test `perf:library` results against 99% confidence interval in addition to 95% (https://github.com/schneems/derailed_benchmarks/pull/165)
|
||||
|
|
|
@ -415,13 +415,23 @@ or point it at your local copy:
|
|||
gem 'rails', path: "<path/to/your/local/copy/rails>"
|
||||
```
|
||||
|
||||
To run your test:
|
||||
To run your tests within the context of your current app/repo:
|
||||
|
||||
```
|
||||
$ bundle exec derailed exec perf:app
|
||||
```
|
||||
|
||||
This will automatically test the two latest commits of your library/current directory.
|
||||
|
||||
If you'd like to test the Rails library instead, make sure that `ENV[DERAILED_PATH_TO_LIBRARY]` is unset.
|
||||
|
||||
```
|
||||
$ bundle exec derailed exec perf:library
|
||||
```
|
||||
|
||||
This will automatically test the two latest commits of Rails (or the library you've specified). If you would like to compare against different SHAs you can manually specify them:
|
||||
This will automatically test the two latest commits of Rails.
|
||||
|
||||
If you would also like to compare against different SHAs you can manually specify them:
|
||||
|
||||
```
|
||||
$ SHAS_TO_TEST="7b4d80cb373e,13d6aa3a7b70" bundle exec derailed exec perf:library
|
||||
|
|
|
@ -28,12 +28,12 @@ Gem::Specification.new do |gem|
|
|||
gem.add_dependency "benchmark-ips", "~> 2"
|
||||
gem.add_dependency "rack", ">= 1"
|
||||
gem.add_dependency "rake", "> 10", "< 14"
|
||||
gem.add_dependency "thor", "~> 0.19"
|
||||
gem.add_dependency "thor", ">= 0.19", "< 2"
|
||||
gem.add_dependency "ruby-statistics", ">= 2.1"
|
||||
|
||||
gem.add_development_dependency "capybara", "~> 2"
|
||||
gem.add_development_dependency "m"
|
||||
gem.add_development_dependency "rails", "> 3", "<= 6"
|
||||
gem.add_development_dependency "rails", "> 3", "<= 7"
|
||||
gem.add_development_dependency "devise", "> 3", "< 6"
|
||||
gem.add_development_dependency "appraisal", "2.2.0"
|
||||
end
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
require_relative 'load_tasks'
|
||||
|
||||
namespace :perf do
|
||||
desc "runs the performance test against two most recent commits of the current app"
|
||||
task :app do
|
||||
ENV["DERAILED_PATH_TO_LIBRARY"] = '.'
|
||||
Rake::Task["perf:library"].invoke
|
||||
end
|
||||
|
||||
desc "runs the same test against two different branches for statistical comparison"
|
||||
task :library do
|
||||
begin
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module DerailedBenchmarks
|
||||
VERSION = "1.5.0"
|
||||
VERSION = "1.6.0"
|
||||
end
|
||||
|
|
|
@ -13,6 +13,13 @@ class TasksTest < ActiveSupport::TestCase
|
|||
FileUtils.remove_entry_secure(rails_app_path('tmp'))
|
||||
end
|
||||
|
||||
def run!(cmd)
|
||||
puts "Running: #{cmd}"
|
||||
out = `#{cmd}`
|
||||
raise "Could not run #{cmd}, output: #{out}" unless $?.success?
|
||||
out
|
||||
end
|
||||
|
||||
def rake(cmd, options = {})
|
||||
assert_success = options.key?(:assert_success) ? options[:assert_success] : true
|
||||
env = options[:env] || {}
|
||||
|
@ -55,6 +62,13 @@ class TasksTest < ActiveSupport::TestCase
|
|||
rake "perf:test"
|
||||
end
|
||||
|
||||
test 'app' do
|
||||
skip unless ENV['USING_RAILS_GIT']
|
||||
run!("cd #{rails_app_path} && git init . && git add . && git commit -m first && git commit --allow-empty -m second")
|
||||
env = { "TEST_COUNT" => 10, "DERAILED_SCRIPT_COUNT" => 2 }
|
||||
puts rake "perf:app", { env: env }
|
||||
end
|
||||
|
||||
test 'TEST_COUNT' do
|
||||
result = rake "perf:test", env: { "TEST_COUNT" => 1 }
|
||||
assert_match "1 derailed requests", result
|
||||
|
|
|
@ -221,6 +221,11 @@ include::basics.adoc[]
|
|||
include::https://example.org/installation.adoc[]
|
||||
```
|
||||
|
||||
To guarantee good system performance and prevent malicious documents causing
|
||||
problems, GitLab enforces a **maximum limit** on the number of include directives
|
||||
processed in any one document. Currently a total of 32 documents can be
|
||||
included, a number that is inclusive of transitive dependencies.
|
||||
|
||||
### Blocks
|
||||
|
||||
```asciidoc
|
||||
|
|
|
@ -85,6 +85,8 @@ module API
|
|||
protected: @project.protected_for?(ref))
|
||||
end
|
||||
|
||||
authorize! :update_pipeline, pipeline
|
||||
|
||||
status = GenericCommitStatus.running_or_pending.find_or_initialize_by(
|
||||
project: @project,
|
||||
pipeline: pipeline,
|
||||
|
|
|
@ -154,7 +154,7 @@ module API
|
|||
|
||||
not_found! 'Commit' unless commit
|
||||
|
||||
present commit, with: Entities::CommitDetail, stats: params[:stats]
|
||||
present commit, with: Entities::CommitDetail, stats: params[:stats], current_user: current_user
|
||||
end
|
||||
|
||||
desc 'Get the diff for a specific commit of a project' do
|
||||
|
|
|
@ -476,8 +476,18 @@ module API
|
|||
class CommitDetail < Commit
|
||||
expose :stats, using: Entities::CommitStats, if: :stats
|
||||
expose :status
|
||||
expose :last_pipeline, using: 'API::Entities::PipelineBasic'
|
||||
expose :project_id
|
||||
|
||||
expose :last_pipeline do |commit, options|
|
||||
pipeline = commit.last_pipeline if can_read_pipeline?
|
||||
::API::Entities::PipelineBasic.represent(pipeline, options)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def can_read_pipeline?
|
||||
Ability.allowed?(options[:current_user], :read_pipeline, object.last_pipeline)
|
||||
end
|
||||
end
|
||||
|
||||
class CommitSignature < Grape::Entity
|
||||
|
|
|
@ -127,6 +127,7 @@ module API
|
|||
get ":id/repository/files/:file_path/raw", requirements: FILE_ENDPOINT_REQUIREMENTS do
|
||||
assign_file_vars!
|
||||
|
||||
no_cache_headers
|
||||
set_http_headers(blob_data)
|
||||
|
||||
send_git_blob @repo, @blob
|
||||
|
|
|
@ -256,11 +256,21 @@ module API
|
|||
end
|
||||
|
||||
def require_gitlab_workhorse!
|
||||
verify_workhorse_api!
|
||||
|
||||
unless env['HTTP_GITLAB_WORKHORSE'].present?
|
||||
forbidden!('Request should be executed via GitLab Workhorse')
|
||||
end
|
||||
end
|
||||
|
||||
def verify_workhorse_api!
|
||||
Gitlab::Workhorse.verify_api_request!(request.headers)
|
||||
rescue => e
|
||||
Gitlab::ErrorTracking.track_exception(e)
|
||||
|
||||
forbidden!
|
||||
end
|
||||
|
||||
def require_pages_enabled!
|
||||
not_found! unless user_project.pages_available?
|
||||
end
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
module API
|
||||
module Helpers
|
||||
module HeadersHelpers
|
||||
include Gitlab::NoCacheHeaders
|
||||
|
||||
def set_http_headers(header_data)
|
||||
header_data.each do |key, value|
|
||||
if value.is_a?(Enumerable)
|
||||
|
@ -12,6 +14,12 @@ module API
|
|||
header "X-Gitlab-#{key.to_s.split('_').collect(&:capitalize).join('-')}", value.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def no_cache_headers
|
||||
DEFAULT_GITLAB_NO_CACHE_HEADERS.each do |k, v|
|
||||
header k, v
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -201,12 +201,14 @@ module Banzai
|
|||
gather_references(nodes)
|
||||
end
|
||||
|
||||
# Gathers the references for the given HTML nodes.
|
||||
# Gathers the references for the given HTML nodes. Returns visible
|
||||
# references and a list of nodes which are not visible to the user
|
||||
def gather_references(nodes)
|
||||
nodes = nodes_user_can_reference(current_user, nodes)
|
||||
nodes = nodes_visible_to_user(current_user, nodes)
|
||||
visible = nodes_visible_to_user(current_user, nodes)
|
||||
not_visible = nodes - visible
|
||||
|
||||
referenced_by(nodes)
|
||||
{ visible: referenced_by(visible), not_visible: not_visible }
|
||||
end
|
||||
|
||||
# Returns a Hash containing the projects for a given list of HTML nodes.
|
||||
|
|
|
@ -11,6 +11,7 @@ module Gitlab
|
|||
# the resulting HTML through HTML pipeline filters.
|
||||
module Asciidoc
|
||||
MAX_INCLUDE_DEPTH = 5
|
||||
MAX_INCLUDES = 32
|
||||
DEFAULT_ADOC_ATTRS = {
|
||||
'showtitle' => true,
|
||||
'sectanchors' => true,
|
||||
|
@ -40,6 +41,7 @@ module Gitlab
|
|||
extensions: extensions }
|
||||
|
||||
context[:pipeline] = :ascii_doc
|
||||
context[:max_includes] = [MAX_INCLUDES, context[:max_includes]].compact.min
|
||||
|
||||
plantuml_setup
|
||||
|
||||
|
|
|
@ -13,7 +13,9 @@ module Gitlab
|
|||
super(logger: Gitlab::AppLogger)
|
||||
|
||||
@context = context
|
||||
@repository = context[:project].try(:repository)
|
||||
@repository = context[:repository] || context[:project].try(:repository)
|
||||
@max_includes = context[:max_includes].to_i
|
||||
@included = []
|
||||
|
||||
# Note: Asciidoctor calls #freeze on extensions, so we can't set new
|
||||
# instance variables after initialization.
|
||||
|
@ -28,8 +30,11 @@ module Gitlab
|
|||
def include_allowed?(target, reader)
|
||||
doc = reader.document
|
||||
|
||||
return false if doc.attributes.fetch('max-include-depth').to_i < 1
|
||||
max_include_depth = doc.attributes.fetch('max-include-depth').to_i
|
||||
|
||||
return false if max_include_depth < 1
|
||||
return false if target_uri?(target)
|
||||
return false if included.size >= max_includes
|
||||
|
||||
true
|
||||
end
|
||||
|
@ -62,7 +67,7 @@ module Gitlab
|
|||
|
||||
private
|
||||
|
||||
attr_accessor :context, :repository, :cache
|
||||
attr_reader :context, :repository, :cache, :max_includes, :included
|
||||
|
||||
# Gets a Blob at a path for a specific revision.
|
||||
# This method will check that the Blob exists and contains readable text.
|
||||
|
@ -77,6 +82,8 @@ module Gitlab
|
|||
raise 'Blob not found' unless blob
|
||||
raise 'File is not readable' unless blob.readable_text?
|
||||
|
||||
included << filename
|
||||
|
||||
blob
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module BackgroundMigration
|
||||
# rubocop:disable Style/Documentation
|
||||
class RecalculateProjectAuthorizations
|
||||
def perform(user_ids)
|
||||
user_ids.each do |user_id|
|
||||
user = User.find_by(id: user_id)
|
||||
|
||||
next unless user
|
||||
|
||||
service = Users::RefreshAuthorizedProjectsService.new(
|
||||
user,
|
||||
incorrect_auth_found_callback:
|
||||
->(project_id, access_level) do
|
||||
logger.info(message: 'Removing ProjectAuthorizations',
|
||||
user_id: user.id,
|
||||
project_id: project_id,
|
||||
access_level: access_level)
|
||||
end,
|
||||
missing_auth_found_callback:
|
||||
->(project_id, access_level) do
|
||||
logger.info(message: 'Creating ProjectAuthorizations',
|
||||
user_id: user.id,
|
||||
project_id: project_id,
|
||||
access_level: access_level)
|
||||
end
|
||||
)
|
||||
|
||||
service.execute
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def logger
|
||||
@logger ||= Gitlab::BackgroundMigration::Logger.build
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
56
lib/gitlab/git/cross_repo_comparer.rb
Normal file
56
lib/gitlab/git/cross_repo_comparer.rb
Normal file
|
@ -0,0 +1,56 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Git
|
||||
class CrossRepoComparer
|
||||
attr_reader :source_repo, :target_repo
|
||||
|
||||
def initialize(source_repo, target_repo)
|
||||
@source_repo = source_repo
|
||||
@target_repo = target_repo
|
||||
end
|
||||
|
||||
def compare(source_ref, target_ref, straight:)
|
||||
ensuring_ref_in_source(target_ref) do |target_commit_id|
|
||||
Gitlab::Git::Compare.new(
|
||||
source_repo,
|
||||
target_commit_id,
|
||||
source_ref,
|
||||
straight: straight
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ensuring_ref_in_source(ref, &blk)
|
||||
return yield ref if source_repo == target_repo
|
||||
|
||||
# If the commit doesn't exist in the target, there's nothing we can do
|
||||
commit_id = target_repo.commit(ref)&.sha
|
||||
return unless commit_id
|
||||
|
||||
# The commit pointed to by ref may exist in the source even when they
|
||||
# are different repositories. This is particularly true of close forks,
|
||||
# but may also be the case if a temporary ref for this comparison has
|
||||
# already been created in the past, and the result hasn't been GC'd yet.
|
||||
return yield commit_id if source_repo.commit(commit_id)
|
||||
|
||||
# Worst case: the commit is not in the source repo so we need to fetch
|
||||
# it. Use a temporary ref and clean up afterwards
|
||||
with_commit_in_source_tmp(commit_id, &blk)
|
||||
end
|
||||
|
||||
# Fetch the ref into source_repo from target_repo, using a temporary ref
|
||||
# name that will be deleted once the method completes. This is a no-op if
|
||||
# fetching the source branch fails
|
||||
def with_commit_in_source_tmp(commit_id, &blk)
|
||||
tmp_ref = "refs/tmp/#{SecureRandom.hex}"
|
||||
|
||||
yield commit_id if source_repo.fetch_source_branch!(target_repo, commit_id, tmp_ref)
|
||||
ensure
|
||||
source_repo.delete_refs(tmp_ref) # best-effort
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -746,29 +746,9 @@ module Gitlab
|
|||
end
|
||||
|
||||
def compare_source_branch(target_branch_name, source_repository, source_branch_name, straight:)
|
||||
reachable_ref =
|
||||
if source_repository == self
|
||||
source_branch_name
|
||||
else
|
||||
# If a tmp ref was created before for a separate repo comparison (forks),
|
||||
# we're able to short-circuit the tmp ref re-creation:
|
||||
# 1. Take the SHA from the source repo
|
||||
# 2. Read that in the current "target" repo
|
||||
# 3. If that SHA is still known (readable), it means GC hasn't
|
||||
# cleaned it up yet, so we can use it instead re-writing the tmp ref.
|
||||
source_commit_id = source_repository.commit(source_branch_name)&.sha
|
||||
commit(source_commit_id)&.sha if source_commit_id
|
||||
end
|
||||
|
||||
return compare(target_branch_name, reachable_ref, straight: straight) if reachable_ref
|
||||
|
||||
tmp_ref = "refs/tmp/#{SecureRandom.hex}"
|
||||
|
||||
return unless fetch_source_branch!(source_repository, source_branch_name, tmp_ref)
|
||||
|
||||
compare(target_branch_name, tmp_ref, straight: straight)
|
||||
ensure
|
||||
delete_refs(tmp_ref) if tmp_ref
|
||||
CrossRepoComparer
|
||||
.new(source_repository, self)
|
||||
.compare(source_branch_name, target_branch_name, straight: straight)
|
||||
end
|
||||
|
||||
def write_ref(ref_path, ref, old_ref: nil)
|
||||
|
@ -1045,13 +1025,6 @@ module Gitlab
|
|||
|
||||
private
|
||||
|
||||
def compare(base_ref, head_ref, straight:)
|
||||
Gitlab::Git::Compare.new(self,
|
||||
base_ref,
|
||||
head_ref,
|
||||
straight: straight)
|
||||
end
|
||||
|
||||
def empty_diff_stats
|
||||
Gitlab::Git::DiffStatsCollection.new([])
|
||||
end
|
||||
|
|
15
lib/gitlab/no_cache_headers.rb
Normal file
15
lib/gitlab/no_cache_headers.rb
Normal file
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module NoCacheHeaders
|
||||
DEFAULT_GITLAB_NO_CACHE_HEADERS = {
|
||||
'Cache-Control' => "#{ActionDispatch::Http::Cache::Response::DEFAULT_CACHE_CONTROL}, no-store, no-cache",
|
||||
'Pragma' => 'no-cache', # HTTP 1.0 compatibility
|
||||
'Expires' => 'Fri, 01 Jan 1990 00:00:00 GMT'
|
||||
}.freeze
|
||||
|
||||
def no_cache_headers
|
||||
raise "#no_cache_headers is not implemented for this object"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -68,12 +68,9 @@ module Gitlab
|
|||
.select([namespaces[:id], members[:access_level]])
|
||||
.except(:order)
|
||||
|
||||
if Feature.enabled?(:share_group_with_group)
|
||||
# Namespaces shared with any of the group
|
||||
cte << Group.select([namespaces[:id], 'group_group_links.group_access AS access_level'])
|
||||
.joins(join_group_group_links)
|
||||
.joins(join_members_on_group_group_links)
|
||||
end
|
||||
cte << Group.select([namespaces[:id], 'group_group_links.group_access AS access_level'])
|
||||
.joins(join_group_group_links)
|
||||
.joins(join_members_on_group_group_links)
|
||||
|
||||
# Sub groups of any groups the user is a member of.
|
||||
cte << Group.select([
|
||||
|
@ -114,6 +111,8 @@ module Gitlab
|
|||
members = Member.arel_table
|
||||
|
||||
cond = group_group_links[:shared_with_group_id].eq(members[:source_id])
|
||||
.and(members[:source_type].eq('Namespace'))
|
||||
.and(members[:requested_at].eq(nil))
|
||||
.and(members[:user_id].eq(user.id))
|
||||
Arel::Nodes::InnerJoin.new(members, Arel::Nodes::On.new(cond))
|
||||
end
|
||||
|
|
|
@ -6,11 +6,16 @@ module Gitlab
|
|||
REFERABLES = %i(user issue label milestone mentioned_user mentioned_group mentioned_project
|
||||
merge_request snippet commit commit_range directly_addressed_user epic).freeze
|
||||
attr_accessor :project, :current_user, :author
|
||||
# This counter is increased by a number of references filtered out by
|
||||
# banzai reference exctractor. Note that this counter is stateful and
|
||||
# not idempotent and is increased whenever you call `references`.
|
||||
attr_reader :stateful_not_visible_counter
|
||||
|
||||
def initialize(project, current_user = nil)
|
||||
@project = project
|
||||
@current_user = current_user
|
||||
@references = {}
|
||||
@stateful_not_visible_counter = 0
|
||||
|
||||
super()
|
||||
end
|
||||
|
@ -20,11 +25,15 @@ module Gitlab
|
|||
end
|
||||
|
||||
def references(type)
|
||||
super(type, project, current_user)
|
||||
refs = super(type, project, current_user)
|
||||
@stateful_not_visible_counter += refs[:not_visible].count
|
||||
|
||||
refs[:visible]
|
||||
end
|
||||
|
||||
def reset_memoized_values
|
||||
@references = {}
|
||||
@stateful_not_visible_counter = 0
|
||||
super()
|
||||
end
|
||||
|
||||
|
|
|
@ -93,6 +93,7 @@
|
|||
"jszip": "^3.1.3",
|
||||
"jszip-utils": "^0.0.2",
|
||||
"katex": "^0.10.0",
|
||||
"lodash": "^4.17.15",
|
||||
"marked": "^0.3.12",
|
||||
"mermaid": "^8.4.2",
|
||||
"monaco-editor": "^0.15.6",
|
||||
|
|
|
@ -59,5 +59,48 @@ module QA
|
|||
a_hash_including(message: '202 Accepted')
|
||||
)
|
||||
end
|
||||
|
||||
describe 'raw file access' do
|
||||
let(:svg_file) do
|
||||
<<-SVG
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
|
||||
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
|
||||
<polygon id="triangle" points="0,0 0,50 50,0" fill="#009900" stroke="#004400"/>
|
||||
<script type="text/javascript">
|
||||
alert("surprise");
|
||||
</script>
|
||||
</svg>
|
||||
SVG
|
||||
end
|
||||
|
||||
it 'sets no-cache headers as expected' do
|
||||
create_project_request = Runtime::API::Request.new(@api_client, '/projects')
|
||||
post create_project_request.url, path: project_name, name: project_name
|
||||
|
||||
create_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/test.svg")
|
||||
post create_file_request.url, branch: 'master', content: svg_file, commit_message: 'Add test.svg'
|
||||
|
||||
get_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/test.svg/raw", ref: 'master')
|
||||
|
||||
3.times do
|
||||
response = get get_file_request.url
|
||||
|
||||
# Subsequent responses aren't cached, so headers should match from
|
||||
# request to request, especially a 200 response rather than a 304
|
||||
# (indicating a cached response.) Further, :content_disposition
|
||||
# should include `attachment` for all responses.
|
||||
#
|
||||
expect(response.headers[:cache_control]).to include("no-store")
|
||||
expect(response.headers[:cache_control]).to include("no-cache")
|
||||
expect(response.headers[:pragma]).to eq("no-cache")
|
||||
expect(response.headers[:expires]).to eq("Fri, 01 Jan 1990 00:00:00 GMT")
|
||||
expect(response.headers[:content_disposition]).to include("attachment")
|
||||
expect(response.headers[:content_disposition]).not_to include("inline")
|
||||
expect(response.headers[:content_type]).to include("image/svg+xml")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -23,6 +23,47 @@ describe DashboardController do
|
|||
end
|
||||
end
|
||||
|
||||
describe "GET activity as JSON" do
|
||||
render_views
|
||||
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project, :public, issues_access_level: ProjectFeature::PRIVATE) }
|
||||
|
||||
before do
|
||||
create(:event, :created, project: project, target: create(:issue))
|
||||
|
||||
sign_in(user)
|
||||
|
||||
request.cookies[:event_filter] = 'all'
|
||||
end
|
||||
|
||||
context 'when user has permission to see the event' do
|
||||
before do
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
it 'returns count' do
|
||||
get :activity, params: { format: :json }
|
||||
|
||||
expect(json_response['count']).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user has no permission to see the event' do
|
||||
it 'filters out invisible event' do
|
||||
get :activity, params: { format: :json }
|
||||
|
||||
expect(json_response['html']).to include(_('No activities found'))
|
||||
end
|
||||
|
||||
it 'filters out invisible event when calculating the count' do
|
||||
get :activity, params: { format: :json }
|
||||
|
||||
expect(json_response['count']).to eq(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'authenticates sessionless user', :issues, :atom, author_id: User.first
|
||||
it_behaves_like 'authenticates sessionless user', :issues_calendar, :ics
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ describe GroupsController do
|
|||
|
||||
it 'assigns events for all the projects in the group', :sidekiq_might_not_need_inline do
|
||||
subject
|
||||
expect(assigns(:events)).to contain_exactly(event)
|
||||
expect(assigns(:events).map(&:id)).to contain_exactly(event.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -119,12 +119,12 @@ describe GroupsController do
|
|||
describe 'GET #activity' do
|
||||
render_views
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
project
|
||||
end
|
||||
|
||||
context 'as json' do
|
||||
before do
|
||||
sign_in(user)
|
||||
project
|
||||
end
|
||||
|
||||
it 'includes events from all projects in group and subgroups', :sidekiq_might_not_need_inline do
|
||||
2.times do
|
||||
project = create(:project, group: group)
|
||||
|
@ -141,6 +141,31 @@ describe GroupsController do
|
|||
expect(assigns(:projects).limit_value).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user has no permission to see the event' do
|
||||
let(:user) { create(:user) }
|
||||
let(:group) { create(:group) }
|
||||
let(:project) { create(:project, group: group) }
|
||||
|
||||
let(:project_with_restricted_access) do
|
||||
create(:project, :public, issues_access_level: ProjectFeature::PRIVATE, group: group)
|
||||
end
|
||||
|
||||
before do
|
||||
create(:event, project: project)
|
||||
create(:event, :created, project: project_with_restricted_access, target: create(:issue))
|
||||
|
||||
group.add_guest(user)
|
||||
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
it 'filters out invisible event' do
|
||||
get :activity, params: { id: group.to_param }, format: :json
|
||||
|
||||
expect(json_response['count']).to eq(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #create' do
|
||||
|
|
|
@ -64,6 +64,46 @@ describe ProjectsController do
|
|||
end
|
||||
end
|
||||
|
||||
describe "GET #activity as JSON" do
|
||||
render_views
|
||||
|
||||
let(:project) { create(:project, :public, issues_access_level: ProjectFeature::PRIVATE) }
|
||||
|
||||
before do
|
||||
create(:event, :created, project: project, target: create(:issue))
|
||||
|
||||
sign_in(user)
|
||||
|
||||
request.cookies[:event_filter] = 'all'
|
||||
end
|
||||
|
||||
context 'when user has permission to see the event' do
|
||||
before do
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
it 'returns count' do
|
||||
get :activity, params: { namespace_id: project.namespace, id: project, format: :json }
|
||||
|
||||
expect(json_response['count']).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user has no permission to see the event' do
|
||||
it 'filters out invisible event' do
|
||||
get :activity, params: { namespace_id: project.namespace, id: project, format: :json }
|
||||
|
||||
expect(json_response['html']).to eq("\n")
|
||||
end
|
||||
|
||||
it 'filters out invisible event when calculating the count' do
|
||||
get :activity, params: { namespace_id: project.namespace, id: project, format: :json }
|
||||
|
||||
expect(json_response['count']).to eq(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET show" do
|
||||
context "user not project member" do
|
||||
before do
|
||||
|
|
9
spec/factories/project_authorizations.rb
Normal file
9
spec/factories/project_authorizations.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
FactoryBot.define do
|
||||
factory :project_authorization do
|
||||
user
|
||||
project
|
||||
access_level { Gitlab::Access::REPORTER }
|
||||
end
|
||||
end
|
|
@ -935,14 +935,14 @@ describe ProjectsHelper do
|
|||
helper.instance_variable_set(:@project, project)
|
||||
end
|
||||
|
||||
subject { helper.grafana_integration_token }
|
||||
subject { helper.grafana_integration_masked_token }
|
||||
|
||||
it { is_expected.to eq(nil) }
|
||||
|
||||
context 'grafana integration exists' do
|
||||
let!(:grafana_integration) { create(:grafana_integration, project: project) }
|
||||
|
||||
it { is_expected.to eq(grafana_integration.token) }
|
||||
it { is_expected.to eq(grafana_integration.masked_token) }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
import bp from '~/breakpoints';
|
||||
import { isMobile, getTopFrequentItems, updateExistingFrequentItem } from '~/frequent_items/utils';
|
||||
import {
|
||||
isMobile,
|
||||
getTopFrequentItems,
|
||||
updateExistingFrequentItem,
|
||||
sanitizeItem,
|
||||
} from '~/frequent_items/utils';
|
||||
import { HOUR_IN_MS, FREQUENT_ITEMS } from '~/frequent_items/constants';
|
||||
import { mockProject, unsortedFrequentItems, sortedFrequentItems } from './mock_data';
|
||||
|
||||
|
@ -86,4 +91,16 @@ describe('Frequent Items utils spec', () => {
|
|||
expect(result.frequency).toBe(mockedProject.frequency);
|
||||
});
|
||||
});
|
||||
|
||||
describe('sanitizeItem', () => {
|
||||
it('strips HTML tags for name and namespace', () => {
|
||||
const input = {
|
||||
name: '<br><b>test</b>',
|
||||
namespace: '<br>test',
|
||||
id: 1,
|
||||
};
|
||||
|
||||
expect(sanitizeItem(input)).toEqual({ name: 'test', namespace: 'test', id: 1 });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -19,7 +19,7 @@ describe Banzai::ReferenceParser::MentionedGroupParser do
|
|||
it 'returns empty array' do
|
||||
link['data-group'] = project.group.id.to_s
|
||||
|
||||
expect(subject.gather_references([link])).to eq([])
|
||||
expect_gathered_references(subject.gather_references([link]), [], 1)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -30,7 +30,7 @@ describe Banzai::ReferenceParser::MentionedGroupParser do
|
|||
end
|
||||
|
||||
it 'returns groups' do
|
||||
expect(subject.gather_references([link])).to eq([group])
|
||||
expect_gathered_references(subject.gather_references([link]), [group], 0)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -38,7 +38,7 @@ describe Banzai::ReferenceParser::MentionedGroupParser do
|
|||
it 'returns an empty Array' do
|
||||
link['data-group'] = 'test-non-existing'
|
||||
|
||||
expect(subject.gather_references([link])).to eq([])
|
||||
expect_gathered_references(subject.gather_references([link]), [], 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,7 +19,7 @@ describe Banzai::ReferenceParser::MentionedProjectParser do
|
|||
it 'returns empty Array' do
|
||||
link['data-project'] = project.id.to_s
|
||||
|
||||
expect(subject.gather_references([link])).to eq([])
|
||||
expect_gathered_references(subject.gather_references([link]), [], 1)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -30,7 +30,7 @@ describe Banzai::ReferenceParser::MentionedProjectParser do
|
|||
end
|
||||
|
||||
it 'returns an Array of referenced projects' do
|
||||
expect(subject.gather_references([link])).to eq([project])
|
||||
expect_gathered_references(subject.gather_references([link]), [project], 0)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -38,7 +38,7 @@ describe Banzai::ReferenceParser::MentionedProjectParser do
|
|||
it 'returns an empty Array' do
|
||||
link['data-project'] = 'inexisting-project-id'
|
||||
|
||||
expect(subject.gather_references([link])).to eq([])
|
||||
expect_gathered_references(subject.gather_references([link]), [], 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -22,7 +22,7 @@ describe Banzai::ReferenceParser::MentionedUserParser do
|
|||
end
|
||||
|
||||
it 'returns empty list of users' do
|
||||
expect(subject.gather_references([link])).to eq([])
|
||||
expect_gathered_references(subject.gather_references([link]), [], 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -35,7 +35,7 @@ describe Banzai::ReferenceParser::MentionedUserParser do
|
|||
end
|
||||
|
||||
it 'returns empty list of users' do
|
||||
expect(subject.gather_references([link])).to eq([])
|
||||
expect_gathered_references(subject.gather_references([link]), [], 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -44,7 +44,7 @@ describe Banzai::ReferenceParser::MentionedUserParser do
|
|||
it 'returns an Array of users' do
|
||||
link['data-user'] = user.id.to_s
|
||||
|
||||
expect(subject.referenced_by([link])).to eq([user])
|
||||
expect_gathered_references(subject.gather_references([link]), [user], 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,7 +17,7 @@ describe Banzai::ReferenceParser::ProjectParser do
|
|||
it 'returns an Array of projects' do
|
||||
link['data-project'] = project.id.to_s
|
||||
|
||||
expect(subject.gather_references([link])).to eq([project])
|
||||
expect_gathered_references(subject.gather_references([link]), [project], 0)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -25,7 +25,7 @@ describe Banzai::ReferenceParser::ProjectParser do
|
|||
it 'returns an empty Array' do
|
||||
link['data-project'] = ''
|
||||
|
||||
expect(subject.gather_references([link])).to eq([])
|
||||
expect_gathered_references(subject.gather_references([link]), [], 1)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -35,7 +35,7 @@ describe Banzai::ReferenceParser::ProjectParser do
|
|||
|
||||
link['data-project'] = private_project.id.to_s
|
||||
|
||||
expect(subject.gather_references([link])).to eq([])
|
||||
expect_gathered_references(subject.gather_references([link]), [], 1)
|
||||
end
|
||||
|
||||
it 'returns an Array when authorized' do
|
||||
|
@ -43,7 +43,7 @@ describe Banzai::ReferenceParser::ProjectParser do
|
|||
|
||||
link['data-project'] = private_project.id.to_s
|
||||
|
||||
expect(subject.gather_references([link])).to eq([private_project])
|
||||
expect_gathered_references(subject.gather_references([link]), [private_project], 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
44
spec/lib/gitlab/asciidoc/include_processor_spec.rb
Normal file
44
spec/lib/gitlab/asciidoc/include_processor_spec.rb
Normal file
|
@ -0,0 +1,44 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require 'nokogiri'
|
||||
|
||||
describe Gitlab::Asciidoc::IncludeProcessor do
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
|
||||
let(:processor_context) do
|
||||
{
|
||||
project: project,
|
||||
max_includes: max_includes,
|
||||
ref: ref
|
||||
}
|
||||
end
|
||||
let(:ref) { project.repository.root_ref }
|
||||
let(:max_includes) { 10 }
|
||||
|
||||
let(:reader) { Asciidoctor::PreprocessorReader.new(document, lines, 'file.adoc') }
|
||||
let(:document) { Asciidoctor::Document.new(lines) }
|
||||
|
||||
subject(:processor) { described_class.new(processor_context) }
|
||||
|
||||
let(:a_blob) { double(:Blob, readable_text?: true, data: a_data) }
|
||||
let(:a_data) { StringIO.new('include::b.adoc[]') }
|
||||
|
||||
let(:lines) { [':max-include-depth: 1000'] + Array.new(10, 'include::a.adoc[]') }
|
||||
|
||||
before do
|
||||
allow(project.repository).to receive(:blob_at).with(ref, 'a.adoc').and_return(a_blob)
|
||||
end
|
||||
|
||||
describe '#include_allowed?' do
|
||||
it 'allows the first include' do
|
||||
expect(processor.send(:include_allowed?, 'foo.adoc', reader)).to be_truthy
|
||||
end
|
||||
|
||||
it 'disallows the Nth + 1 include' do
|
||||
max_includes.times { processor.send(:read_blob, ref, 'a.adoc') }
|
||||
|
||||
expect(processor.send(:include_allowed?, 'foo.adoc', reader)).to be_falsey
|
||||
end
|
||||
end
|
||||
end
|
|
@ -425,6 +425,24 @@ module Gitlab
|
|||
create_file(current_file, "= AsciiDoc\n")
|
||||
end
|
||||
|
||||
def many_includes(target)
|
||||
Array.new(10, "include::#{target}[]").join("\n")
|
||||
end
|
||||
|
||||
context 'cyclic imports' do
|
||||
before do
|
||||
create_file('doc/api/a.adoc', many_includes('b.adoc'))
|
||||
create_file('doc/api/b.adoc', many_includes('a.adoc'))
|
||||
end
|
||||
|
||||
let(:include_path) { 'a.adoc' }
|
||||
let(:requested_path) { 'doc/api/README.md' }
|
||||
|
||||
it 'completes successfully' do
|
||||
is_expected.to include('<p>Include this:</p>')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with path to non-existing file' do
|
||||
let(:include_path) { 'not-exists.adoc' }
|
||||
|
||||
|
|
|
@ -0,0 +1,243 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::BackgroundMigration::RecalculateProjectAuthorizations, :migration, schema: 20200204113223 do
|
||||
let(:users_table) { table(:users) }
|
||||
let(:namespaces_table) { table(:namespaces) }
|
||||
let(:projects_table) { table(:projects) }
|
||||
let(:project_authorizations_table) { table(:project_authorizations) }
|
||||
let(:members_table) { table(:members) }
|
||||
let(:group_group_links) { table(:group_group_links) }
|
||||
let(:project_group_links) { table(:project_group_links) }
|
||||
|
||||
let(:user) { users_table.create!(id: 1, email: 'user@example.com', projects_limit: 10) }
|
||||
let(:group) { namespaces_table.create!(type: 'Group', name: 'group', path: 'group') }
|
||||
|
||||
subject { described_class.new.perform([user.id]) }
|
||||
|
||||
context 'missing authorization' do
|
||||
context 'personal project' do
|
||||
before do
|
||||
user_namespace = namespaces_table.create!(owner_id: user.id, name: 'User', path: 'user')
|
||||
projects_table.create!(id: 1,
|
||||
name: 'personal-project',
|
||||
path: 'personal-project',
|
||||
visibility_level: 0,
|
||||
namespace_id: user_namespace.id)
|
||||
end
|
||||
|
||||
it 'creates correct authorization' do
|
||||
expect { subject }.to change { project_authorizations_table.count }.from(0).to(1)
|
||||
expect(project_authorizations_table.all).to(
|
||||
match_array([have_attributes(user_id: 1, project_id: 1, access_level: 40)]))
|
||||
end
|
||||
end
|
||||
|
||||
context 'group membership' do
|
||||
before do
|
||||
projects_table.create!(id: 1, name: 'group-project', path: 'group-project',
|
||||
visibility_level: 0, namespace_id: group.id)
|
||||
members_table.create!(user_id: user.id, source_id: group.id, source_type: 'Namespace',
|
||||
type: 'GroupMember', access_level: 20, notification_level: 3)
|
||||
end
|
||||
|
||||
it 'creates correct authorization' do
|
||||
expect { subject }.to change { project_authorizations_table.count }.from(0).to(1)
|
||||
expect(project_authorizations_table.all).to(
|
||||
match_array([have_attributes(user_id: 1, project_id: 1, access_level: 20)]))
|
||||
end
|
||||
end
|
||||
|
||||
context 'inherited group membership' do
|
||||
before do
|
||||
sub_group = namespaces_table.create!(type: 'Group', name: 'subgroup',
|
||||
path: 'subgroup', parent_id: group.id)
|
||||
projects_table.create!(id: 1, name: 'group-project', path: 'group-project',
|
||||
visibility_level: 0, namespace_id: sub_group.id)
|
||||
members_table.create!(user_id: user.id, source_id: group.id, source_type: 'Namespace',
|
||||
type: 'GroupMember', access_level: 20, notification_level: 3)
|
||||
end
|
||||
|
||||
it 'creates correct authorization' do
|
||||
expect { subject }.to change { project_authorizations_table.count }.from(0).to(1)
|
||||
expect(project_authorizations_table.all).to(
|
||||
match_array([have_attributes(user_id: 1, project_id: 1, access_level: 20)]))
|
||||
end
|
||||
end
|
||||
|
||||
context 'project membership' do
|
||||
before do
|
||||
project = projects_table.create!(id: 1, name: 'group-project', path: 'group-project',
|
||||
visibility_level: 0, namespace_id: group.id)
|
||||
members_table.create!(user_id: user.id, source_id: project.id, source_type: 'Project',
|
||||
type: 'ProjectMember', access_level: 20, notification_level: 3)
|
||||
end
|
||||
|
||||
it 'creates correct authorization' do
|
||||
expect { subject }.to change { project_authorizations_table.count }.from(0).to(1)
|
||||
expect(project_authorizations_table.all).to(
|
||||
match_array([have_attributes(user_id: 1, project_id: 1, access_level: 20)]))
|
||||
end
|
||||
end
|
||||
|
||||
context 'shared group' do
|
||||
before do
|
||||
members_table.create!(user_id: user.id, source_id: group.id, source_type: 'Namespace',
|
||||
type: 'GroupMember', access_level: 30, notification_level: 3)
|
||||
|
||||
shared_group = namespaces_table.create!(type: 'Group', name: 'shared group',
|
||||
path: 'shared-group')
|
||||
projects_table.create!(id: 1, name: 'project', path: 'project', visibility_level: 0,
|
||||
namespace_id: shared_group.id)
|
||||
|
||||
group_group_links.create(shared_group_id: shared_group.id, shared_with_group_id: group.id,
|
||||
group_access: 20)
|
||||
end
|
||||
|
||||
it 'creates correct authorization' do
|
||||
expect { subject }.to change { project_authorizations_table.count }.from(0).to(1)
|
||||
expect(project_authorizations_table.all).to(
|
||||
match_array([have_attributes(user_id: 1, project_id: 1, access_level: 20)]))
|
||||
end
|
||||
end
|
||||
|
||||
context 'shared project' do
|
||||
before do
|
||||
members_table.create!(user_id: user.id, source_id: group.id, source_type: 'Namespace',
|
||||
type: 'GroupMember', access_level: 30, notification_level: 3)
|
||||
|
||||
another_group = namespaces_table.create!(type: 'Group', name: 'another group', path: 'another-group')
|
||||
shared_project = projects_table.create!(id: 1, name: 'shared project', path: 'shared-project',
|
||||
visibility_level: 0, namespace_id: another_group.id)
|
||||
|
||||
project_group_links.create(project_id: shared_project.id, group_id: group.id, group_access: 20)
|
||||
end
|
||||
|
||||
it 'creates correct authorization' do
|
||||
expect { subject }.to change { project_authorizations_table.count }.from(0).to(1)
|
||||
expect(project_authorizations_table.all).to(
|
||||
match_array([have_attributes(user_id: 1, project_id: 1, access_level: 20)]))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'unapproved access requests' do
|
||||
context 'group membership' do
|
||||
before do
|
||||
projects_table.create!(id: 1, name: 'group-project', path: 'group-project',
|
||||
visibility_level: 0, namespace_id: group.id)
|
||||
members_table.create!(user_id: user.id, source_id: group.id, source_type: 'Namespace',
|
||||
type: 'GroupMember', access_level: 20, requested_at: Time.now, notification_level: 3)
|
||||
end
|
||||
|
||||
it 'does not create authorization' do
|
||||
expect { subject }.not_to change { project_authorizations_table.count }.from(0)
|
||||
end
|
||||
end
|
||||
|
||||
context 'inherited group membership' do
|
||||
before do
|
||||
sub_group = namespaces_table.create!(type: 'Group', name: 'subgroup', path: 'subgroup',
|
||||
parent_id: group.id)
|
||||
projects_table.create!(id: 1, name: 'group-project', path: 'group-project',
|
||||
visibility_level: 0, namespace_id: sub_group.id)
|
||||
members_table.create!(user_id: user.id, source_id: group.id, source_type: 'Namespace',
|
||||
type: 'GroupMember', access_level: 20, requested_at: Time.now, notification_level: 3)
|
||||
end
|
||||
|
||||
it 'does not create authorization' do
|
||||
expect { subject }.not_to change { project_authorizations_table.count }.from(0)
|
||||
end
|
||||
end
|
||||
|
||||
context 'project membership' do
|
||||
before do
|
||||
project = projects_table.create!(id: 1, name: 'group-project', path: 'group-project',
|
||||
visibility_level: 0, namespace_id: group.id)
|
||||
members_table.create!(user_id: user.id, source_id: project.id, source_type: 'Project',
|
||||
type: 'ProjectMember', access_level: 20, requested_at: Time.now, notification_level: 3)
|
||||
end
|
||||
|
||||
it 'does not create authorization' do
|
||||
expect { subject }.not_to change { project_authorizations_table.count }.from(0)
|
||||
end
|
||||
end
|
||||
|
||||
context 'shared group' do
|
||||
before do
|
||||
members_table.create!(user_id: user.id, source_id: group.id, source_type: 'Namespace',
|
||||
type: 'GroupMember', access_level: 30, requested_at: Time.now, notification_level: 3)
|
||||
|
||||
shared_group = namespaces_table.create!(type: 'Group', name: 'shared group',
|
||||
path: 'shared-group')
|
||||
projects_table.create!(id: 1, name: 'project', path: 'project', visibility_level: 0,
|
||||
namespace_id: shared_group.id)
|
||||
|
||||
group_group_links.create(shared_group_id: shared_group.id, shared_with_group_id: group.id,
|
||||
group_access: 20)
|
||||
end
|
||||
|
||||
it 'does not create authorization' do
|
||||
expect { subject }.not_to change { project_authorizations_table.count }.from(0)
|
||||
end
|
||||
end
|
||||
|
||||
context 'shared project' do
|
||||
before do
|
||||
members_table.create!(user_id: user.id, source_id: group.id, source_type: 'Namespace',
|
||||
type: 'GroupMember', access_level: 30, requested_at: Time.now, notification_level: 3)
|
||||
|
||||
another_group = namespaces_table.create!(type: 'Group', name: 'another group', path: 'another-group')
|
||||
shared_project = projects_table.create!(id: 1, name: 'shared project', path: 'shared-project',
|
||||
visibility_level: 0, namespace_id: another_group.id)
|
||||
|
||||
project_group_links.create(project_id: shared_project.id, group_id: group.id, group_access: 20)
|
||||
end
|
||||
|
||||
it 'does not create authorization' do
|
||||
expect { subject }.not_to change { project_authorizations_table.count }.from(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'incorrect authorization' do
|
||||
before do
|
||||
project = projects_table.create!(id: 1, name: 'group-project', path: 'group-project',
|
||||
visibility_level: 0, namespace_id: group.id)
|
||||
members_table.create!(user_id: user.id, source_id: group.id, source_type: 'Namespace',
|
||||
type: 'GroupMember', access_level: 30, notification_level: 3)
|
||||
|
||||
project_authorizations_table.create!(user_id: user.id, project_id: project.id,
|
||||
access_level: 10)
|
||||
end
|
||||
|
||||
it 'fixes authorization' do
|
||||
expect { subject }.not_to change { project_authorizations_table.count }.from(1)
|
||||
expect(project_authorizations_table.all).to(
|
||||
match_array([have_attributes(user_id: 1, project_id: 1, access_level: 30)]))
|
||||
end
|
||||
end
|
||||
|
||||
context 'unwanted authorization' do
|
||||
before do
|
||||
project = projects_table.create!(name: 'group-project', path: 'group-project',
|
||||
visibility_level: 0, namespace_id: group.id)
|
||||
|
||||
project_authorizations_table.create!(user_id: user.id, project_id: project.id,
|
||||
access_level: 10)
|
||||
end
|
||||
|
||||
it 'deletes authorization' do
|
||||
expect { subject }.to change { project_authorizations_table.count }.from(1).to(0)
|
||||
end
|
||||
end
|
||||
|
||||
context 'deleted user' do
|
||||
let(:nonexistent_user_id) { User.maximum(:id).to_i + 999 }
|
||||
|
||||
it 'does not fail' do
|
||||
expect { described_class.new.perform([nonexistent_user_id]) }.not_to raise_error
|
||||
end
|
||||
end
|
||||
end
|
117
spec/lib/gitlab/git/cross_repo_comparer_spec.rb
Normal file
117
spec/lib/gitlab/git/cross_repo_comparer_spec.rb
Normal file
|
@ -0,0 +1,117 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Git::CrossRepoComparer do
|
||||
let(:source_project) { create(:project, :repository) }
|
||||
let(:target_project) { create(:project, :repository) }
|
||||
|
||||
let(:source_repo) { source_project.repository.raw_repository }
|
||||
let(:target_repo) { target_project.repository.raw_repository }
|
||||
|
||||
let(:source_branch) { 'feature' }
|
||||
let(:target_branch) { 'master' }
|
||||
let(:straight) { false }
|
||||
|
||||
let(:source_commit) { source_repo.commit(source_branch) }
|
||||
let(:target_commit) { source_repo.commit(target_branch) }
|
||||
|
||||
subject(:result) { described_class.new(source_repo, target_repo).compare(source_branch, target_branch, straight: straight) }
|
||||
|
||||
describe '#compare' do
|
||||
context 'within a single repository' do
|
||||
let(:target_project) { source_project }
|
||||
|
||||
context 'a non-straight comparison' do
|
||||
it 'compares without fetching from another repo' do
|
||||
expect(source_repo).not_to receive(:fetch_source_branch!)
|
||||
|
||||
expect_compare(result, from: source_commit, to: target_commit)
|
||||
expect(result.straight).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'a straight comparison' do
|
||||
let(:straight) { true }
|
||||
|
||||
it 'compares without fetching from another repo' do
|
||||
expect(source_repo).not_to receive(:fetch_source_branch!)
|
||||
|
||||
expect_compare(result, from: source_commit, to: target_commit)
|
||||
expect(result.straight).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'across two repositories' do
|
||||
context 'target ref exists in source repo' do
|
||||
it 'compares without fetching from another repo' do
|
||||
expect(source_repo).not_to receive(:fetch_source_branch!)
|
||||
expect(source_repo).not_to receive(:delete_refs)
|
||||
|
||||
expect_compare(result, from: source_commit, to: target_commit)
|
||||
end
|
||||
end
|
||||
|
||||
context 'target ref does not exist in source repo' do
|
||||
it 'compares in the source repo by fetching from the target to a temporary ref' do
|
||||
new_commit_id = create_commit(target_project.owner, target_repo, target_branch)
|
||||
new_commit = target_repo.commit(new_commit_id)
|
||||
|
||||
# This is how the temporary ref is generated
|
||||
expect(SecureRandom).to receive(:hex).at_least(:once).and_return('foo')
|
||||
|
||||
expect(source_repo)
|
||||
.to receive(:fetch_source_branch!)
|
||||
.with(target_repo, new_commit_id, 'refs/tmp/foo')
|
||||
.and_call_original
|
||||
|
||||
expect(source_repo).to receive(:delete_refs).with('refs/tmp/foo').and_call_original
|
||||
|
||||
expect_compare(result, from: source_commit, to: new_commit)
|
||||
end
|
||||
end
|
||||
|
||||
context 'source ref does not exist in source repo' do
|
||||
let(:source_branch) { 'does-not-exist' }
|
||||
|
||||
it 'returns an empty comparison' do
|
||||
expect(source_repo).not_to receive(:fetch_source_branch!)
|
||||
expect(source_repo).not_to receive(:delete_refs)
|
||||
|
||||
expect(result).to be_a(::Gitlab::Git::Compare)
|
||||
expect(result.commits.size).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
context 'target ref does not exist in target repo' do
|
||||
let(:target_branch) { 'does-not-exist' }
|
||||
|
||||
it 'returns nil' do
|
||||
expect(source_repo).not_to receive(:fetch_source_branch!)
|
||||
expect(source_repo).not_to receive(:delete_refs)
|
||||
|
||||
is_expected.to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def expect_compare(of, from:, to:)
|
||||
expect(of).to be_a(::Gitlab::Git::Compare)
|
||||
expect(from).to be_a(::Gitlab::Git::Commit)
|
||||
expect(to).to be_a(::Gitlab::Git::Commit)
|
||||
|
||||
expect(of.commits).not_to be_empty
|
||||
expect(of.head).to eq(from)
|
||||
expect(of.base).to eq(to)
|
||||
end
|
||||
|
||||
def create_commit(user, repo, branch)
|
||||
action = { action: :create, file_path: '/FILE', content: 'content' }
|
||||
|
||||
result = repo.multi_action(user, branch_name: branch, message: 'Commit', actions: [action])
|
||||
|
||||
result.newrev
|
||||
end
|
||||
end
|
|
@ -1962,66 +1962,15 @@ describe Gitlab::Git::Repository, :seed_helper do
|
|||
end
|
||||
|
||||
describe '#compare_source_branch' do
|
||||
let(:repository) { Gitlab::Git::Repository.new('default', TEST_GITATTRIBUTES_REPO_PATH, '', 'group/project') }
|
||||
it 'delegates to Gitlab::Git::CrossRepoComparer' do
|
||||
expect_next_instance_of(::Gitlab::Git::CrossRepoComparer) do |instance|
|
||||
expect(instance.source_repo).to eq(:source_repository)
|
||||
expect(instance.target_repo).to eq(repository)
|
||||
|
||||
context 'within same repository' do
|
||||
it 'does not create a temp ref' do
|
||||
expect(repository).not_to receive(:fetch_source_branch!)
|
||||
expect(repository).not_to receive(:delete_refs)
|
||||
|
||||
compare = repository.compare_source_branch('master', repository, 'feature', straight: false)
|
||||
expect(compare).to be_a(Gitlab::Git::Compare)
|
||||
expect(compare.commits.count).to be > 0
|
||||
expect(instance).to receive(:compare).with('feature', 'master', straight: :straight)
|
||||
end
|
||||
|
||||
it 'returns empty commits when source ref does not exist' do
|
||||
compare = repository.compare_source_branch('master', repository, 'non-existent-branch', straight: false)
|
||||
|
||||
expect(compare.commits).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'with different repositories' do
|
||||
context 'when ref is known by source repo, but not by target' do
|
||||
before do
|
||||
mutable_repository.write_ref('another-branch', 'feature')
|
||||
end
|
||||
|
||||
it 'creates temp ref' do
|
||||
expect(repository).not_to receive(:fetch_source_branch!)
|
||||
expect(repository).not_to receive(:delete_refs)
|
||||
|
||||
compare = repository.compare_source_branch('master', mutable_repository, 'another-branch', straight: false)
|
||||
expect(compare).to be_a(Gitlab::Git::Compare)
|
||||
expect(compare.commits.count).to be > 0
|
||||
end
|
||||
end
|
||||
|
||||
context 'when ref is known by source and target repos' do
|
||||
before do
|
||||
mutable_repository.write_ref('another-branch', 'feature')
|
||||
repository.write_ref('another-branch', 'feature')
|
||||
end
|
||||
|
||||
it 'does not create a temp ref' do
|
||||
expect(repository).not_to receive(:fetch_source_branch!)
|
||||
expect(repository).not_to receive(:delete_refs)
|
||||
|
||||
compare = repository.compare_source_branch('master', mutable_repository, 'another-branch', straight: false)
|
||||
expect(compare).to be_a(Gitlab::Git::Compare)
|
||||
expect(compare.commits.count).to be > 0
|
||||
end
|
||||
end
|
||||
|
||||
context 'when ref is unknown by source repo' do
|
||||
it 'returns nil when source ref does not exist' do
|
||||
expect(repository).to receive(:fetch_source_branch!).and_call_original
|
||||
expect(repository).to receive(:delete_refs).and_call_original
|
||||
|
||||
compare = repository.compare_source_branch('master', mutable_repository, 'non-existent-branch', straight: false)
|
||||
expect(compare).to be_nil
|
||||
end
|
||||
end
|
||||
repository.compare_source_branch('master', :source_repository, 'feature', straight: :straight)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
17
spec/lib/gitlab/no_cache_headers_spec.rb
Normal file
17
spec/lib/gitlab/no_cache_headers_spec.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::NoCacheHeaders do
|
||||
class NoCacheTester
|
||||
include Gitlab::NoCacheHeaders
|
||||
end
|
||||
|
||||
describe "#no_cache_headers" do
|
||||
subject { NoCacheTester.new }
|
||||
|
||||
it "raises a RuntimeError" do
|
||||
expect { subject.no_cache_headers }.to raise_error(RuntimeError)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -97,87 +97,68 @@ describe Gitlab::ProjectAuthorizations do
|
|||
create(:group_group_link, shared_group: shared_group, shared_with_group: group)
|
||||
end
|
||||
|
||||
context 'when feature flag share_group_with_group is enabled' do
|
||||
before do
|
||||
stub_feature_flags(share_group_with_group: true)
|
||||
end
|
||||
context 'group user' do
|
||||
let(:user) { group_user }
|
||||
|
||||
context 'group user' do
|
||||
let(:user) { group_user }
|
||||
it 'creates proper authorizations' do
|
||||
mapping = map_access_levels(authorizations)
|
||||
|
||||
it 'creates proper authorizations' do
|
||||
mapping = map_access_levels(authorizations)
|
||||
|
||||
expect(mapping[project_parent.id]).to be_nil
|
||||
expect(mapping[project.id]).to eq(Gitlab::Access::DEVELOPER)
|
||||
expect(mapping[project_child.id]).to eq(Gitlab::Access::DEVELOPER)
|
||||
end
|
||||
end
|
||||
|
||||
context 'parent group user' do
|
||||
let(:user) { parent_group_user }
|
||||
|
||||
it 'creates proper authorizations' do
|
||||
mapping = map_access_levels(authorizations)
|
||||
|
||||
expect(mapping[project_parent.id]).to be_nil
|
||||
expect(mapping[project.id]).to be_nil
|
||||
expect(mapping[project_child.id]).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'child group user' do
|
||||
let(:user) { child_group_user }
|
||||
|
||||
it 'creates proper authorizations' do
|
||||
mapping = map_access_levels(authorizations)
|
||||
|
||||
expect(mapping[project_parent.id]).to be_nil
|
||||
expect(mapping[project.id]).to be_nil
|
||||
expect(mapping[project_child.id]).to be_nil
|
||||
end
|
||||
expect(mapping[project_parent.id]).to be_nil
|
||||
expect(mapping[project.id]).to eq(Gitlab::Access::DEVELOPER)
|
||||
expect(mapping[project_child.id]).to eq(Gitlab::Access::DEVELOPER)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when feature flag share_group_with_group is disabled' do
|
||||
before do
|
||||
stub_feature_flags(share_group_with_group: false)
|
||||
context 'parent group user' do
|
||||
let(:user) { parent_group_user }
|
||||
|
||||
it 'creates proper authorizations' do
|
||||
mapping = map_access_levels(authorizations)
|
||||
|
||||
expect(mapping[project_parent.id]).to be_nil
|
||||
expect(mapping[project.id]).to be_nil
|
||||
expect(mapping[project_child.id]).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'group user' do
|
||||
let(:user) { group_user }
|
||||
context 'child group user' do
|
||||
let(:user) { child_group_user }
|
||||
|
||||
it 'creates proper authorizations' do
|
||||
mapping = map_access_levels(authorizations)
|
||||
it 'creates proper authorizations' do
|
||||
mapping = map_access_levels(authorizations)
|
||||
|
||||
expect(mapping[project_parent.id]).to be_nil
|
||||
expect(mapping[project.id]).to be_nil
|
||||
expect(mapping[project_child.id]).to be_nil
|
||||
end
|
||||
expect(mapping[project_parent.id]).to be_nil
|
||||
expect(mapping[project.id]).to be_nil
|
||||
expect(mapping[project_child.id]).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'parent group user' do
|
||||
let(:user) { parent_group_user }
|
||||
context 'user without accepted access request' do
|
||||
let!(:user) { create(:user) }
|
||||
|
||||
it 'creates proper authorizations' do
|
||||
mapping = map_access_levels(authorizations)
|
||||
it 'does not have access to group and its projects' do
|
||||
create(:group_member, :developer, :access_request, user: user, group: group)
|
||||
|
||||
expect(mapping[project_parent.id]).to be_nil
|
||||
expect(mapping[project.id]).to be_nil
|
||||
expect(mapping[project_child.id]).to be_nil
|
||||
end
|
||||
mapping = map_access_levels(authorizations)
|
||||
|
||||
expect(mapping[project_parent.id]).to be_nil
|
||||
expect(mapping[project.id]).to be_nil
|
||||
expect(mapping[project_child.id]).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'child group user' do
|
||||
let(:user) { child_group_user }
|
||||
context 'unrelated project owner' do
|
||||
let(:common_id) { [Project.maximum(:id).to_i, Namespace.maximum(:id).to_i].max + 999 }
|
||||
let!(:group) { create(:group, id: common_id) }
|
||||
let!(:unrelated_project) { create(:project, id: common_id) }
|
||||
let(:user) { unrelated_project.owner }
|
||||
|
||||
it 'creates proper authorizations' do
|
||||
mapping = map_access_levels(authorizations)
|
||||
it 'does not have access to group and its projects' do
|
||||
mapping = map_access_levels(authorizations)
|
||||
|
||||
expect(mapping[project_parent.id]).to be_nil
|
||||
expect(mapping[project.id]).to be_nil
|
||||
expect(mapping[project_child.id]).to be_nil
|
||||
end
|
||||
expect(mapping[project_parent.id]).to be_nil
|
||||
expect(mapping[project.id]).to be_nil
|
||||
expect(mapping[project_child.id]).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::ReferenceExtractor do
|
||||
let(:project) { create(:project) }
|
||||
let_it_be(:project) { create(:project) }
|
||||
|
||||
before do
|
||||
project.add_developer(project.creator)
|
||||
|
@ -293,4 +293,34 @@ describe Gitlab::ReferenceExtractor do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#references' do
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:issue) { create(:issue, project: project) }
|
||||
let(:text) { "Ref. #{issue.to_reference}" }
|
||||
|
||||
subject { described_class.new(project, user) }
|
||||
|
||||
before do
|
||||
subject.analyze(text)
|
||||
end
|
||||
|
||||
context 'when references are visible' do
|
||||
before do
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
it 'returns visible references of given type' do
|
||||
expect(subject.references(:issue)).to eq([issue])
|
||||
end
|
||||
|
||||
it 'does not increase stateful_not_visible_counter' do
|
||||
expect { subject.references(:issue) }.not_to change { subject.stateful_not_visible_counter }
|
||||
end
|
||||
end
|
||||
|
||||
it 'increases stateful_not_visible_counter' do
|
||||
expect { subject.references(:issue) }.to change { subject.stateful_not_visible_counter }.by(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require Rails.root.join('db', 'post_migrate', '20200204113223_schedule_recalculate_project_authorizations.rb')
|
||||
|
||||
describe ScheduleRecalculateProjectAuthorizations, :migration, :sidekiq do
|
||||
let(:users_table) { table(:users) }
|
||||
let(:namespaces_table) { table(:namespaces) }
|
||||
let(:projects_table) { table(:projects) }
|
||||
let(:project_authorizations_table) { table(:project_authorizations) }
|
||||
|
||||
let(:user1) { users_table.create!(name: 'user1', email: 'user1@example.com', projects_limit: 1) }
|
||||
let(:user2) { users_table.create!(name: 'user2', email: 'user2@example.com', projects_limit: 1) }
|
||||
let(:group) { namespaces_table.create!(id: 1, type: 'Group', name: 'group', path: 'group') }
|
||||
let(:project) do
|
||||
projects_table.create!(id: 1, name: 'project', path: 'project',
|
||||
visibility_level: 0, namespace_id: group.id)
|
||||
end
|
||||
|
||||
before do
|
||||
stub_const("#{described_class}::BATCH_SIZE", 1)
|
||||
|
||||
project_authorizations_table.create!(user_id: user1.id, project_id: project.id, access_level: 30)
|
||||
project_authorizations_table.create!(user_id: user2.id, project_id: project.id, access_level: 30)
|
||||
end
|
||||
|
||||
it 'schedules background migration' do
|
||||
Sidekiq::Testing.fake! do
|
||||
Timecop.freeze do
|
||||
migrate!
|
||||
|
||||
expect(BackgroundMigrationWorker.jobs.size).to eq(2)
|
||||
expect(described_class::MIGRATION).to be_scheduled_migration([user1.id])
|
||||
expect(described_class::MIGRATION).to be_scheduled_migration([user2.id])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'ignores projects with higher id than maximum group id' do
|
||||
another_user = users_table.create!(name: 'another user', email: 'another-user@example.com',
|
||||
projects_limit: 1)
|
||||
ignored_project = projects_table.create!(id: 2, name: 'ignored-project', path: 'ignored-project',
|
||||
visibility_level: 0, namespace_id: group.id)
|
||||
project_authorizations_table.create!(user_id: another_user.id, project_id: ignored_project.id,
|
||||
access_level: 30)
|
||||
|
||||
Sidekiq::Testing.fake! do
|
||||
Timecop.freeze do
|
||||
migrate!
|
||||
|
||||
expect(BackgroundMigrationWorker.jobs.size).to eq(2)
|
||||
expect(described_class::MIGRATION).to be_scheduled_migration([user1.id])
|
||||
expect(described_class::MIGRATION).to be_scheduled_migration([user2.id])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -19,6 +19,74 @@ describe GenericCommitStatus do
|
|||
it { is_expected.not_to allow_value('javascript:alert(1)').for(:target_url) }
|
||||
end
|
||||
|
||||
describe '#name_uniqueness_across_types' do
|
||||
let(:attributes) { {} }
|
||||
let(:commit_status) { described_class.new(attributes) }
|
||||
let(:status_name) { 'test-job' }
|
||||
|
||||
subject(:errors) { commit_status.errors[:name] }
|
||||
|
||||
shared_examples 'it does not have uniqueness errors' do
|
||||
it 'does not return errors' do
|
||||
commit_status.valid?
|
||||
|
||||
is_expected.to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'without attributes' do
|
||||
it_behaves_like 'it does not have uniqueness errors'
|
||||
end
|
||||
|
||||
context 'with only a pipeline' do
|
||||
let(:attributes) { { pipeline: pipeline } }
|
||||
|
||||
context 'without name' do
|
||||
it_behaves_like 'it does not have uniqueness errors'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with only a name' do
|
||||
let(:attributes) { { name: status_name } }
|
||||
|
||||
context 'without pipeline' do
|
||||
it_behaves_like 'it does not have uniqueness errors'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with pipeline and name' do
|
||||
let(:attributes) do
|
||||
{
|
||||
pipeline: pipeline,
|
||||
name: status_name
|
||||
}
|
||||
end
|
||||
|
||||
context 'without other statuses' do
|
||||
it_behaves_like 'it does not have uniqueness errors'
|
||||
end
|
||||
|
||||
context 'with generic statuses' do
|
||||
before do
|
||||
create(:generic_commit_status, pipeline: pipeline, name: status_name)
|
||||
end
|
||||
|
||||
it_behaves_like 'it does not have uniqueness errors'
|
||||
end
|
||||
|
||||
context 'with ci_build statuses' do
|
||||
before do
|
||||
create(:ci_build, pipeline: pipeline, name: status_name)
|
||||
end
|
||||
|
||||
it 'returns name error' do
|
||||
expect(commit_status).to be_invalid
|
||||
is_expected.to include('has already been taken')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#context' do
|
||||
subject { generic_commit_status.context }
|
||||
|
||||
|
@ -79,6 +147,12 @@ describe GenericCommitStatus do
|
|||
|
||||
it { is_expected.not_to be_nil }
|
||||
end
|
||||
|
||||
describe '#stage_idx' do
|
||||
subject { generic_commit_status.stage_idx }
|
||||
|
||||
it { is_expected.not_to be_nil }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#present' do
|
||||
|
|
|
@ -9,7 +9,7 @@ describe GrafanaIntegration do
|
|||
|
||||
describe 'validations' do
|
||||
it { is_expected.to validate_presence_of(:project) }
|
||||
it { is_expected.to validate_presence_of(:token) }
|
||||
it { is_expected.to validate_presence_of(:encrypted_token) }
|
||||
|
||||
it 'disallows invalid urls for grafana_url' do
|
||||
unsafe_url = %{https://replaceme.com/'><script>alert(document.cookie)</script>}
|
||||
|
@ -66,4 +66,24 @@ describe GrafanaIntegration do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'attribute encryption' do
|
||||
subject(:grafana_integration) { create(:grafana_integration, token: 'super-secret') }
|
||||
|
||||
context 'token' do
|
||||
it 'encrypts original value into encrypted_token attribute' do
|
||||
expect(grafana_integration.encrypted_token).not_to be_nil
|
||||
end
|
||||
|
||||
it 'locks access to raw value in private method', :aggregate_failures do
|
||||
expect { grafana_integration.token }.to raise_error(NoMethodError, /private method .token. called/)
|
||||
expect(grafana_integration.send(:token)).to eql('super-secret')
|
||||
end
|
||||
|
||||
it 'prevents overriding token value with its encrypted or masked version', :aggregate_failures do
|
||||
expect { grafana_integration.update(token: grafana_integration.encrypted_token) }.not_to change { grafana_integration.reload.send(:token) }
|
||||
expect { grafana_integration.update(token: grafana_integration.masked_token) }.not_to change { grafana_integration.reload.send(:token) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -350,12 +350,12 @@ describe Note do
|
|||
end
|
||||
|
||||
describe "cross_reference_not_visible_for?" do
|
||||
let(:private_user) { create(:user) }
|
||||
let(:private_project) { create(:project, namespace: private_user.namespace) { |p| p.add_maintainer(private_user) } }
|
||||
let(:private_issue) { create(:issue, project: private_project) }
|
||||
let_it_be(:private_user) { create(:user) }
|
||||
let_it_be(:private_project) { create(:project, namespace: private_user.namespace) { |p| p.add_maintainer(private_user) } }
|
||||
let_it_be(:private_issue) { create(:issue, project: private_project) }
|
||||
|
||||
let(:ext_proj) { create(:project, :public) }
|
||||
let(:ext_issue) { create(:issue, project: ext_proj) }
|
||||
let_it_be(:ext_proj) { create(:project, :public) }
|
||||
let_it_be(:ext_issue) { create(:issue, project: ext_proj) }
|
||||
|
||||
shared_examples "checks references" do
|
||||
it "returns true" do
|
||||
|
@ -393,10 +393,24 @@ describe Note do
|
|||
it_behaves_like "checks references"
|
||||
end
|
||||
|
||||
context "when there are two references in note" do
|
||||
context "when there is a reference to a label" do
|
||||
let_it_be(:private_label) { create(:label, project: private_project) }
|
||||
let(:note) do
|
||||
create :note,
|
||||
noteable: ext_issue, project: ext_proj,
|
||||
note: "added label #{private_label.to_reference(ext_proj)}",
|
||||
system: true
|
||||
end
|
||||
let!(:system_note_metadata) { create(:system_note_metadata, note: note, action: :label) }
|
||||
|
||||
it_behaves_like "checks references"
|
||||
end
|
||||
|
||||
context "when there are two references in note" do
|
||||
let_it_be(:ext_issue2) { create(:issue, project: ext_proj) }
|
||||
let(:note) do
|
||||
create :note,
|
||||
noteable: ext_issue2, project: ext_proj,
|
||||
note: "mentioned in issue #{private_issue.to_reference(ext_proj)} and " \
|
||||
"public issue #{ext_issue.to_reference(ext_proj)}",
|
||||
system: true
|
||||
|
|
|
@ -141,6 +141,34 @@ describe GlobalPolicy do
|
|||
it { is_expected.to be_allowed(:access_api) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'inactive user' do
|
||||
before do
|
||||
current_user.update!(confirmed_at: nil, confirmation_sent_at: 5.days.ago)
|
||||
end
|
||||
|
||||
context 'when within the confirmation grace period' do
|
||||
before do
|
||||
allow(User).to receive(:allow_unconfirmed_access_for).and_return(10.days)
|
||||
end
|
||||
|
||||
it { is_expected.to be_allowed(:access_api) }
|
||||
end
|
||||
|
||||
context 'when confirmation grace period is expired' do
|
||||
before do
|
||||
allow(User).to receive(:allow_unconfirmed_access_for).and_return(2.days)
|
||||
end
|
||||
|
||||
it { is_expected.not_to be_allowed(:access_api) }
|
||||
end
|
||||
|
||||
it 'when `inactive_policy_condition` feature flag is turned off' do
|
||||
stub_feature_flags(inactive_policy_condition: false)
|
||||
|
||||
is_expected.to be_allowed(:access_api)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'receive notifications' do
|
||||
|
@ -202,6 +230,20 @@ describe GlobalPolicy do
|
|||
it { is_expected.not_to be_allowed(:access_git) }
|
||||
end
|
||||
|
||||
describe 'inactive user' do
|
||||
before do
|
||||
current_user.update!(confirmed_at: nil)
|
||||
end
|
||||
|
||||
it { is_expected.not_to be_allowed(:access_git) }
|
||||
|
||||
it 'when `inactive_policy_condition` feature flag is turned off' do
|
||||
stub_feature_flags(inactive_policy_condition: false)
|
||||
|
||||
is_expected.to be_allowed(:access_git)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when terms are enforced' do
|
||||
before do
|
||||
enforce_terms
|
||||
|
@ -298,6 +340,20 @@ describe GlobalPolicy do
|
|||
it { is_expected.not_to be_allowed(:use_slash_commands) }
|
||||
end
|
||||
|
||||
describe 'inactive user' do
|
||||
before do
|
||||
current_user.update!(confirmed_at: nil)
|
||||
end
|
||||
|
||||
it { is_expected.not_to be_allowed(:use_slash_commands) }
|
||||
|
||||
it 'when `inactive_policy_condition` feature flag is turned off' do
|
||||
stub_feature_flags(inactive_policy_condition: false)
|
||||
|
||||
is_expected.to be_allowed(:use_slash_commands)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when access locked' do
|
||||
before do
|
||||
current_user.lock_access!
|
||||
|
|
|
@ -164,6 +164,7 @@ describe API::CommitStatuses do
|
|||
|
||||
expect(response).to have_gitlab_http_status(201)
|
||||
expect(job.status).to eq('pending')
|
||||
expect(job.stage_idx).to eq(GenericCommitStatus::EXTERNAL_STAGE_IDX)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -331,6 +332,29 @@ describe API::CommitStatuses do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when updating a protected ref' do
|
||||
before do
|
||||
create(:protected_branch, project: project, name: 'master')
|
||||
post api(post_url, user), params: { state: 'running', ref: 'master' }
|
||||
end
|
||||
|
||||
context 'with user as developer' do
|
||||
let(:user) { developer }
|
||||
|
||||
it 'does not create commit status' do
|
||||
expect(response).to have_gitlab_http_status(403)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with user as maintainer' do
|
||||
let(:user) { create_user(:maintainer) }
|
||||
|
||||
it 'creates commit status' do
|
||||
expect(response).to have_gitlab_http_status(201)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when commit SHA is invalid' do
|
||||
let(:sha) { 'invalid_sha' }
|
||||
|
||||
|
@ -372,6 +396,22 @@ describe API::CommitStatuses do
|
|||
.to include 'is blocked: Only allowed schemes are http, https'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when trying to update a status of a different type' do
|
||||
let!(:pipeline) { create(:ci_pipeline, project: project, sha: sha, ref: 'ref') }
|
||||
let!(:ci_build) { create(:ci_build, pipeline: pipeline, name: 'test-job') }
|
||||
let(:params) { { state: 'pending', name: 'test-job' } }
|
||||
|
||||
before do
|
||||
post api(post_url, developer), params: params
|
||||
end
|
||||
|
||||
it 'responds with bad request status and validation errors' do
|
||||
expect(response).to have_gitlab_http_status(400)
|
||||
expect(json_response['message']['name'])
|
||||
.to include 'has already been taken'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'reporter user' do
|
||||
|
|
|
@ -8,6 +8,7 @@ describe API::Commits do
|
|||
|
||||
let(:user) { create(:user) }
|
||||
let(:guest) { create(:user).tap { |u| project.add_guest(u) } }
|
||||
let(:developer) { create(:user).tap { |u| project.add_developer(u) } }
|
||||
let(:project) { create(:project, :repository, creator: user, path: 'my.project') }
|
||||
let(:branch_with_dot) { project.repository.find_branch('ends-with.json') }
|
||||
let(:branch_with_slash) { project.repository.find_branch('improve/awesome') }
|
||||
|
@ -964,6 +965,56 @@ describe API::Commits do
|
|||
end
|
||||
end
|
||||
|
||||
shared_examples_for 'ref with pipeline' do
|
||||
let!(:pipeline) do
|
||||
project
|
||||
.ci_pipelines
|
||||
.create!(source: :push, ref: 'master', sha: commit.sha, protected: false)
|
||||
end
|
||||
|
||||
it 'includes status as "created" and a last_pipeline object' do
|
||||
get api(route, current_user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to match_response_schema('public_api/v4/commit/detail')
|
||||
expect(json_response['status']).to eq('created')
|
||||
expect(json_response['last_pipeline']['id']).to eq(pipeline.id)
|
||||
expect(json_response['last_pipeline']['ref']).to eq(pipeline.ref)
|
||||
expect(json_response['last_pipeline']['sha']).to eq(pipeline.sha)
|
||||
expect(json_response['last_pipeline']['status']).to eq(pipeline.status)
|
||||
end
|
||||
|
||||
context 'when pipeline succeeds' do
|
||||
before do
|
||||
pipeline.update!(status: 'success')
|
||||
end
|
||||
|
||||
it 'includes a "success" status' do
|
||||
get api(route, current_user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to match_response_schema('public_api/v4/commit/detail')
|
||||
expect(json_response['status']).to eq('success')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples_for 'ref with unaccessible pipeline' do
|
||||
let!(:pipeline) do
|
||||
project
|
||||
.ci_pipelines
|
||||
.create!(source: :push, ref: 'master', sha: commit.sha, protected: false)
|
||||
end
|
||||
|
||||
it 'does not include last_pipeline' do
|
||||
get api(route, current_user)
|
||||
|
||||
expect(response).to match_response_schema('public_api/v4/commit/detail')
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(json_response['last_pipeline']).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when stat param' do
|
||||
let(:route) { "/projects/#{project_id}/repository/commits/#{commit_id}" }
|
||||
|
||||
|
@ -993,6 +1044,15 @@ describe API::Commits do
|
|||
let(:project) { create(:project, :public, :repository) }
|
||||
|
||||
it_behaves_like 'ref commit'
|
||||
it_behaves_like 'ref with pipeline'
|
||||
|
||||
context 'with private builds' do
|
||||
before do
|
||||
project.project_feature.update!(builds_access_level: ProjectFeature::PRIVATE)
|
||||
end
|
||||
|
||||
it_behaves_like 'ref with unaccessible pipeline'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when unauthenticated', 'and project is private' do
|
||||
|
@ -1006,6 +1066,17 @@ describe API::Commits do
|
|||
let(:current_user) { user }
|
||||
|
||||
it_behaves_like 'ref commit'
|
||||
it_behaves_like 'ref with pipeline'
|
||||
|
||||
context 'when builds are disabled' do
|
||||
before do
|
||||
project
|
||||
.project_feature
|
||||
.update!(builds_access_level: ProjectFeature::DISABLED)
|
||||
end
|
||||
|
||||
it_behaves_like 'ref with unaccessible pipeline'
|
||||
end
|
||||
|
||||
context 'when branch contains a dot' do
|
||||
let(:commit) { project.repository.commit(branch_with_dot.name) }
|
||||
|
@ -1041,35 +1112,53 @@ describe API::Commits do
|
|||
it_behaves_like 'ref commit'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the ref has a pipeline' do
|
||||
let!(:pipeline) { project.ci_pipelines.create(source: :push, ref: 'master', sha: commit.sha, protected: false) }
|
||||
context 'when authenticated', 'as a developer' do
|
||||
let(:current_user) { developer }
|
||||
|
||||
it 'includes a "created" status' do
|
||||
get api(route, current_user)
|
||||
it_behaves_like 'ref commit'
|
||||
it_behaves_like 'ref with pipeline'
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to match_response_schema('public_api/v4/commit/detail')
|
||||
expect(json_response['status']).to eq('created')
|
||||
expect(json_response['last_pipeline']['id']).to eq(pipeline.id)
|
||||
expect(json_response['last_pipeline']['ref']).to eq(pipeline.ref)
|
||||
expect(json_response['last_pipeline']['sha']).to eq(pipeline.sha)
|
||||
expect(json_response['last_pipeline']['status']).to eq(pipeline.status)
|
||||
context 'with private builds' do
|
||||
before do
|
||||
project.project_feature.update!(builds_access_level: ProjectFeature::PRIVATE)
|
||||
end
|
||||
|
||||
context 'when pipeline succeeds' do
|
||||
before do
|
||||
pipeline.update(status: 'success')
|
||||
end
|
||||
it_behaves_like 'ref with pipeline'
|
||||
end
|
||||
end
|
||||
|
||||
it 'includes a "success" status' do
|
||||
get api(route, current_user)
|
||||
context 'when authenticated', 'as a guest' do
|
||||
let(:current_user) { guest }
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to match_response_schema('public_api/v4/commit/detail')
|
||||
expect(json_response['status']).to eq('success')
|
||||
end
|
||||
it_behaves_like '403 response' do
|
||||
let(:request) { get api(route, guest) }
|
||||
let(:message) { '403 Forbidden' }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when authenticated', 'as a non member' do
|
||||
let(:current_user) { create(:user) }
|
||||
|
||||
it_behaves_like '403 response' do
|
||||
let(:request) { get api(route, guest) }
|
||||
let(:message) { '403 Forbidden' }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when authenticated', 'as non_member and project is public' do
|
||||
let(:current_user) { create(:user) }
|
||||
let(:project) { create(:project, :public, :repository) }
|
||||
|
||||
it_behaves_like 'ref with pipeline'
|
||||
|
||||
context 'with private builds' do
|
||||
before do
|
||||
project.project_feature.update!(builds_access_level: ProjectFeature::PRIVATE)
|
||||
end
|
||||
|
||||
it_behaves_like 'ref with unaccessible pipeline'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -447,6 +447,18 @@ describe API::Files do
|
|||
expect(response).to have_gitlab_http_status(200)
|
||||
end
|
||||
|
||||
it 'sets no-cache headers' do
|
||||
url = route('.gitignore') + "/raw"
|
||||
expect(Gitlab::Workhorse).to receive(:send_git_blob)
|
||||
|
||||
get api(url, current_user), params: params
|
||||
|
||||
expect(response.headers["Cache-Control"]).to include("no-store")
|
||||
expect(response.headers["Cache-Control"]).to include("no-cache")
|
||||
expect(response.headers["Pragma"]).to eq("no-cache")
|
||||
expect(response.headers["Expires"]).to eq("Fri, 01 Jan 1990 00:00:00 GMT")
|
||||
end
|
||||
|
||||
context 'when mandatory params are not given' do
|
||||
it_behaves_like '400 response' do
|
||||
let(:request) { get api(route("any%2Ffile"), current_user) }
|
||||
|
|
|
@ -30,26 +30,40 @@ describe 'OAuth tokens' do
|
|||
end
|
||||
end
|
||||
|
||||
context "when user is blocked" do
|
||||
it "does not create an access token" do
|
||||
user = create(:user)
|
||||
shared_examples 'does not create an access token' do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
it { expect(response).to have_gitlab_http_status(401) }
|
||||
end
|
||||
|
||||
context 'when user is blocked' do
|
||||
before do
|
||||
user.block
|
||||
|
||||
request_oauth_token(user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(401)
|
||||
end
|
||||
|
||||
include_examples 'does not create an access token'
|
||||
end
|
||||
|
||||
context "when user is ldap_blocked" do
|
||||
it "does not create an access token" do
|
||||
user = create(:user)
|
||||
context 'when user is ldap_blocked' do
|
||||
before do
|
||||
user.ldap_block
|
||||
|
||||
request_oauth_token(user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(401)
|
||||
end
|
||||
|
||||
include_examples 'does not create an access token'
|
||||
end
|
||||
|
||||
context 'when user account is not confirmed' do
|
||||
before do
|
||||
user.update!(confirmed_at: nil)
|
||||
|
||||
request_oauth_token(user)
|
||||
end
|
||||
|
||||
include_examples 'does not create an access token'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1509,7 +1509,7 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
|
|||
|
||||
authorize_artifacts
|
||||
|
||||
expect(response).to have_gitlab_http_status(500)
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
|
||||
context 'authorization token is invalid' do
|
||||
|
@ -1639,6 +1639,18 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
|
|||
end
|
||||
end
|
||||
|
||||
context 'Is missing GitLab Workhorse token headers' do
|
||||
let(:jwt_token) { JWT.encode({ 'iss' => 'invalid-header' }, Gitlab::Workhorse.secret, 'HS256') }
|
||||
|
||||
it 'fails to post artifacts without GitLab-Workhorse' do
|
||||
expect(Gitlab::ErrorTracking).to receive(:track_exception).once
|
||||
|
||||
upload_artifacts(file_upload, headers_with_token)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when setting an expire date' do
|
||||
let(:default_artifacts_expire_in) {}
|
||||
let(:post_data) do
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Projects::GroupLinks::DestroyService, '#execute' do
|
||||
let(:group_link) { create :project_group_link }
|
||||
let(:project) { group_link.project }
|
||||
let(:project) { create(:project, :private) }
|
||||
let!(:group_link) { create(:project_group_link, project: project) }
|
||||
let(:user) { create :user }
|
||||
let(:subject) { described_class.new(project, user) }
|
||||
|
||||
|
@ -15,4 +15,39 @@ describe Projects::GroupLinks::DestroyService, '#execute' do
|
|||
it 'returns false if group_link is blank' do
|
||||
expect { subject.execute(nil) }.not_to change { project.project_group_links.count }
|
||||
end
|
||||
|
||||
describe 'todos cleanup' do
|
||||
context 'when project is private' do
|
||||
it 'triggers todos cleanup' do
|
||||
expect(TodosDestroyer::ProjectPrivateWorker).to receive(:perform_in).with(Todo::WAIT_FOR_DELETE, project.id)
|
||||
expect(project.private?).to be true
|
||||
|
||||
subject.execute(group_link)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when project is public or internal' do
|
||||
shared_examples_for 'removes confidential todos' do
|
||||
it 'does not trigger todos cleanup' do
|
||||
expect(TodosDestroyer::ProjectPrivateWorker).not_to receive(:perform_in).with(Todo::WAIT_FOR_DELETE, project.id)
|
||||
expect(TodosDestroyer::ConfidentialIssueWorker).to receive(:perform_in).with(Todo::WAIT_FOR_DELETE, nil, project.id)
|
||||
expect(project.private?).to be false
|
||||
|
||||
subject.execute(group_link)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when project is public' do
|
||||
let(:project) { create(:project, :public) }
|
||||
|
||||
it_behaves_like 'removes confidential todos'
|
||||
end
|
||||
|
||||
context 'when project is internal' do
|
||||
let(:project) { create(:project, :public) }
|
||||
|
||||
it_behaves_like 'removes confidential todos'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,6 +10,10 @@ describe Projects::ImportExport::ExportService do
|
|||
let(:service) { described_class.new(project, user) }
|
||||
let!(:after_export_strategy) { Gitlab::ImportExport::AfterExportStrategies::DownloadNotificationStrategy.new }
|
||||
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
end
|
||||
|
||||
it 'saves the version' do
|
||||
expect(Gitlab::ImportExport::VersionSaver).to receive(:new).and_call_original
|
||||
|
||||
|
@ -133,5 +137,18 @@ describe Projects::ImportExport::ExportService do
|
|||
expect(service).not_to receive(:execute_after_export_action)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user does not have admin_project permission' do
|
||||
let!(:another_user) { create(:user) }
|
||||
|
||||
subject(:service) { described_class.new(project, another_user) }
|
||||
|
||||
it 'fails' do
|
||||
expected_message =
|
||||
"User with ID: %s does not have permission to Project %s with ID: %s." %
|
||||
[another_user.id, project.name, project.id]
|
||||
expect { service.execute }.to raise_error(Gitlab::ImportExport::Error).with_message(expected_message)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -210,7 +210,7 @@ describe Projects::Operations::UpdateService do
|
|||
integration = project.reload.grafana_integration
|
||||
|
||||
expect(integration.grafana_url).to eq(expected_attrs[:grafana_url])
|
||||
expect(integration.token).to eq(expected_attrs[:token])
|
||||
expect(integration.send(:token)).to eq(expected_attrs[:token])
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -226,7 +226,7 @@ describe Projects::Operations::UpdateService do
|
|||
integration = project.reload.grafana_integration
|
||||
|
||||
expect(integration.grafana_url).to eq(expected_attrs[:grafana_url])
|
||||
expect(integration.token).to eq(expected_attrs[:token])
|
||||
expect(integration.send(:token)).to eq(expected_attrs[:token])
|
||||
end
|
||||
|
||||
context 'with all grafana attributes blank in params' do
|
||||
|
|
|
@ -5,7 +5,7 @@ require "spec_helper"
|
|||
describe Projects::UpdatePagesService do
|
||||
set(:project) { create(:project, :repository) }
|
||||
set(:pipeline) { create(:ci_pipeline, project: project, sha: project.commit('HEAD').sha) }
|
||||
set(:build) { create(:ci_build, pipeline: pipeline, ref: 'HEAD') }
|
||||
let(:build) { create(:ci_build, pipeline: pipeline, ref: 'HEAD') }
|
||||
let(:invalid_file) { fixture_file_upload('spec/fixtures/dk.png') }
|
||||
|
||||
let(:file) { fixture_file_upload("spec/fixtures/pages.zip") }
|
||||
|
@ -242,6 +242,32 @@ describe Projects::UpdatePagesService do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when file size is spoofed' do
|
||||
let(:metadata) { spy('metadata') }
|
||||
|
||||
include_context 'pages zip with spoofed size'
|
||||
|
||||
before do
|
||||
file = fixture_file_upload(fake_zip_path, 'pages.zip')
|
||||
metafile = fixture_file_upload('spec/fixtures/pages.zip.meta')
|
||||
|
||||
create(:ci_job_artifact, :archive, file: file, job: build)
|
||||
create(:ci_job_artifact, :metadata, file: metafile, job: build)
|
||||
|
||||
allow(build).to receive(:artifacts_metadata_entry)
|
||||
.and_return(metadata)
|
||||
allow(metadata).to receive(:total_size).and_return(100)
|
||||
end
|
||||
|
||||
it 'raises an error' do
|
||||
expect do
|
||||
subject.execute
|
||||
end.to raise_error(Projects::UpdatePagesService::FailedToExtractError,
|
||||
'Entry public/index.html should be 1B but is larger when inflated')
|
||||
expect(deploy_status).to be_script_failure
|
||||
end
|
||||
end
|
||||
|
||||
def deploy_status
|
||||
GenericCommitStatus.find_by(name: 'pages:deploy')
|
||||
end
|
||||
|
|
|
@ -22,6 +22,42 @@ describe Users::RefreshAuthorizedProjectsService do
|
|||
|
||||
service.execute
|
||||
end
|
||||
|
||||
context 'callbacks' do
|
||||
let(:callback) { double('callback') }
|
||||
|
||||
context 'incorrect_auth_found_callback callback' do
|
||||
let(:user) { create(:user) }
|
||||
let(:service) do
|
||||
described_class.new(user,
|
||||
incorrect_auth_found_callback: callback)
|
||||
end
|
||||
|
||||
it 'is called' do
|
||||
access_level = Gitlab::Access::DEVELOPER
|
||||
create(:project_authorization, user: user, project: project, access_level: access_level)
|
||||
|
||||
expect(callback).to receive(:call).with(project.id, access_level).once
|
||||
|
||||
service.execute
|
||||
end
|
||||
end
|
||||
|
||||
context 'missing_auth_found_callback callback' do
|
||||
let(:service) do
|
||||
described_class.new(user,
|
||||
missing_auth_found_callback: callback)
|
||||
end
|
||||
|
||||
it 'is called' do
|
||||
ProjectAuthorization.delete_all
|
||||
|
||||
expect(callback).to receive(:call).with(project.id, Gitlab::Access::MAINTAINER).once
|
||||
|
||||
service.execute
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#execute_without_lease' do
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue