Commit 403678e0 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent f5050253
<script> <script>
import { mapState, mapActions } from 'vuex'; import { mapState, mapGetters, mapActions } from 'vuex';
import { GlSkeletonLoader } from '@gitlab/ui'; import { GlSkeletonLoader } from '@gitlab/ui';
import EditArea from './edit_area.vue'; import EditArea from './edit_area.vue';
...@@ -10,7 +10,8 @@ export default { ...@@ -10,7 +10,8 @@ export default {
GlSkeletonLoader, GlSkeletonLoader,
}, },
computed: { computed: {
...mapState(['content', 'isContentLoaded', 'isLoadingContent']), ...mapState(['content', 'isLoadingContent']),
...mapGetters(['isContentLoaded']),
}, },
mounted() { mounted() {
this.loadContent(); this.loadContent();
......
...@@ -3,7 +3,11 @@ import StaticSiteEditor from './components/static_site_editor.vue'; ...@@ -3,7 +3,11 @@ import StaticSiteEditor from './components/static_site_editor.vue';
import createStore from './store'; import createStore from './store';
const initStaticSiteEditor = el => { const initStaticSiteEditor = el => {
const store = createStore(); const { projectId, path: sourcePath } = el.dataset;
const store = createStore({
initialState: { projectId, sourcePath },
});
return new Vue({ return new Vue({
el, el,
......
import createFlash from '~/flash';
import { __ } from '~/locale';
import * as mutationTypes from './mutation_types';
import loadSourceContent from '~/static_site_editor/services/load_source_content';
export const loadContent = ({ commit, state: { sourcePath, projectId } }) => {
commit(mutationTypes.LOAD_CONTENT);
return loadSourceContent({ sourcePath, projectId })
.then(data => commit(mutationTypes.RECEIVE_CONTENT_SUCCESS, data))
.catch(() => {
commit(mutationTypes.RECEIVE_CONTENT_ERROR);
createFlash(__('An error ocurred while loading your content. Please try again.'));
});
};
export default () => {};
// eslint-disable-next-line import/prefer-default-export
export const isContentLoaded = ({ content }) => Boolean(content);
import Vuex from 'vuex'; import Vuex from 'vuex';
import Vue from 'vue'; import Vue from 'vue';
import createState from './state'; import createState from './state';
import * as getters from './getters';
import * as actions from './actions';
import mutations from './mutations';
Vue.use(Vuex); Vue.use(Vuex);
const createStore = ({ initialState } = {}) => { const createStore = ({ initialState } = {}) => {
return new Vuex.Store({ return new Vuex.Store({
state: createState(initialState), state: createState(initialState),
getters,
actions,
mutations,
}); });
}; };
......
export const LOAD_CONTENT = 'loadContent';
export const RECEIVE_CONTENT_SUCCESS = 'receiveContentSuccess';
export const RECEIVE_CONTENT_ERROR = 'receiveContentError';
import * as types from './mutation_types';
export default {
[types.LOAD_CONTENT](state) {
state.isLoadingContent = true;
},
[types.RECEIVE_CONTENT_SUCCESS](state, { title, content }) {
state.isLoadingContent = false;
state.title = title;
state.content = content;
},
[types.RECEIVE_CONTENT_ERROR](state) {
state.isLoadingContent = false;
},
};
const createState = (initialState = {}) => ({ const createState = (initialState = {}) => ({
projectId: null,
sourcePath: null,
isLoadingContent: false, isLoadingContent: false,
isContentLoaded: false,
content: '', content: '',
title: '',
...initialState, ...initialState,
}); });
......
...@@ -11,11 +11,12 @@ module Ci ...@@ -11,11 +11,12 @@ module Ci
include StaticModel include StaticModel
include Gitlab::Utils::StrongMemoize include Gitlab::Utils::StrongMemoize
attr_reader :stage, :name, :jobs attr_reader :project, :stage, :name, :jobs
delegate :size, to: :jobs delegate :size, to: :jobs
def initialize(stage, name:, jobs:) def initialize(project, stage, name:, jobs:)
@project = project
@stage = stage @stage = stage
@name = name @name = name
@jobs = jobs @jobs = jobs
...@@ -23,7 +24,7 @@ module Ci ...@@ -23,7 +24,7 @@ module Ci
def status def status
strong_memoize(:status) do strong_memoize(:status) do
if Feature.enabled?(:ci_composite_status, default_enabled: false) if Feature.enabled?(:ci_composite_status, project, default_enabled: false)
Gitlab::Ci::Status::Composite Gitlab::Ci::Status::Composite
.new(@jobs) .new(@jobs)
.status .status
...@@ -44,11 +45,11 @@ module Ci ...@@ -44,11 +45,11 @@ module Ci
end end
end end
def self.fabricate(stage) def self.fabricate(project, stage)
stage.statuses.ordered.latest stage.statuses.ordered.latest
.sort_by(&:sortable_name).group_by(&:group_name) .sort_by(&:sortable_name).group_by(&:group_name)
.map do |group_name, grouped_statuses| .map do |group_name, grouped_statuses|
self.new(stage, name: group_name, jobs: grouped_statuses) self.new(project, stage, name: group_name, jobs: grouped_statuses)
end end
end end
end end
......
...@@ -20,7 +20,7 @@ module Ci ...@@ -20,7 +20,7 @@ module Ci
end end
def groups def groups
@groups ||= Ci::Group.fabricate(self) @groups ||= Ci::Group.fabricate(project, self)
end end
def to_param def to_param
......
...@@ -109,7 +109,7 @@ module Ci ...@@ -109,7 +109,7 @@ module Ci
end end
def groups def groups
@groups ||= Ci::Group.fabricate(self) @groups ||= Ci::Group.fabricate(project, self)
end end
def has_warnings? def has_warnings?
......
...@@ -9,9 +9,9 @@ module Ci ...@@ -9,9 +9,9 @@ module Ci
# * No variables # * No variables
# * No spaces # * No spaces
# * Minimal length of 8 characters # * Minimal length of 8 characters
# * Characters must be from the Base64 alphabet (RFC4648) with the addition of @ and : # * Characters must be from the Base64 alphabet (RFC4648) with the addition of '@', ':' and '.'
# * Absolutely no fun is allowed # * Absolutely no fun is allowed
REGEX = /\A[a-zA-Z0-9_+=\/@:-]{8,}\z/.freeze REGEX = /\A[a-zA-Z0-9_+=\/@:.-]{8,}\z/.freeze
included do included do
validates :masked, inclusion: { in: [true, false] } validates :masked, inclusion: { in: [true, false] }
......
...@@ -3,4 +3,5 @@ ...@@ -3,4 +3,5 @@
class AnalyticsSummaryEntity < Grape::Entity class AnalyticsSummaryEntity < Grape::Entity
expose :value, safe: true expose :value, safe: true
expose :title expose :title
expose :unit, if: { with_unit: true }
end end
# frozen_string_literal: true
# This service is responsible for creating a pipeline for a given
# ExternalPullRequest coming from other providers such as GitHub.
module Ci
module ExternalPullRequests
class CreatePipelineService < BaseService
def execute(pull_request)
return unless pull_request.open? && pull_request.actual_branch_head?
create_pipeline_for(pull_request)
end
private
def create_pipeline_for(pull_request)
Ci::CreatePipelineService.new(project, current_user, create_params(pull_request))
.execute(:external_pull_request_event, external_pull_request: pull_request)
end
def create_params(pull_request)
{
ref: pull_request.source_ref,
source_sha: pull_request.source_sha,
target_sha: pull_request.target_sha
}
end
end
end
end
# frozen_string_literal: true
# This service is responsible for creating a pipeline for a given
# ExternalPullRequest coming from other providers such as GitHub.
module ExternalPullRequests
class CreatePipelineService < BaseService
def execute(pull_request)
return unless pull_request.open? && pull_request.actual_branch_head?
create_pipeline_for(pull_request)
end
private
def create_pipeline_for(pull_request)
Ci::CreatePipelineService.new(project, current_user, create_params(pull_request))
.execute(:external_pull_request_event, external_pull_request: pull_request)
end
def create_params(pull_request)
{
ref: pull_request.source_ref,
source_sha: pull_request.source_sha,
target_sha: pull_request.target_sha
}
end
end
end
...@@ -5,4 +5,6 @@ ...@@ -5,4 +5,6 @@
%i.fa.fa-rss %i.fa.fa-rss
.content_list .content_list
= spinner .loading
.spinner.spinner-md
...@@ -20,6 +20,8 @@ ...@@ -20,6 +20,8 @@
= f.text_field :tag_list, value: @project.tag_list.join(', '), maxlength: 2000, class: "form-control" = f.text_field :tag_list, value: @project.tag_list.join(', '), maxlength: 2000, class: "form-control"
%p.form-text.text-muted= _('Separate topics with commas.') %p.form-text.text-muted= _('Separate topics with commas.')
= render_if_exists 'compliance_management/compliance_framework/project_settings', f: f
.row .row
.form-group.col-md-9 .form-group.col-md-9
= f.label :description, _('Project description (optional)'), class: 'label-bold' = f.label :description, _('Project description (optional)'), class: 'label-bold'
......
...@@ -21,7 +21,7 @@ class UpdateExternalPullRequestsWorker # rubocop:disable Scalability/IdempotentW ...@@ -21,7 +21,7 @@ class UpdateExternalPullRequestsWorker # rubocop:disable Scalability/IdempotentW
.by_source_branch(branch) .by_source_branch(branch)
external_pull_requests.find_each do |pull_request| external_pull_requests.find_each do |pull_request|
ExternalPullRequests::CreatePipelineService.new(project, user) Ci::ExternalPullRequests::CreatePipelineService.new(project, user)
.execute(pull_request) .execute(pull_request)
end end
end end
......
---
title: Add read_api scope to personal access tokens for granting read only API access
merge_request: 28944
author:
type: added
---
title: Migrate .fa-spinner to .spinner for app/views/groups
merge_request: 25053
author: nuwe1
type: other
---
title: Create operations_user_lists table
merge_request: 28822
author:
type: added
---
title: Add support for dot (.) in variables masking
merge_request: 29022
author:
type: changed
...@@ -70,6 +70,8 @@ en: ...@@ -70,6 +70,8 @@ en:
scope_desc: scope_desc:
api: api:
Grants complete read/write access to the API, including all groups and projects, the container registry, and the package registry. Grants complete read/write access to the API, including all groups and projects, the container registry, and the package registry.
read_api:
Grants read access to the API, including all groups and projects, the container registry, and the package registry.
read_user: read_user:
Grants read-only access to the authenticated user's profile through the /user API endpoint, which includes username, public email, and full name. Also grants access to read-only API endpoints under /users. Grants read-only access to the authenticated user's profile through the /user API endpoint, which includes username, public email, and full name. Also grants access to read-only API endpoints under /users.
read_repository: read_repository:
......
# frozen_string_literal: true
class AddProjectComplianceFrameworkSettingsTable < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
with_lock_retries do
create_table :project_compliance_framework_settings, id: false do |t|
t.references :project, primary_key: true, null: false, index: true, foreign_key: { on_delete: :cascade }
t.integer :framework, null: false, limit: 2
end
end
end
def down
with_lock_retries do
drop_table :project_compliance_framework_settings
end
end
end
# frozen_string_literal: true
class CreateOperationsUserLists < ActiveRecord::Migration[6.0]
DOWNTIME = false
def change
create_table :operations_user_lists do |t|
t.references :project, index: false, foreign_key: { on_delete: :cascade }, null: false
t.timestamps_with_timezone
t.integer :iid, null: false
t.string :name, null: false, limit: 255
t.text :user_xids, null: false, default: ''
t.index [:project_id, :iid], unique: true
t.index [:project_id, :name], unique: true
end
end
end
...@@ -4323,6 +4323,25 @@ CREATE SEQUENCE public.operations_strategies_id_seq ...@@ -4323,6 +4323,25 @@ CREATE SEQUENCE public.operations_strategies_id_seq
ALTER SEQUENCE public.operations_strategies_id_seq OWNED BY public.operations_strategies.id; ALTER SEQUENCE public.operations_strategies_id_seq OWNED BY public.operations_strategies.id;
CREATE TABLE public.operations_user_lists (
id bigint NOT NULL,
project_id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
iid integer NOT NULL,
name character varying(255) NOT NULL,
user_xids text DEFAULT ''::text NOT NULL
);
CREATE SEQUENCE public.operations_user_lists_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.operations_user_lists_id_seq OWNED BY public.operations_user_lists.id;
CREATE TABLE public.packages_build_infos ( CREATE TABLE public.packages_build_infos (
id bigint NOT NULL, id bigint NOT NULL,
package_id integer NOT NULL, package_id integer NOT NULL,
...@@ -4722,6 +4741,20 @@ CREATE SEQUENCE public.project_ci_cd_settings_id_seq ...@@ -4722,6 +4741,20 @@ CREATE SEQUENCE public.project_ci_cd_settings_id_seq
ALTER SEQUENCE public.project_ci_cd_settings_id_seq OWNED BY public.project_ci_cd_settings.id; ALTER SEQUENCE public.project_ci_cd_settings_id_seq OWNED BY public.project_ci_cd_settings.id;
CREATE TABLE public.project_compliance_framework_settings (
project_id bigint NOT NULL,
framework smallint NOT NULL
);
CREATE SEQUENCE public.project_compliance_framework_settings_project_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.project_compliance_framework_settings_project_id_seq OWNED BY public.project_compliance_framework_settings.project_id;
CREATE TABLE public.project_custom_attributes ( CREATE TABLE public.project_custom_attributes (
id integer NOT NULL, id integer NOT NULL,
created_at timestamp with time zone NOT NULL, created_at timestamp with time zone NOT NULL,
...@@ -7275,6 +7308,8 @@ ALTER TABLE ONLY public.operations_scopes ALTER COLUMN id SET DEFAULT nextval('p ...@@ -7275,6 +7308,8 @@ ALTER TABLE ONLY public.operations_scopes ALTER COLUMN id SET DEFAULT nextval('p
ALTER TABLE ONLY public.operations_strategies ALTER COLUMN id SET DEFAULT nextval('public.operations_strategies_id_seq'::regclass); ALTER TABLE ONLY public.operations_strategies ALTER COLUMN id SET DEFAULT nextval('public.operations_strategies_id_seq'::regclass);
ALTER TABLE ONLY public.operations_user_lists ALTER COLUMN id SET DEFAULT nextval('public.operations_user_lists_id_seq'::regclass);
ALTER TABLE ONLY public.packages_build_infos ALTER COLUMN id SET DEFAULT nextval('public.packages_build_infos_id_seq'::regclass); ALTER TABLE ONLY public.packages_build_infos ALTER COLUMN id SET DEFAULT nextval('public.packages_build_infos_id_seq'::regclass);
ALTER TABLE ONLY public.packages_conan_file_metadata ALTER COLUMN id SET DEFAULT nextval('public.packages_conan_file_metadata_id_seq'::regclass); ALTER TABLE ONLY public.packages_conan_file_metadata ALTER COLUMN id SET DEFAULT nextval('public.packages_conan_file_metadata_id_seq'::regclass);
...@@ -7315,6 +7350,8 @@ ALTER TABLE ONLY public.project_auto_devops ALTER COLUMN id SET DEFAULT nextval( ...@@ -7315,6 +7350,8 @@ ALTER TABLE ONLY public.project_auto_devops ALTER COLUMN id SET DEFAULT nextval(
ALTER TABLE ONLY public.project_ci_cd_settings ALTER COLUMN id SET DEFAULT nextval('public.project_ci_cd_settings_id_seq'::regclass); ALTER TABLE ONLY public.project_ci_cd_settings ALTER COLUMN id SET DEFAULT nextval('public.project_ci_cd_settings_id_seq'::regclass);
ALTER TABLE ONLY public.project_compliance_framework_settings ALTER COLUMN project_id SET DEFAULT nextval('public.project_compliance_framework_settings_project_id_seq'::regclass);
ALTER TABLE ONLY public.project_custom_attributes ALTER COLUMN id SET DEFAULT nextval('public.project_custom_attributes_id_seq'::regclass); ALTER TABLE ONLY public.project_custom_attributes ALTER COLUMN id SET DEFAULT nextval('public.project_custom_attributes_id_seq'::regclass);
ALTER TABLE ONLY public.project_daily_statistics ALTER COLUMN id SET DEFAULT nextval('public.project_daily_statistics_id_seq'::regclass); ALTER TABLE ONLY public.project_daily_statistics ALTER COLUMN id SET DEFAULT nextval('public.project_daily_statistics_id_seq'::regclass);
...@@ -8081,6 +8118,9 @@ ALTER TABLE ONLY public.operations_scopes ...@@ -8081,6 +8118,9 @@ ALTER TABLE ONLY public.operations_scopes
ALTER TABLE ONLY public.operations_strategies ALTER TABLE ONLY public.operations_strategies
ADD CONSTRAINT operations_strategies_pkey PRIMARY KEY (id); ADD CONSTRAINT operations_strategies_pkey PRIMARY KEY (id);
ALTER TABLE ONLY public.operations_user_lists
ADD CONSTRAINT operations_user_lists_pkey PRIMARY KEY (id);
ALTER TABLE ONLY public.packages_build_infos ALTER TABLE ONLY public.packages_build_infos
ADD CONSTRAINT packages_build_infos_pkey PRIMARY KEY (id); ADD CONSTRAINT packages_build_infos_pkey PRIMARY KEY (id);
...@@ -8144,6 +8184,9 @@ ALTER TABLE ONLY public.project_auto_devops ...@@ -8144,6 +8184,9 @@ ALTER TABLE ONLY public.project_auto_devops
ALTER TABLE ONLY public.project_ci_cd_settings ALTER TABLE ONLY public.project_ci_cd_settings
ADD CONSTRAINT project_ci_cd_settings_pkey PRIMARY KEY (id); ADD CONSTRAINT project_ci_cd_settings_pkey PRIMARY KEY (id);
ALTER TABLE ONLY public.project_compliance_framework_settings
ADD CONSTRAINT project_compliance_framework_settings_pkey PRIMARY KEY (project_id);
ALTER TABLE ONLY public.project_custom_attributes ALTER TABLE ONLY public.project_custom_attributes
ADD CONSTRAINT project_custom_attributes_pkey PRIMARY KEY (id); ADD CONSTRAINT project_custom_attributes_pkey PRIMARY KEY (id);
...@@ -9644,6 +9687,10 @@ CREATE UNIQUE INDEX index_operations_scopes_on_strategy_id_and_environment_scope ...@@ -9644,6 +9687,10 @@ CREATE UNIQUE INDEX index_operations_scopes_on_strategy_id_and_environment_scope
CREATE INDEX index_operations_strategies_on_feature_flag_id ON public.operations_strategies USING btree (feature_flag_id); CREATE INDEX index_operations_strategies_on_feature_flag_id ON public.operations_strategies USING btree (feature_flag_id);
CREATE UNIQUE INDEX index_operations_user_lists_on_project_id_and_iid ON public.operations_user_lists USING btree (project_id, iid);
CREATE UNIQUE INDEX index_operations_user_lists_on_project_id_and_name ON public.operations_user_lists USING btree (project_id, name);
CREATE UNIQUE INDEX index_packages_build_infos_on_package_id ON public.packages_build_infos USING btree (package_id); CREATE UNIQUE INDEX index_packages_build_infos_on_package_id ON public.packages_build_infos USING btree (package_id);
CREATE INDEX index_packages_build_infos_on_pipeline_id ON public.packages_build_infos USING btree (pipeline_id); CREATE INDEX index_packages_build_infos_on_pipeline_id ON public.packages_build_infos USING btree (pipeline_id);
...@@ -9736,6 +9783,8 @@ CREATE UNIQUE INDEX index_project_auto_devops_on_project_id ON public.project_au ...@@ -9736,6 +9783,8 @@ CREATE UNIQUE INDEX index_project_auto_devops_on_project_id ON public.project_au
CREATE UNIQUE INDEX index_project_ci_cd_settings_on_project_id ON public.project_ci_cd_settings USING btree (project_id); CREATE UNIQUE INDEX index_project_ci_cd_settings_on_project_id ON public.project_ci_cd_settings USING btree (project_id);
CREATE INDEX index_project_compliance_framework_settings_on_project_id ON public.project_compliance_framework_settings USING btree (project_id);
CREATE INDEX index_project_custom_attributes_on_key_and_value ON public.project_custom_attributes USING btree (key, value); CREATE INDEX index_project_custom_attributes_on_key_and_value ON public.project_custom_attributes USING btree (key, value);
CREATE UNIQUE INDEX index_project_custom_attributes_on_project_id_and_key ON public.project_custom_attributes USING btree (project_id, key); CREATE UNIQUE INDEX index_project_custom_attributes_on_project_id_and_key ON public.project_custom_attributes USING btree (project_id, key);
...@@ -10956,6 +11005,9 @@ ALTER TABLE ONLY public.project_deploy_tokens ...@@ -10956,6 +11005,9 @@ ALTER TABLE ONLY public.project_deploy_tokens
ALTER TABLE ONLY public.packages_conan_file_metadata ALTER TABLE ONLY public.packages_conan_file_metadata
ADD CONSTRAINT fk_rails_0afabd9328 FOREIGN KEY (package_file_id) REFERENCES public.packages_package_files(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_0afabd9328 FOREIGN KEY (package_file_id) REFERENCES public.packages_package_files(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.operations_user_lists
ADD CONSTRAINT fk_rails_0c716e079b FOREIGN KEY (project_id) REFERENCES public.projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.geo_node_statuses ALTER TABLE ONLY public.geo_node_statuses
ADD CONSTRAINT fk_rails_0ecc699c2a FOREIGN KEY (geo_node_id) REFERENCES public.geo_nodes(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_0ecc699c2a FOREIGN KEY (geo_node_id) REFERENCES public.geo_nodes(id) ON DELETE CASCADE;
...@@ -11391,6 +11443,9 @@ ALTER TABLE ONLY public.prometheus_alerts ...@@ -11391,6 +11443,9 @@ ALTER TABLE ONLY public.prometheus_alerts
ALTER TABLE ONLY public.term_agreements ALTER TABLE ONLY public.term_agreements
ADD CONSTRAINT fk_rails_6ea6520e4a FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_6ea6520e4a FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.project_compliance_framework_settings
ADD CONSTRAINT fk_rails_6f5294f16c FOREIGN KEY (project_id) REFERENCES public.projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.users_security_dashboard_projects ALTER TABLE ONLY public.users_security_dashboard_projects
ADD CONSTRAINT fk_rails_6f6cf8e66e FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_6f6cf8e66e FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
...@@ -13002,8 +13057,10 @@ COPY "schema_migrations" (version) FROM STDIN; ...@@ -13002,8 +13057,10 @@ COPY "schema_migrations" (version) FROM STDIN;
20200330121000 20200330121000
20200330123739 20200330123739
20200330132913 20200330132913
20200331132103
20200331195952 20200331195952
20200331220930 20200331220930
20200401211005
20200402123926 20200402123926
20200402135250 20200402135250
20200402185044 20200402185044
......
...@@ -43,6 +43,7 @@ the following table. ...@@ -43,6 +43,7 @@ the following table.
| ------------------ | ------------- | ----------- | | ------------------ | ------------- | ----------- |
| `read_user` | [GitLab 8.15](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/5951) | Allows access to the read-only endpoints under `/users`. Essentially, any of the `GET` requests in the [Users API][users] are allowed. | | `read_user` | [GitLab 8.15](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/5951) | Allows access to the read-only endpoints under `/users`. Essentially, any of the `GET` requests in the [Users API][users] are allowed. |
| `api` | [GitLab 8.15](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/5951) | Grants complete read/write access to the API, including all groups and projects, the container registry, and the package registry. | | `api` | [GitLab 8.15](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/5951) | Grants complete read/write access to the API, including all groups and projects, the container registry, and the package registry. |
| `read_api` | [GitLab 12.10](https://https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28944) | Grants read access to the API, including all groups and projects, the container registry, and the package registry. |
| `read_registry` | [GitLab 9.3](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/11845) | Allows to read (pull) [container registry] images if a project is private and authorization is required. | | `read_registry` | [GitLab 9.3](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/11845) | Allows to read (pull) [container registry] images if a project is private and authorization is required. |
| `sudo` | [GitLab 10.2](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/14838) | Allows performing API actions as any user in the system (if the authenticated user is an admin). | | `sudo` | [GitLab 10.2](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/14838) | Allows performing API actions as any user in the system (if the authenticated user is an admin). |
| `read_repository` | [GitLab 10.7](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17894) | Allows read-only access (pull) to the repository through `git clone`. | | `read_repository` | [GitLab 10.7](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17894) | Allows read-only access (pull) to the repository through `git clone`. |
......
...@@ -28,6 +28,7 @@ module API ...@@ -28,6 +28,7 @@ module API
] ]
allow_access_with_scope :api allow_access_with_scope :api
allow_access_with_scope :read_api, if: -> (request) { request.get? }
prefix :api prefix :api
version 'v3', using: :path do version 'v3', using: :path do
......
...@@ -129,6 +129,7 @@ module API ...@@ -129,6 +129,7 @@ module API
:avatar, :avatar,
:suggestion_commit_message, :suggestion_commit_message,
:repository_storage, :repository_storage,
:compliance_framework_setting,
# TODO: remove in API v5, replaced by *_access_level # TODO: remove in API v5, replaced by *_access_level
:issues_enabled, :issues_enabled,
......
...@@ -6,7 +6,7 @@ module Gitlab ...@@ -6,7 +6,7 @@ module Gitlab
IpBlacklisted = Class.new(StandardError) IpBlacklisted = Class.new(StandardError)
# Scopes used for GitLab API access # Scopes used for GitLab API access
API_SCOPES = [:api, :read_user].freeze API_SCOPES = [:api, :read_user, :read_api].freeze
# Scopes used for GitLab Repository access # Scopes used for GitLab Repository access
REPOSITORY_SCOPES = [:read_repository, :write_repository].freeze REPOSITORY_SCOPES = [:read_repository, :write_repository].freeze
...@@ -198,6 +198,7 @@ module Gitlab ...@@ -198,6 +198,7 @@ module Gitlab
def abilities_for_scopes(scopes) def abilities_for_scopes(scopes)
abilities_by_scope = { abilities_by_scope = {
api: full_authentication_abilities, api: full_authentication_abilities,
read_api: read_only_authentication_abilities,
read_registry: [:read_container_image], read_registry: [:read_container_image],
read_repository: [:download_code], read_repository: [:download_code],
write_repository: [:download_code, :push_code] write_repository: [:download_code, :push_code]
......
...@@ -159,6 +159,7 @@ excluded_attributes: ...@@ -159,6 +159,7 @@ excluded_attributes:
- :max_artifacts_size - :max_artifacts_size
- :marked_for_deletion_at - :marked_for_deletion_at
- :marked_for_deletion_by_user_id - :marked_for_deletion_by_user_id
- :compliance_framework_setting
namespaces: namespaces:
- :runners_token - :runners_token
- :runners_token_encrypted - :runners_token_encrypted
......
...@@ -2090,6 +2090,9 @@ msgstr "" ...@@ -2090,6 +2090,9 @@ msgstr ""
msgid "An error occurred. Please try again." msgid "An error occurred. Please try again."
msgstr "" msgstr ""
msgid "An error ocurred while loading your content. Please try again."
msgstr ""
msgid "An instance-level serverless domain already exists." msgid "An instance-level serverless domain already exists."
msgstr "" msgstr ""
...@@ -3830,6 +3833,9 @@ msgstr "" ...@@ -3830,6 +3833,9 @@ msgstr ""
msgid "Choose which status most accurately reflects the current state of this issue:" msgid "Choose which status most accurately reflects the current state of this issue:"
msgstr "" msgstr ""
msgid "Choose your framework"
msgstr ""
msgid "CiStatusLabel|canceled" msgid "CiStatusLabel|canceled"
msgstr "" msgstr ""
...@@ -5236,6 +5242,24 @@ msgstr "" ...@@ -5236,6 +5242,24 @@ msgstr ""
msgid "Compliance Dashboard" msgid "Compliance Dashboard"
msgstr "" msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
msgid "ComplianceFramework|GDPR - General Data Protection Regulation"
msgstr ""
msgid "ComplianceFramework|HIPAA - Health Insurance Portability and Accountability Act"
msgstr ""
msgid "ComplianceFramework|PCI-DSS - Payment Card Industry-Data Security Standard"
msgstr ""
msgid "ComplianceFramework|SOC 2 - Service Organization Control 2"
msgstr ""
msgid "ComplianceFramework|SOX - Sarbanes-Oxley"
msgstr ""
msgid "Confidence: %{confidence}" msgid "Confidence: %{confidence}"
msgstr "" msgstr ""
...@@ -17967,6 +17991,9 @@ msgstr "" ...@@ -17967,6 +17991,9 @@ msgstr ""
msgid "Select projects you want to import." msgid "Select projects you want to import."
msgstr "" msgstr ""
msgid "Select required regulatory standard"
msgstr ""
msgid "Select shards to replicate" msgid "Select shards to replicate"
msgstr "" msgstr ""
......
...@@ -17,11 +17,15 @@ describe('StaticSiteEditor', () => { ...@@ -17,11 +17,15 @@ describe('StaticSiteEditor', () => {
let store; let store;
let loadContentActionMock; let loadContentActionMock;
const buildStore = (initialState = {}) => { const buildStore = ({ initialState, getters } = {}) => {
loadContentActionMock = jest.fn(); loadContentActionMock = jest.fn();
store = new Vuex.Store({ store = new Vuex.Store({
state: createState(initialState), state: createState(initialState),
getters: {
isContentLoaded: () => false,
...getters,
},
actions: { actions: {
loadContent: loadContentActionMock, loadContent: loadContentActionMock,
}, },
...@@ -56,7 +60,7 @@ describe('StaticSiteEditor', () => { ...@@ -56,7 +60,7 @@ describe('StaticSiteEditor', () => {
const content = 'edit area content'; const content = 'edit area content';
beforeEach(() => { beforeEach(() => {
buildStore({ content, isContentLoaded: true }); buildStore({ initialState: { content }, getters: { isContentLoaded: () => true } });
buildWrapper(); buildWrapper();
}); });
...@@ -70,7 +74,7 @@ describe('StaticSiteEditor', () => { ...@@ -70,7 +74,7 @@ describe('StaticSiteEditor', () => {
}); });
it('displays skeleton loader while loading content', () => { it('displays skeleton loader while loading content', () => {
buildStore({ isLoadingContent: true }); buildStore({ initialState: { isLoadingContent: true } });
buildWrapper(); buildWrapper();
expect(wrapper.find(GlSkeletonLoader).exists()).toBe(true); expect(wrapper.find(GlSkeletonLoader).exists()).toBe(true);
......
import testAction from 'helpers/vuex_action_helper';
import createState from '~/static_site_editor/store/state';
import * as actions from '~/static_site_editor/store/actions';
import * as mutationTypes from '~/static_site_editor/store/mutation_types';
import loadSourceContent from '~/static_site_editor/services/load_source_content';
import createFlash from '~/flash';
import {
projectId,
sourcePath,
sourceContentTitle as title,
sourceContent as content,
} from '../mock_data';
jest.mock('~/flash');
jest.mock('~/static_site_editor/services/load_source_content', () => jest.fn());
describe('Static Site Editor Store actions', () => {
let state;
beforeEach(() => {
state = createState({
projectId,
sourcePath,
});
});
describe('loadContent', () => {
describe('on success', () => {
const payload = { title, content };
beforeEach(() => {
loadSourceContent.mockResolvedValueOnce(payload);
});
it('commits receiveContentSuccess', () => {
testAction(
actions.loadContent,
null,
state,
[
{ type: mutationTypes.LOAD_CONTENT },
{ type: mutationTypes.RECEIVE_CONTENT_SUCCESS, payload },
],
[],
);
expect(loadSourceContent).toHaveBeenCalledWith({ projectId, sourcePath });
});
});
describe('on error', () => {
const expectedMutations = [
{ type: mutationTypes.LOAD_CONTENT },
{ type: mutationTypes.RECEIVE_CONTENT_ERROR },
];
beforeEach(() => {
loadSourceContent.mockRejectedValueOnce();
});
it('commits receiveContentError', () => {
testAction(actions.loadContent, null, state, expectedMutations);
});
it('displays flash communicating error', () => {
return testAction(actions.loadContent, null, state, expectedMutations).then(() => {
expect(createFlash).toHaveBeenCalledWith(
'An error ocurred while loading your content. Please try again.',
);
});
});
});
});
});
import createState from '~/static_site_editor/store/state';
import { isContentLoaded } from '~/static_site_editor/store/getters';
import { sourceContent as content } from '../mock_data';
describe('Static Site Editor Store getters', () => {
describe('isContentLoaded', () => {
it('returns true when content is not empty', () => {
expect(isContentLoaded(createState({ content }))).toBe(true);
});
it('returns false when content is empty', () => {
expect(isContentLoaded(createState({ content: '' }))).toBe(false);
});
});
});
import createState from '~/static_site_editor/store/state';
import mutations from '~/static_site_editor/store/mutations';
import * as types from '~/static_site_editor/store/mutation_types';
import { sourceContentTitle as title, sourceContent as content } from '../mock_data';
describe('Static Site Editor Store mutations', () => {
let state;
beforeEach(() => {
state = createState();
});
describe('loadContent', () => {
beforeEach(() => {
mutations[types.LOAD_CONTENT](state);
});
it('sets isLoadingContent to true', () => {
expect(state.isLoadingContent).toBe(true);
});
});
describe('receiveContentSuccess', () => {
const payload = { title, content };
beforeEach(() => {
mutations[types.RECEIVE_CONTENT_SUCCESS](state, payload);
});
it('sets current state to LOADING', () => {
expect(state.isLoadingContent).toBe(false);
});
it('sets title', () => {
expect(state.title).toBe(payload.title);
});
it('sets content', () => {
expect(state.content).toBe(payload.content);
});
});
describe('receiveContentError', () => {
beforeEach(() => {
mutations[types.RECEIVE_CONTENT_ERROR](state);
});
it('sets current state to LOADING_ERROR', () => {
expect(state.isLoadingContent).toBe(false);
});
});
});
...@@ -224,7 +224,7 @@ describe('AjaxFormVariableList', () => { ...@@ -224,7 +224,7 @@ describe('AjaxFormVariableList', () => {
describe('maskableRegex', () => { describe('maskableRegex', () => {
it('takes in the regex provided by the data attribute', () => { it('takes in the regex provided by the data attribute', () => {
expect(container.dataset.maskableRegex).toBe('^[a-zA-Z0-9_+=/@:-]{8,}$'); expect(container.dataset.maskableRegex).toBe('^[a-zA-Z0-9_+=/@:.-]{8,}$');
expect(ajaxVariableList.maskableRegex).toBe(container.dataset.maskableRegex); expect(ajaxVariableList.maskableRegex).toBe(container.dataset.maskableRegex);
}); });
}); });
......
...@@ -162,7 +162,7 @@ describe('VariableList', () => { ...@@ -162,7 +162,7 @@ describe('VariableList', () => {
}); });
it('has a regex provided via a data attribute', () => { it('has a regex provided via a data attribute', () => {
expect($wrapper.attr('data-maskable-regex')).toBe('^[a-zA-Z0-9_+=/@:-]{8,}$'); expect($wrapper.attr('data-maskable-regex')).toBe('^[a-zA-Z0-9_+=/@:.-]{8,}$');
}); });
it('allows values that are 8 characters long', done => { it('allows values that are 8 characters long', done => {
......
...@@ -8,7 +8,7 @@ describe Gitlab::Auth, :use_clean_rails_memory_store_caching do ...@@ -8,7 +8,7 @@ describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
describe 'constants' do describe 'constants' do
it 'API_SCOPES contains all scopes for API access' do it 'API_SCOPES contains all scopes for API access' do
expect(subject::API_SCOPES).to eq %i[api read_user] expect(subject::API_SCOPES).to eq %i[api read_user read_api]
end end
it 'ADMIN_SCOPES contains all scopes for ADMIN access' do it 'ADMIN_SCOPES contains all scopes for ADMIN access' do
...@@ -30,7 +30,7 @@ describe Gitlab::Auth, :use_clean_rails_memory_store_caching do ...@@ -30,7 +30,7 @@ describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
it 'optional_scopes contains all non-default scopes' do it 'optional_scopes contains all non-default scopes' do
stub_container_registry_config(enabled: true) stub_container_registry_config(enabled: true)
expect(subject.optional_scopes).to eq %i[read_user read_repository write_repository read_registry sudo openid profile email] expect(subject.optional_scopes).to eq %i[read_user read_api read_repository write_repository read_registry sudo openid profile email]
end end
end end
...@@ -38,21 +38,21 @@ describe Gitlab::Auth, :use_clean_rails_memory_store_caching do ...@@ -38,21 +38,21 @@ describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
it 'contains all non-default scopes' do it 'contains all non-default scopes' do
stub_container_registry_config(enabled: true) stub_container_registry_config(enabled: true)
expect(subject.all_available_scopes).to eq %i[api read_user read_repository write_repository read_registry sudo] expect(subject.all_available_scopes).to eq %i[api read_user read_api read_repository write_repository read_registry sudo]
end end
it 'contains for non-admin user all non-default scopes without ADMIN access' do it 'contains for non-admin user all non-default scopes without ADMIN access' do
stub_container_registry_config(enabled: true) stub_container_registry_config(enabled: true)
user = create(:user, admin: false) user = create(:user, admin: false)
expect(subject.available_scopes_for(user)).to eq %i[api read_user read_repository write_repository read_registry] expect(subject.available_scopes_for(user)).to eq %i[api read_user read_api read_repository write_repository read_registry]
end end
it 'contains for admin user all non-default scopes with ADMIN access' do it 'contains for admin user all non-default scopes with ADMIN access' do
stub_container_registry_config(enabled: true) stub_container_registry_config(enabled: true)
user = create(:user, admin: true) user = create(:user, admin: true)
expect(subject.available_scopes_for(user)).to eq %i[api read_user read_repository write_repository read_registry sudo] expect(subject.available_scopes_for(user)).to eq %i[api read_user read_api read_repository write_repository read_registry sudo]
end end
context 'registry_scopes' do context 'registry_scopes' do
......
...@@ -477,6 +477,7 @@ project: ...@@ -477,6 +477,7 @@ project:
- export_jobs - export_jobs
- daily_report_results - daily_report_results
- jira_imports - jira_imports
- compliance_framework_setting
award_emoji: award_emoji:
- awardable - awardable
- user - user
......
...@@ -3,12 +3,14 @@ ...@@ -3,12 +3,14 @@
require 'spec_helper' require 'spec_helper'
describe Ci::Group do describe Ci::Group do
let_it_be(:project) { create(:project) }
let!(:jobs) { build_list(:ci_build, 1, :success, project: project) }
subject do subject do
described_class.new('test', name: 'rspec', jobs: jobs) described_class.new(project, 'test', name: 'rspec', jobs: jobs)
end end
let!(:jobs) { build_list(:ci_build, 1, :success) }
it { is_expected.to include_module(StaticModel) } it { is_expected.to include_module(StaticModel) }
it { is_expected.to respond_to(:stage) } it { is_expected.to respond_to(:stage) }
......
...@@ -61,8 +61,12 @@ describe Ci::Maskable do ...@@ -61,8 +61,12 @@ describe Ci::Maskable do
expect(subject.match?(string)).to eq(false) expect(subject.match?(string)).to eq(false)
end end
it 'does not match strings using unsupported characters' do
expect(subject.match?('HelloWorld%#^')).to eq(false)
end
it 'matches valid strings' do it 'matches valid strings' do
expect(subject.match?('helloworld')).to eq(true) expect(subject.match?('Hello+World_123/@:-.')).to eq(true)
end end
end end
......
...@@ -3,15 +3,60 @@ ...@@ -3,15 +3,60 @@
require 'spec_helper' require 'spec_helper'
describe API::API do describe API::API do
let(:user) { create(:user, last_activity_on: Date.yesterday) } include GroupAPIHelpers
describe 'Record user last activity in after hook' do describe 'Record user last activity in after hook' do
# It does not matter which endpoint is used because last_activity_on should # It does not matter which endpoint is used because last_activity_on should
# be updated on every request. `/groups` is used as an example # be updated on every request. `/groups` is used as an example
# to represent any API endpoint # to represent any API endpoint
let(:user) { create(:user, last_activity_on: Date.yesterday) }
it 'updates the users last_activity_on date' do it 'updates the users last_activity_on to the current date' do
expect { get api('/groups', user) }.to change { user.reload.last_activity_on }.to(Date.today) expect { get api('/groups', user) }.to change { user.reload.last_activity_on }.to(Date.today)
end end
end end
describe 'User with only read_api scope personal access token' do
# It does not matter which endpoint is used because this should behave
# in the same way for every request. `/groups` is used as an example
# to represent any API endpoint
context 'when personal access token has only read_api scope' do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:token) { create(:personal_access_token, user: user, scopes: [:read_api]) }
before_all do
group.add_owner(user)
end
it 'does authorize user for get request' do
get api('/groups', personal_access_token: token)
expect(response).to have_gitlab_http_status(:ok)
end
it 'does not authorize user for post request' do
params = attributes_for_group_api
post api("/groups", personal_access_token: token), params: params
expect(response).to have_gitlab_http_status(:forbidden)
end
it 'does not authorize user for put request' do
group_param = { name: 'Test' }
put api("/groups/#{group.id}", personal_access_token: token), params: group_param
expect(response).to have_gitlab_http_status(:forbidden)
end
it 'does not authorize user for delete request' do
delete api("/groups/#{group.id}", personal_access_token: token)
expect(response).to have_gitlab_http_status(:forbidden)
end
end
end
end end
...@@ -180,7 +180,7 @@ describe 'OpenID Connect requests' do ...@@ -180,7 +180,7 @@ describe 'OpenID Connect requests' do
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
expect(json_response['issuer']).to eq('http://localhost') expect(json_response['issuer']).to eq('http://localhost')
expect(json_response['jwks_uri']).to eq('http://www.example.com/oauth/discovery/keys') expect(json_response['jwks_uri']).to eq('http://www.example.com/oauth/discovery/keys')
expect(json_response['scopes_supported']).to eq(%w[api read_user read_repository write_repository sudo openid profile email]) expect(json_response['scopes_supported']).to eq(%w[api read_user read_api read_repository write_repository sudo openid profile email])
end end
end end
......
...@@ -28,4 +28,18 @@ describe AnalyticsSummarySerializer do ...@@ -28,4 +28,18 @@ describe AnalyticsSummarySerializer do
it 'contains important elements of AnalyticsStage' do it 'contains important elements of AnalyticsStage' do
expect(subject).to include(:title, :value) expect(subject).to include(:title, :value)
end end
it 'does not include unit' do
expect(subject).not_to include(:unit)
end
context 'when representing with unit' do
let(:resource) { { title: 'frequency', value: 1.12, unit: 'per day' } }
subject { described_class.new.represent(resource, with_unit: true) }
it 'contains unit' do
expect(subject).to include(:unit)
end
end
end end
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
describe ExternalPullRequests::CreatePipelineService do describe Ci::ExternalPullRequests::CreatePipelineService do
describe '#execute' do describe '#execute' do
let_it_be(:project) { create(:project, :auto_devops, :repository) } let_it_be(:project) { create(:project, :auto_devops, :repository) }
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
......
...@@ -28,10 +28,10 @@ describe UpdateExternalPullRequestsWorker do ...@@ -28,10 +28,10 @@ describe UpdateExternalPullRequestsWorker do
context 'when ref is a branch' do context 'when ref is a branch' do
let(:ref) { 'refs/heads/feature-1' } let(:ref) { 'refs/heads/feature-1' }
let(:create_pipeline_service) { instance_double(ExternalPullRequests::CreatePipelineService) } let(:create_pipeline_service) { instance_double(Ci::ExternalPullRequests::CreatePipelineService) }
it 'runs CreatePipelineService for each pull request matching the source branch and repository' do it 'runs CreatePipelineService for each pull request matching the source branch and repository' do
expect(ExternalPullRequests::CreatePipelineService) expect(Ci::ExternalPullRequests::CreatePipelineService)
.to receive(:new) .to receive(:new)
.and_return(create_pipeline_service) .and_return(create_pipeline_service)
.twice .twice
...@@ -45,7 +45,7 @@ describe UpdateExternalPullRequestsWorker do ...@@ -45,7 +45,7 @@ describe UpdateExternalPullRequestsWorker do
let(:ref) { 'refs/tags/v1.2.3' } let(:ref) { 'refs/tags/v1.2.3' }
it 'does nothing' do it 'does nothing' do
expect(ExternalPullRequests::CreatePipelineService).not_to receive(:new) expect(Ci::ExternalPullRequests::CreatePipelineService).not_to receive(:new)
subject subject
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment