Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
6728ed6fe2
commit
cce7638874
30 changed files with 576 additions and 244 deletions
|
@ -2,7 +2,7 @@
|
|||
import { GlAlert, GlCard, GlToggle, GlLink, GlSkeletonLoader } from '@gitlab/ui';
|
||||
import * as Sentry from '@sentry/browser';
|
||||
import Tracking from '~/tracking';
|
||||
import { __ } from '~/locale';
|
||||
import { __, s__ } from '~/locale';
|
||||
import {
|
||||
TRACK_TOGGLE_TRAINING_PROVIDER_ACTION,
|
||||
TRACK_TOGGLE_TRAINING_PROVIDER_LABEL,
|
||||
|
@ -10,9 +10,12 @@ import {
|
|||
TRACK_PROVIDER_LEARN_MORE_CLICK_LABEL,
|
||||
} from '~/security_configuration/constants';
|
||||
import dismissUserCalloutMutation from '~/graphql_shared/mutations/dismiss_user_callout.mutation.graphql';
|
||||
import { updateSecurityTrainingOptimisticResponse } from '~/security_configuration/graphql/utils/optimistic_response';
|
||||
import securityTrainingProvidersQuery from '../graphql/security_training_providers.query.graphql';
|
||||
import configureSecurityTrainingProvidersMutation from '../graphql/configure_security_training_providers.mutation.graphql';
|
||||
import securityTrainingProvidersQuery from '~/security_configuration/graphql/security_training_providers.query.graphql';
|
||||
import configureSecurityTrainingProvidersMutation from '~/security_configuration/graphql/configure_security_training_providers.mutation.graphql';
|
||||
import {
|
||||
updateSecurityTrainingCache,
|
||||
updateSecurityTrainingOptimisticResponse,
|
||||
} from '~/security_configuration/graphql/cache_utils';
|
||||
|
||||
const i18n = {
|
||||
providerQueryErrorMessage: __(
|
||||
|
@ -21,6 +24,7 @@ const i18n = {
|
|||
configMutationErrorMessage: __(
|
||||
'Could not save configuration. Please refresh the page, or try again later.',
|
||||
),
|
||||
primaryTraining: s__('SecurityTraining|Primary Training'),
|
||||
};
|
||||
|
||||
export default {
|
||||
|
@ -57,6 +61,9 @@ export default {
|
|||
};
|
||||
},
|
||||
computed: {
|
||||
enabledProviders() {
|
||||
return this.securityTrainingProviders.filter(({ isEnabled }) => isEnabled);
|
||||
},
|
||||
isLoading() {
|
||||
return this.$apollo.queries.securityTrainingProviders.loading;
|
||||
},
|
||||
|
@ -91,14 +98,42 @@ export default {
|
|||
Sentry.captureException(e);
|
||||
}
|
||||
},
|
||||
toggleProvider(provider) {
|
||||
const { isEnabled } = provider;
|
||||
async toggleProvider(provider) {
|
||||
const { isEnabled, isPrimary } = provider;
|
||||
const toggledIsEnabled = !isEnabled;
|
||||
|
||||
this.trackProviderToggle(provider.id, toggledIsEnabled);
|
||||
this.storeProvider({ ...provider, isEnabled: toggledIsEnabled });
|
||||
|
||||
// when the current primary provider gets disabled then set the first enabled to be the new primary
|
||||
if (!toggledIsEnabled && isPrimary && this.enabledProviders.length > 1) {
|
||||
const firstOtherEnabledProvider = this.enabledProviders.find(
|
||||
({ id }) => id !== provider.id,
|
||||
);
|
||||
this.setPrimaryProvider(firstOtherEnabledProvider);
|
||||
}
|
||||
|
||||
this.storeProvider({
|
||||
...provider,
|
||||
isEnabled: toggledIsEnabled,
|
||||
});
|
||||
},
|
||||
async storeProvider({ id, isEnabled, isPrimary }) {
|
||||
setPrimaryProvider(provider) {
|
||||
this.storeProvider({ ...provider, isPrimary: true });
|
||||
},
|
||||
async storeProvider(provider) {
|
||||
const { id, isEnabled, isPrimary } = provider;
|
||||
let nextIsPrimary = isPrimary;
|
||||
|
||||
// if the current provider has been disabled it can't be primary
|
||||
if (!isEnabled) {
|
||||
nextIsPrimary = false;
|
||||
}
|
||||
|
||||
// if the current provider is the only enabled provider it should be primary
|
||||
if (isEnabled && !this.enabledProviders.length) {
|
||||
nextIsPrimary = true;
|
||||
}
|
||||
|
||||
try {
|
||||
const {
|
||||
data: {
|
||||
|
@ -111,13 +146,17 @@ export default {
|
|||
projectPath: this.projectFullPath,
|
||||
providerId: id,
|
||||
isEnabled,
|
||||
isPrimary,
|
||||
isPrimary: nextIsPrimary,
|
||||
},
|
||||
},
|
||||
optimisticResponse: updateSecurityTrainingOptimisticResponse({
|
||||
id,
|
||||
isEnabled,
|
||||
isPrimary,
|
||||
isPrimary: nextIsPrimary,
|
||||
}),
|
||||
update: updateSecurityTrainingCache({
|
||||
query: securityTrainingProvidersQuery,
|
||||
variables: { fullPath: this.projectFullPath },
|
||||
}),
|
||||
});
|
||||
|
||||
|
@ -188,6 +227,27 @@ export default {
|
|||
{{ __('Learn more.') }}
|
||||
</gl-link>
|
||||
</p>
|
||||
<!-- Note: The following `div` and it's content will be replaced by 'GlFormRadio' once https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1720#note_857342988 is resolved -->
|
||||
<div
|
||||
class="gl-form-radio custom-control custom-radio"
|
||||
data-testid="primary-provider-radio"
|
||||
>
|
||||
<input
|
||||
:id="`security-training-provider-${provider.id}`"
|
||||
type="radio"
|
||||
:checked="provider.isPrimary"
|
||||
name="radio-group-name"
|
||||
class="custom-control-input"
|
||||
:disabled="!provider.isEnabled"
|
||||
@change="setPrimaryProvider(provider)"
|
||||
/>
|
||||
<label
|
||||
class="custom-control-label"
|
||||
:for="`security-training-provider-${provider.id}`"
|
||||
>
|
||||
{{ $options.i18n.primaryTraining }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</gl-card>
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
import produce from 'immer';
|
||||
|
||||
export const updateSecurityTrainingOptimisticResponse = (changes) => ({
|
||||
// False positive i18n lint: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/26
|
||||
// eslint-disable-next-line @gitlab/require-i18n-strings
|
||||
__typename: 'Mutation',
|
||||
securityTrainingUpdate: {
|
||||
__typename: 'SecurityTrainingUpdatePayload',
|
||||
training: {
|
||||
__typename: 'ProjectSecurityTraining',
|
||||
...changes,
|
||||
},
|
||||
errors: [],
|
||||
},
|
||||
});
|
||||
|
||||
export const updateSecurityTrainingCache = ({ query, variables }) => (cache, { data }) => {
|
||||
const {
|
||||
securityTrainingUpdate: { training: updatedProvider },
|
||||
} = data;
|
||||
const { project } = cache.readQuery({ query, variables });
|
||||
if (!updatedProvider.isPrimary) {
|
||||
return;
|
||||
}
|
||||
|
||||
// when we set a new primary provider, we need to unset the previous one(s)
|
||||
const updatedProject = produce(project, (draft) => {
|
||||
draft.securityTrainingProviders.forEach((provider) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
provider.isPrimary = provider.id === updatedProvider.id;
|
||||
});
|
||||
});
|
||||
|
||||
// write to the cache
|
||||
cache.writeQuery({
|
||||
query,
|
||||
variables,
|
||||
data: { project: updatedProject },
|
||||
});
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
export const updateSecurityTrainingOptimisticResponse = (changes) => ({
|
||||
// False positive i18n lint: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/26
|
||||
// eslint-disable-next-line @gitlab/require-i18n-strings
|
||||
__typename: 'Mutation',
|
||||
securityTrainingUpdate: {
|
||||
__typename: 'SecurityTrainingUpdatePayload',
|
||||
training: {
|
||||
__typename: 'ProjectSecurityTraining',
|
||||
...changes,
|
||||
},
|
||||
errors: [],
|
||||
},
|
||||
});
|
|
@ -127,7 +127,7 @@ module MarkupHelper
|
|||
text = wiki_page.content
|
||||
return '' unless text.present?
|
||||
|
||||
context = render_wiki_content_context(@wiki, wiki_page, context)
|
||||
context = render_wiki_content_context(wiki_page.wiki, wiki_page, context)
|
||||
html = markup_unsafe(wiki_page.path, text, context)
|
||||
|
||||
prepare_for_rendering(html, context)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
.js-groups-list-holder
|
||||
#js-groups-tree{ data: { hide_projects: 'true', endpoint: explore_groups_path(format: :json), path: explore_groups_path, form_sel: 'form#group-filter-form', filter_sel: '.js-groups-list-filter', holder_sel: '.js-groups-list-holder', dropdown_sel: '.js-group-filter-dropdown-wrap' } }
|
||||
.loading-container.text-center.prepend-top-20
|
||||
.gl-spinner.gl-spinner-md
|
||||
= gl_loading_icon(size: 'md', css_class: 'gl-mt-6')
|
||||
|
|
|
@ -9,6 +9,7 @@ type: reference, api
|
|||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/212199) in GitLab 13.5.
|
||||
> - The `encoding` field was [added](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81150) in GitLab 14.9.
|
||||
> - The `render_html` attribute was [added](https://gitlab.com/gitlab-org/gitlab/-/issues/336792) in GitLab 14.9.
|
||||
|
||||
The [group wikis](../user/project/wiki/group.md) API is available only in APIv4.
|
||||
An API for [project wikis](wikis.md) is also available.
|
||||
|
@ -69,6 +70,7 @@ GET /groups/:id/wikis/:slug
|
|||
| --------- | ------- | -------- | --------------------- |
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) |
|
||||
| `slug` | string | yes | URL-encoded slug (a unique string) of the wiki page, such as `dir%2Fpage_name` |
|
||||
| `render_html` | boolean | no | Return the rendered HTML of the wiki page |
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/1/wikis/home"
|
||||
|
|
|
@ -6,7 +6,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
# Project wikis API **(FREE)**
|
||||
|
||||
> The `encoding` field was [added](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81150) in GitLab 14.9.
|
||||
> - The `encoding` field was [added](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81150) in GitLab 14.9.
|
||||
> - The `render_html` attribute was [added](https://gitlab.com/gitlab-org/gitlab/-/issues/336792) in GitLab 14.9.
|
||||
|
||||
The project [wikis](../user/project/wiki/index.md) API is available only in APIv4.
|
||||
An API for [group wikis](group_wikis.md) is also available.
|
||||
|
@ -67,6 +68,7 @@ GET /projects/:id/wikis/:slug
|
|||
| --------- | ------- | -------- | --------------------- |
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) |
|
||||
| `slug` | string | yes | URLencoded slug (a unique string) of the wiki page, such as `dir%2Fpage_name` |
|
||||
| `render_html` | boolean | no | Return the rendered HTML of the wiki page |
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/wikis/home"
|
||||
|
|
|
@ -66,8 +66,8 @@ git cherry-pick -m 2 7a39eb0
|
|||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/21268) in GitLab 13.11 behind a [feature flag](../../feature_flags.md), disabled by default.
|
||||
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/324154) in GitLab 14.0.
|
||||
|
||||
You can use the GitLab UI to cherry-pick merge requests into a project, even if the
|
||||
merge request is from a fork:
|
||||
You can cherry-pick merge requests from the same project, or forks of the same
|
||||
project, from the GitLab user interface:
|
||||
|
||||
1. In the merge request's secondary menu, click **Commits** to display the commit details page.
|
||||
1. Click on the **Options** dropdown and select **Cherry-pick** to show the cherry-pick modal.
|
||||
|
|
|
@ -3,7 +3,11 @@
|
|||
module API
|
||||
module Entities
|
||||
class WikiPage < WikiPageBasic
|
||||
expose :content
|
||||
include ::MarkupHelper
|
||||
|
||||
expose :content do |wiki_page, options|
|
||||
options[:render_html] ? render_wiki_content(wiki_page) : wiki_page.content
|
||||
end
|
||||
|
||||
expose :encoding do |wiki_page|
|
||||
wiki_page.content.encoding.name
|
||||
|
|
|
@ -45,11 +45,12 @@ module API
|
|||
end
|
||||
params do
|
||||
requires :slug, type: String, desc: 'The slug of a wiki page'
|
||||
optional :render_html, type: Boolean, default: false, desc: 'Render content to HTML'
|
||||
end
|
||||
get ':id/wikis/:slug' do
|
||||
authorize! :read_wiki, container
|
||||
|
||||
present wiki_page, with: Entities::WikiPage
|
||||
present wiki_page, with: Entities::WikiPage, render_html: params[:render_html]
|
||||
end
|
||||
|
||||
desc 'Create a wiki page' do
|
||||
|
|
|
@ -11,9 +11,6 @@ module Gitlab
|
|||
module Ldap
|
||||
class User < Gitlab::Auth::OAuth::User
|
||||
extend ::Gitlab::Utils::Override
|
||||
def save
|
||||
super('LDAP')
|
||||
end
|
||||
|
||||
# instance methods
|
||||
def find_user
|
||||
|
@ -44,6 +41,10 @@ module Gitlab
|
|||
def auth_hash=(auth_hash)
|
||||
@auth_hash = Gitlab::Auth::Ldap::AuthHash.new(auth_hash)
|
||||
end
|
||||
|
||||
def protocol_name
|
||||
'LDAP'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -46,7 +46,7 @@ module Gitlab
|
|||
valid? && persisted?
|
||||
end
|
||||
|
||||
def save(provider = 'OAuth')
|
||||
def save(provider = protocol_name)
|
||||
raise SigninDisabledForProviderError if oauth_provider_disabled?
|
||||
raise SignupDisabledError unless gl_user
|
||||
|
||||
|
@ -96,6 +96,10 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
def protocol_name
|
||||
'OAuth'
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def should_save?
|
||||
|
|
|
@ -11,10 +11,6 @@ module Gitlab
|
|||
class User < Gitlab::Auth::OAuth::User
|
||||
extend ::Gitlab::Utils::Override
|
||||
|
||||
def save
|
||||
super('SAML')
|
||||
end
|
||||
|
||||
def find_user
|
||||
user = find_by_uid_and_provider
|
||||
|
||||
|
@ -40,6 +36,10 @@ module Gitlab
|
|||
saml_config.upstream_two_factor_authn_contexts&.include?(auth_hash.authn_context)
|
||||
end
|
||||
|
||||
def protocol_name
|
||||
'SAML'
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def saml_config
|
||||
|
|
|
@ -32827,6 +32827,9 @@ msgstr ""
|
|||
msgid "SecurityReports|Change status"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityReports|Check the messages generated while parsing the following security reports, as they may prevent the results from being ingested by GitLab. Ensure the security report conforms to a supported %{helpPageLinkStart}JSON schema%{helpPageLinkEnd}."
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityReports|Comment added to '%{vulnerabilityName}'"
|
||||
msgstr ""
|
||||
|
||||
|
@ -33013,7 +33016,7 @@ msgstr ""
|
|||
msgid "SecurityReports|The Vulnerability Report shows the results of the latest successful pipeline on your project's default branch, as well as vulnerabilities from your latest container scan. %{linkStart}Learn more.%{linkEnd}"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityReports|The security reports below contain one or more vulnerability findings that could not be parsed and were not recorded. Download the artifacts in the job output to investigate. Ensure any security report created conforms to the relevant %{helpPageLinkStart}JSON schema%{helpPageLinkEnd}."
|
||||
msgid "SecurityReports|The following security reports contain one or more vulnerability findings that could not be parsed and were not recorded. To investigate a report, download the artifacts in the job output. Ensure the security report conforms to the relevant %{helpPageLinkStart}JSON schema%{helpPageLinkEnd}."
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityReports|There was an error adding the comment."
|
||||
|
@ -33073,6 +33076,9 @@ msgstr ""
|
|||
msgid "SecurityReports|Vulnerability Report"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityReports|Warning parsing security reports"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityReports|While it's rare to have no vulnerabilities for your pipeline, it can happen. In any event, we ask that you double check your settings to make sure all security scanning jobs have passed successfully."
|
||||
msgstr ""
|
||||
|
||||
|
@ -33094,6 +33100,9 @@ msgstr ""
|
|||
msgid "SecurityReports|scanned resources"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityTraining|Primary Training"
|
||||
msgstr ""
|
||||
|
||||
msgid "See example DevOps Score page in our documentation."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -8,12 +8,11 @@ module QA
|
|||
|
||||
AuthorizationError = Class.new(RuntimeError)
|
||||
|
||||
def initialize(address = :gitlab, personal_access_token: nil, is_new_session: true, user: nil, ip_limits: false)
|
||||
def initialize(address = :gitlab, personal_access_token: nil, is_new_session: true, user: nil)
|
||||
@address = address
|
||||
@personal_access_token = personal_access_token
|
||||
@is_new_session = is_new_session
|
||||
@user = user
|
||||
enable_ip_limits if ip_limits
|
||||
end
|
||||
|
||||
# Personal access token
|
||||
|
@ -68,24 +67,6 @@ module QA
|
|||
|
||||
private
|
||||
|
||||
def enable_ip_limits
|
||||
Page::Main::Menu.perform(&:sign_out) if Page::Main::Menu.perform { |p| p.has_personal_area?(wait: 0) }
|
||||
|
||||
Runtime::Browser.visit(@address, Page::Main::Login)
|
||||
Page::Main::Login.perform(&:sign_in_using_admin_credentials)
|
||||
Page::Main::Menu.perform(&:go_to_admin_area)
|
||||
Page::Admin::Menu.perform(&:go_to_network_settings)
|
||||
|
||||
Page::Admin::Settings::Network.perform do |setting|
|
||||
setting.expand_ip_limits do |page|
|
||||
page.enable_throttles
|
||||
page.save_settings
|
||||
end
|
||||
end
|
||||
|
||||
Page::Main::Menu.perform(&:sign_out)
|
||||
end
|
||||
|
||||
# Create PAT
|
||||
#
|
||||
# Use api if admin personal access token is present and skip any UI actions otherwise perform creation via UI
|
||||
|
|
|
@ -1,19 +1,41 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'airborne'
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Manage with IP rate limits', :requires_admin, :skip_live_env do
|
||||
describe 'Users API' do
|
||||
let(:api_client) { Runtime::API::Client.new(:gitlab, ip_limits: true) }
|
||||
let(:request) { Runtime::API::Request.new(api_client, '/users') }
|
||||
RSpec.describe 'Manage', :requires_admin, :skip_live_env, except: { job: 'review-qa-*' } do
|
||||
describe 'rate limits' do
|
||||
let(:rate_limited_user) { Resource::User.fabricate_via_api! }
|
||||
let(:api_client) { Runtime::API::Client.new(:gitlab, user: rate_limited_user) }
|
||||
let!(:request) { Runtime::API::Request.new(api_client, '/users') }
|
||||
|
||||
it 'GET /users', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347881' do
|
||||
after do
|
||||
rate_limited_user.remove_via_api!
|
||||
end
|
||||
|
||||
it 'throttles authenticated api requests by user', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347881' do
|
||||
with_application_settings(
|
||||
throttle_authenticated_api_requests_per_period: 5,
|
||||
throttle_authenticated_api_period_in_seconds: 60,
|
||||
throttle_authenticated_api_enabled: true
|
||||
) do
|
||||
5.times do
|
||||
get request.url
|
||||
expect_status(200)
|
||||
res = RestClient.get request.url
|
||||
expect(res.code).to be(200)
|
||||
end
|
||||
|
||||
expect { RestClient.get request.url }.to raise_error do |e|
|
||||
expect(e.class).to be(RestClient::TooManyRequests)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def with_application_settings(**hargs)
|
||||
QA::Runtime::ApplicationSettings.set_application_settings(**hargs)
|
||||
yield
|
||||
ensure
|
||||
QA::Runtime::ApplicationSettings.restore_application_settings(*hargs.keys)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -151,7 +151,7 @@ FactoryBot.define do
|
|||
|
||||
transient do
|
||||
extern_uid { '123456' }
|
||||
provider { 'ldapmain' }
|
||||
provider { 'twitter' }
|
||||
end
|
||||
|
||||
after(:create) do |user, evaluator|
|
||||
|
@ -166,6 +166,12 @@ FactoryBot.define do
|
|||
|
||||
user.identities << create(:identity, identity_attrs)
|
||||
end
|
||||
|
||||
trait :ldap do
|
||||
transient do
|
||||
provider { 'ldapmain' }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
factory :atlassian_user do
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import * as Sentry from '@sentry/browser';
|
||||
import { GlAlert, GlLink, GlToggle, GlCard, GlSkeletonLoader } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
|
||||
import {
|
||||
|
@ -12,7 +12,7 @@ import {
|
|||
TRACK_PROVIDER_LEARN_MORE_CLICK_LABEL,
|
||||
} from '~/security_configuration/constants';
|
||||
import TrainingProviderList from '~/security_configuration/components/training_provider_list.vue';
|
||||
import { updateSecurityTrainingOptimisticResponse } from '~/security_configuration/graphql/utils/optimistic_response';
|
||||
import { updateSecurityTrainingOptimisticResponse } from '~/security_configuration/graphql/cache_utils';
|
||||
import securityTrainingProvidersQuery from '~/security_configuration/graphql/security_training_providers.query.graphql';
|
||||
import configureSecurityTrainingProvidersMutation from '~/security_configuration/graphql/configure_security_training_providers.mutation.graphql';
|
||||
import dismissUserCalloutMutation from '~/graphql_shared/mutations/dismiss_user_callout.mutation.graphql';
|
||||
|
@ -20,8 +20,7 @@ import waitForPromises from 'helpers/wait_for_promises';
|
|||
import {
|
||||
dismissUserCalloutResponse,
|
||||
dismissUserCalloutErrorResponse,
|
||||
securityTrainingProviders,
|
||||
securityTrainingProvidersResponse,
|
||||
getSecurityTrainingProvidersData,
|
||||
updateSecurityTrainingProvidersResponse,
|
||||
updateSecurityTrainingProvidersErrorResponse,
|
||||
testProjectPath,
|
||||
|
@ -30,6 +29,19 @@ import {
|
|||
|
||||
Vue.use(VueApollo);
|
||||
|
||||
const TEST_TRAINING_PROVIDERS_ALL_DISABLED = getSecurityTrainingProvidersData();
|
||||
const TEST_TRAINING_PROVIDERS_FIRST_ENABLED = getSecurityTrainingProvidersData({
|
||||
providerOverrides: { first: { isEnabled: true, isPrimary: true } },
|
||||
});
|
||||
const TEST_TRAINING_PROVIDERS_ALL_ENABLED = getSecurityTrainingProvidersData({
|
||||
providerOverrides: {
|
||||
first: { isEnabled: true, isPrimary: true },
|
||||
second: { isEnabled: true, isPrimary: false },
|
||||
third: { isEnabled: true, isPrimary: false },
|
||||
},
|
||||
});
|
||||
const TEST_TRAINING_PROVIDERS_DEFAULT = TEST_TRAINING_PROVIDERS_ALL_DISABLED;
|
||||
|
||||
describe('TrainingProviderList component', () => {
|
||||
let wrapper;
|
||||
let apolloProvider;
|
||||
|
@ -38,7 +50,7 @@ describe('TrainingProviderList component', () => {
|
|||
const defaultHandlers = [
|
||||
[
|
||||
securityTrainingProvidersQuery,
|
||||
jest.fn().mockResolvedValue(securityTrainingProvidersResponse),
|
||||
jest.fn().mockResolvedValue(TEST_TRAINING_PROVIDERS_DEFAULT.response),
|
||||
],
|
||||
[
|
||||
configureSecurityTrainingProvidersMutation,
|
||||
|
@ -53,7 +65,7 @@ describe('TrainingProviderList component', () => {
|
|||
};
|
||||
|
||||
const createComponent = () => {
|
||||
wrapper = shallowMount(TrainingProviderList, {
|
||||
wrapper = shallowMountExtended(TrainingProviderList, {
|
||||
provide: {
|
||||
projectFullPath: testProjectPath,
|
||||
},
|
||||
|
@ -68,6 +80,7 @@ describe('TrainingProviderList component', () => {
|
|||
const findLinks = () => wrapper.findAllComponents(GlLink);
|
||||
const findToggles = () => wrapper.findAllComponents(GlToggle);
|
||||
const findFirstToggle = () => findToggles().at(0);
|
||||
const findPrimaryProviderRadios = () => wrapper.findAllByTestId('primary-provider-radio');
|
||||
const findLoader = () => wrapper.findComponent(GlSkeletonLoader);
|
||||
const findErrorAlert = () => wrapper.findComponent(GlAlert);
|
||||
|
||||
|
@ -107,7 +120,7 @@ describe('TrainingProviderList component', () => {
|
|||
Mutation: {
|
||||
configureSecurityTrainingProviders: () => ({
|
||||
errors: [],
|
||||
securityTrainingProviders: [],
|
||||
TEST_TRAINING_PROVIDERS_DEFAULT: [],
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
@ -122,10 +135,11 @@ describe('TrainingProviderList component', () => {
|
|||
});
|
||||
|
||||
it('renders correct amount of cards', () => {
|
||||
expect(findCards()).toHaveLength(securityTrainingProviders.length);
|
||||
expect(findCards()).toHaveLength(TEST_TRAINING_PROVIDERS_DEFAULT.data.length);
|
||||
});
|
||||
|
||||
securityTrainingProviders.forEach(({ name, description, url, isEnabled }, index) => {
|
||||
TEST_TRAINING_PROVIDERS_DEFAULT.data.forEach(
|
||||
({ name, description, url, isEnabled }, index) => {
|
||||
it(`shows the name for card ${index}`, () => {
|
||||
expect(findCards().at(index).text()).toContain(name);
|
||||
});
|
||||
|
@ -145,10 +159,24 @@ describe('TrainingProviderList component', () => {
|
|||
expect(findToggles().at(index).props('value')).toEqual(isEnabled);
|
||||
});
|
||||
|
||||
it(`shows a radio button to select the provider as primary within card ${index}`, () => {
|
||||
const primaryProviderRadioForCurrentCard = findPrimaryProviderRadios().at(index);
|
||||
|
||||
// if the given provider is not enabled it should not be possible select it as primary
|
||||
expect(primaryProviderRadioForCurrentCard.find('input').attributes('disabled')).toBe(
|
||||
isEnabled ? undefined : 'disabled',
|
||||
);
|
||||
|
||||
expect(primaryProviderRadioForCurrentCard.text()).toBe(
|
||||
TrainingProviderList.i18n.primaryTraining,
|
||||
);
|
||||
});
|
||||
|
||||
it('does not show loader when query is populated', () => {
|
||||
expect(findLoader().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('storing training provider settings', () => {
|
||||
|
@ -168,7 +196,7 @@ describe('TrainingProviderList component', () => {
|
|||
input: {
|
||||
providerId: testProviderIds[0],
|
||||
isEnabled: true,
|
||||
isPrimary: false,
|
||||
isPrimary: true,
|
||||
projectPath: testProjectPath,
|
||||
},
|
||||
},
|
||||
|
@ -178,9 +206,9 @@ describe('TrainingProviderList component', () => {
|
|||
|
||||
it('returns an optimistic response when calling the mutation', () => {
|
||||
const optimisticResponse = updateSecurityTrainingOptimisticResponse({
|
||||
id: securityTrainingProviders[0].id,
|
||||
id: TEST_TRAINING_PROVIDERS_DEFAULT.data[0].id,
|
||||
isEnabled: true,
|
||||
isPrimary: false,
|
||||
isPrimary: true,
|
||||
});
|
||||
|
||||
expect(apolloProvider.defaultClient.mutate).toHaveBeenCalledWith(
|
||||
|
@ -243,7 +271,7 @@ describe('TrainingProviderList component', () => {
|
|||
// Once https://gitlab.com/gitlab-org/gitlab/-/issues/348985 and https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79492
|
||||
// are merged this will be much easer to do and should be tackled then.
|
||||
expect(trackingSpy).toHaveBeenCalledWith(undefined, TRACK_TOGGLE_TRAINING_PROVIDER_ACTION, {
|
||||
property: securityTrainingProviders[0].id,
|
||||
property: TEST_TRAINING_PROVIDERS_DEFAULT.data[0].id,
|
||||
label: TRACK_TOGGLE_TRAINING_PROVIDER_LABEL,
|
||||
extra: {
|
||||
providerIsEnabled: true,
|
||||
|
@ -253,7 +281,7 @@ describe('TrainingProviderList component', () => {
|
|||
|
||||
it(`tracks when a provider's "Learn more" link is clicked`, () => {
|
||||
const firstProviderLink = findLinks().at(0);
|
||||
const [{ id: firstProviderId }] = securityTrainingProviders;
|
||||
const [{ id: firstProviderId }] = TEST_TRAINING_PROVIDERS_DEFAULT.data;
|
||||
|
||||
expect(trackingSpy).not.toHaveBeenCalled();
|
||||
|
||||
|
@ -271,6 +299,37 @@ describe('TrainingProviderList component', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('primary provider settings', () => {
|
||||
it.each`
|
||||
description | initialProviderData | expectedMutationInput
|
||||
${'sets the provider to be non-primary when it gets disabled'} | ${TEST_TRAINING_PROVIDERS_FIRST_ENABLED.response} | ${{ providerId: TEST_TRAINING_PROVIDERS_FIRST_ENABLED.data[0].id, isEnabled: false, isPrimary: false }}
|
||||
${'sets a provider to be primary when it is the only one enabled'} | ${TEST_TRAINING_PROVIDERS_ALL_DISABLED.response} | ${{ providerId: TEST_TRAINING_PROVIDERS_ALL_DISABLED.data[0].id, isEnabled: true, isPrimary: true }}
|
||||
${'sets the first other enabled provider to be primary when the primary one gets disabled'} | ${TEST_TRAINING_PROVIDERS_ALL_ENABLED.response} | ${{ providerId: TEST_TRAINING_PROVIDERS_ALL_ENABLED.data[1].id, isEnabled: true, isPrimary: true }}
|
||||
`('$description', async ({ initialProviderData, expectedMutationInput }) => {
|
||||
createApolloProvider({
|
||||
handlers: [
|
||||
[securityTrainingProvidersQuery, jest.fn().mockResolvedValue(initialProviderData)],
|
||||
],
|
||||
});
|
||||
jest.spyOn(apolloProvider.defaultClient, 'mutate');
|
||||
createComponent();
|
||||
|
||||
await waitForQueryToBeLoaded();
|
||||
await toggleFirstProvider();
|
||||
|
||||
expect(apolloProvider.defaultClient.mutate).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
input: expect.objectContaining({
|
||||
...expectedMutationInput,
|
||||
}),
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with errors', () => {
|
||||
const expectErrorAlertToExist = () => {
|
||||
expect(findErrorAlert().props()).toMatchObject({
|
||||
|
|
108
spec/frontend/security_configuration/graphql/cache_utils_spec.js
Normal file
108
spec/frontend/security_configuration/graphql/cache_utils_spec.js
Normal file
|
@ -0,0 +1,108 @@
|
|||
import {
|
||||
updateSecurityTrainingCache,
|
||||
updateSecurityTrainingOptimisticResponse,
|
||||
} from '~/security_configuration/graphql/cache_utils';
|
||||
|
||||
describe('EE - Security configuration graphQL cache utils', () => {
|
||||
describe('updateSecurityTrainingOptimisticResponse', () => {
|
||||
it('returns an optimistic response in the correct shape', () => {
|
||||
const changes = { isEnabled: true, isPrimary: true };
|
||||
const mutationResponse = updateSecurityTrainingOptimisticResponse(changes);
|
||||
|
||||
expect(mutationResponse).toEqual({
|
||||
__typename: 'Mutation',
|
||||
securityTrainingUpdate: {
|
||||
__typename: 'SecurityTrainingUpdatePayload',
|
||||
training: {
|
||||
__typename: 'ProjectSecurityTraining',
|
||||
...changes,
|
||||
},
|
||||
errors: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateSecurityTrainingCache', () => {
|
||||
let mockCache;
|
||||
|
||||
beforeEach(() => {
|
||||
// freezing the data makes sure that we don't mutate the original project
|
||||
const mockCacheData = Object.freeze({
|
||||
project: {
|
||||
securityTrainingProviders: [
|
||||
{ id: 1, isEnabled: true, isPrimary: true },
|
||||
{ id: 2, isEnabled: true, isPrimary: false },
|
||||
{ id: 3, isEnabled: false, isPrimary: false },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
mockCache = {
|
||||
readQuery: () => mockCacheData,
|
||||
writeQuery: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
it('does not update the cache when the primary provider is not getting disabled', () => {
|
||||
const providerAfterUpdate = {
|
||||
id: 2,
|
||||
isEnabled: true,
|
||||
isPrimary: false,
|
||||
};
|
||||
|
||||
updateSecurityTrainingCache({
|
||||
query: 'GraphQL query',
|
||||
variables: { fullPath: 'gitlab/project' },
|
||||
})(mockCache, {
|
||||
data: {
|
||||
securityTrainingUpdate: {
|
||||
training: {
|
||||
...providerAfterUpdate,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(mockCache.writeQuery).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('sets the previous primary provider to be non-primary when another provider gets set as primary', () => {
|
||||
const providerAfterUpdate = {
|
||||
id: 2,
|
||||
isEnabled: true,
|
||||
isPrimary: true,
|
||||
};
|
||||
|
||||
const expectedTrainingProvidersWrittenToCache = [
|
||||
// this was the previous primary primary provider and it should not be primary any longer
|
||||
{ id: 1, isEnabled: true, isPrimary: false },
|
||||
{ id: 2, isEnabled: true, isPrimary: true },
|
||||
{ id: 3, isEnabled: false, isPrimary: false },
|
||||
];
|
||||
|
||||
updateSecurityTrainingCache({
|
||||
query: 'GraphQL query',
|
||||
variables: { fullPath: 'gitlab/project' },
|
||||
})(mockCache, {
|
||||
data: {
|
||||
securityTrainingUpdate: {
|
||||
training: {
|
||||
...providerAfterUpdate,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(mockCache.writeQuery).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
data: {
|
||||
project: {
|
||||
securityTrainingProviders: expectedTrainingProvidersWrittenToCache,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,8 +1,8 @@
|
|||
export const testProjectPath = 'foo/bar';
|
||||
|
||||
export const testProviderIds = [101, 102];
|
||||
export const testProviderIds = [101, 102, 103];
|
||||
|
||||
export const securityTrainingProviders = [
|
||||
const createSecurityTrainingProviders = ({ providerOverrides = {} }) => [
|
||||
{
|
||||
id: testProviderIds[0],
|
||||
name: 'Vendor Name 1',
|
||||
|
@ -10,18 +10,31 @@ export const securityTrainingProviders = [
|
|||
url: 'https://www.example.org/security/training',
|
||||
isEnabled: false,
|
||||
isPrimary: false,
|
||||
...providerOverrides.first,
|
||||
},
|
||||
{
|
||||
id: testProviderIds[1],
|
||||
name: 'Vendor Name 2',
|
||||
description: 'Security training with guide and learning pathways.',
|
||||
url: 'https://www.vendornametwo.com/',
|
||||
isEnabled: true,
|
||||
isEnabled: false,
|
||||
isPrimary: false,
|
||||
...providerOverrides.second,
|
||||
},
|
||||
{
|
||||
id: testProviderIds[2],
|
||||
name: 'Vendor Name 3',
|
||||
description: 'Security training for the everyday developer.',
|
||||
url: 'https://www.vendornamethree.com/',
|
||||
isEnabled: false,
|
||||
isPrimary: false,
|
||||
...providerOverrides.third,
|
||||
},
|
||||
];
|
||||
|
||||
export const securityTrainingProvidersResponse = {
|
||||
export const getSecurityTrainingProvidersData = (providerOverrides = {}) => {
|
||||
const securityTrainingProviders = createSecurityTrainingProviders(providerOverrides);
|
||||
const response = {
|
||||
data: {
|
||||
project: {
|
||||
id: 1,
|
||||
|
@ -30,13 +43,10 @@ export const securityTrainingProvidersResponse = {
|
|||
},
|
||||
};
|
||||
|
||||
export const disabledSecurityTrainingProvidersResponse = {
|
||||
data: {
|
||||
project: {
|
||||
id: 1,
|
||||
securityTrainingProviders: [securityTrainingProviders[0]],
|
||||
},
|
||||
},
|
||||
return {
|
||||
response,
|
||||
data: securityTrainingProviders,
|
||||
};
|
||||
};
|
||||
|
||||
export const dismissUserCalloutResponse = {
|
||||
|
|
|
@ -315,33 +315,26 @@ RSpec.describe MarkupHelper do
|
|||
end
|
||||
|
||||
describe '#render_wiki_content' do
|
||||
let(:wiki) { double('WikiPage', path: "file.#{extension}") }
|
||||
let(:wiki_repository) { double('Repository') }
|
||||
let(:wiki) { build(:wiki, container: project) }
|
||||
let(:content) { 'wiki content' }
|
||||
let(:slug) { 'nested/page' }
|
||||
let(:wiki_page) { double('WikiPage', path: "file.#{extension}", content: content, slug: slug, wiki: wiki) }
|
||||
|
||||
let(:context) do
|
||||
{
|
||||
pipeline: :wiki, project: project, wiki: wiki,
|
||||
page_slug: 'nested/page', issuable_reference_expansion_enabled: true,
|
||||
repository: wiki_repository
|
||||
page_slug: slug, issuable_reference_expansion_enabled: true,
|
||||
repository: wiki.repository
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
expect(wiki).to receive(:content).and_return(content)
|
||||
expect(wiki).to receive(:slug).and_return('nested/page')
|
||||
expect(wiki).to receive(:repository).and_return(wiki_repository)
|
||||
allow(wiki).to receive(:container).and_return(project)
|
||||
|
||||
helper.instance_variable_set(:@wiki, wiki)
|
||||
end
|
||||
|
||||
context 'when file is Markdown' do
|
||||
let(:extension) { 'md' }
|
||||
|
||||
it 'renders using #markdown_unsafe helper method' do
|
||||
expect(helper).to receive(:markdown_unsafe).with('wiki content', context)
|
||||
|
||||
helper.render_wiki_content(wiki)
|
||||
helper.render_wiki_content(wiki_page)
|
||||
end
|
||||
|
||||
context 'when context has labels' do
|
||||
|
@ -350,7 +343,7 @@ RSpec.describe MarkupHelper do
|
|||
let(:content) { '~Bug' }
|
||||
|
||||
it 'renders label' do
|
||||
result = helper.render_wiki_content(wiki)
|
||||
result = helper.render_wiki_content(wiki_page)
|
||||
doc = Nokogiri::HTML.parse(result)
|
||||
|
||||
expect(doc.css('.gl-label-link')).not_to be_empty
|
||||
|
@ -366,7 +359,7 @@ RSpec.describe MarkupHelper do
|
|||
end
|
||||
|
||||
it 'renders uploads relative to project' do
|
||||
result = helper.render_wiki_content(wiki)
|
||||
result = helper.render_wiki_content(wiki_page)
|
||||
|
||||
expect(result).to include("#{project.full_path}#{upload_link}")
|
||||
end
|
||||
|
@ -379,7 +372,7 @@ RSpec.describe MarkupHelper do
|
|||
it 'renders using Gitlab::Asciidoc' do
|
||||
expect(Gitlab::Asciidoc).to receive(:render)
|
||||
|
||||
helper.render_wiki_content(wiki)
|
||||
helper.render_wiki_content(wiki_page)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -398,7 +391,7 @@ FooBar
|
|||
it 'renders using #markdown_unsafe helper method' do
|
||||
expect(helper).to receive(:markdown_unsafe).with(content, context)
|
||||
|
||||
result = helper.render_wiki_content(wiki)
|
||||
result = helper.render_wiki_content(wiki_page)
|
||||
|
||||
expect(result).to be_empty
|
||||
end
|
||||
|
@ -410,7 +403,7 @@ FooBar
|
|||
it 'renders all other formats using Gitlab::OtherMarkup' do
|
||||
expect(Gitlab::OtherMarkup).to receive(:render)
|
||||
|
||||
helper.render_wiki_content(wiki)
|
||||
helper.render_wiki_content(wiki_page)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,7 +5,10 @@ require 'spec_helper'
|
|||
RSpec.describe API::Entities::WikiPage do
|
||||
let_it_be_with_reload(:wiki_page) { create(:wiki_page) }
|
||||
|
||||
let(:entity) { described_class.new(wiki_page) }
|
||||
let(:params) { {} }
|
||||
let(:entity) { described_class.new(wiki_page, params) }
|
||||
|
||||
subject { entity.as_json }
|
||||
|
||||
it 'returns the proper encoding for the wiki page content' do
|
||||
expect(entity.as_json[:encoding]).to eq 'UTF-8'
|
||||
|
@ -14,4 +17,26 @@ RSpec.describe API::Entities::WikiPage do
|
|||
|
||||
expect(entity.as_json[:encoding]).to eq 'ISO-8859-1'
|
||||
end
|
||||
|
||||
it 'returns the raw wiki page content' do
|
||||
expect(subject[:content]).to eq wiki_page.content
|
||||
end
|
||||
|
||||
context 'when render_html param is passed' do
|
||||
context 'when it is true' do
|
||||
let(:params) { { render_html: true } }
|
||||
|
||||
it 'returns the wiki page content rendered' do
|
||||
expect(subject[:content]).to eq "<p data-sourcepos=\"1:1-1:#{wiki_page.content.size}\" dir=\"auto\">#{wiki_page.content}</p>"
|
||||
end
|
||||
end
|
||||
|
||||
context 'when it is false' do
|
||||
let(:params) { { render_html: false } }
|
||||
|
||||
it 'returns the raw wiki page content' do
|
||||
expect(subject[:content]).to eq wiki_page.content
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,7 +5,7 @@ require 'spec_helper'
|
|||
RSpec.describe Gitlab::Auth::Ldap::Access do
|
||||
include LdapHelpers
|
||||
|
||||
let(:user) { create(:omniauth_user) }
|
||||
let(:user) { create(:omniauth_user, :ldap) }
|
||||
|
||||
subject(:access) { described_class.new(user) }
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe Gitlab::Auth::Ldap::Authentication do
|
||||
let(:dn) { 'uid=John Smith, ou=People, dc=example, dc=com' }
|
||||
let(:user) { create(:omniauth_user, extern_uid: Gitlab::Auth::Ldap::Person.normalize_dn(dn)) }
|
||||
let(:user) { create(:omniauth_user, :ldap, extern_uid: Gitlab::Auth::Ldap::Person.normalize_dn(dn)) }
|
||||
let(:login) { 'john' }
|
||||
let(:password) { 'password' }
|
||||
|
||||
|
|
|
@ -577,28 +577,66 @@ RSpec.describe Gitlab::Auth::OAuth::User do
|
|||
stub_omniauth_config(allow_single_sign_on: ['twitter'])
|
||||
end
|
||||
|
||||
context 'signup with omniauth only' do
|
||||
context 'dont block on create' do
|
||||
before do
|
||||
stub_omniauth_config(block_auto_created_users: false)
|
||||
shared_examples 'being blocked on creation' do
|
||||
context 'when blocking on creation' do
|
||||
it 'creates a blocked user' do
|
||||
oauth_user.save # rubocop:disable Rails/SaveBang
|
||||
expect(gl_user).to be_valid
|
||||
expect(gl_user).to be_blocked
|
||||
end
|
||||
|
||||
it do
|
||||
context 'when a sign up user cap has been set up but has not been reached yet' do
|
||||
it 'still creates a blocked user' do
|
||||
stub_application_setting(new_user_signups_cap: 999)
|
||||
|
||||
oauth_user.save # rubocop:disable Rails/SaveBang
|
||||
expect(gl_user).to be_valid
|
||||
expect(gl_user).to be_blocked
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'not being blocked on creation' do
|
||||
context 'when not blocking on creation' do
|
||||
it 'creates a non-blocked user' do
|
||||
oauth_user.save # rubocop:disable Rails/SaveBang
|
||||
expect(gl_user).to be_valid
|
||||
expect(gl_user).not_to be_blocked
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'block on create' do
|
||||
context 'signup with SAML' do
|
||||
let(:provider) { 'saml' }
|
||||
|
||||
before do
|
||||
stub_omniauth_config({
|
||||
allow_single_sign_on: ['saml'],
|
||||
auto_link_saml_user: true,
|
||||
block_auto_created_users: block_auto_created_users
|
||||
})
|
||||
end
|
||||
|
||||
it_behaves_like 'being blocked on creation' do
|
||||
let(:block_auto_created_users) { true }
|
||||
end
|
||||
|
||||
it_behaves_like 'not being blocked on creation' do
|
||||
let(:block_auto_created_users) { false }
|
||||
end
|
||||
end
|
||||
|
||||
context 'signup with omniauth only' do
|
||||
it_behaves_like 'being blocked on creation' do
|
||||
before do
|
||||
stub_omniauth_config(block_auto_created_users: true)
|
||||
end
|
||||
end
|
||||
|
||||
it do
|
||||
oauth_user.save # rubocop:disable Rails/SaveBang
|
||||
expect(gl_user).to be_valid
|
||||
expect(gl_user).to be_blocked
|
||||
it_behaves_like 'not being blocked on creation' do
|
||||
before do
|
||||
stub_omniauth_config(block_auto_created_users: false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -614,31 +652,19 @@ RSpec.describe Gitlab::Auth::OAuth::User do
|
|||
end
|
||||
|
||||
context "and no account for the LDAP user" do
|
||||
context 'dont block on create (LDAP)' do
|
||||
before do
|
||||
allow_next_instance_of(Gitlab::Auth::Ldap::Config) do |instance|
|
||||
allow(instance).to receive_messages(block_auto_created_users: false)
|
||||
end
|
||||
end
|
||||
|
||||
it do
|
||||
oauth_user.save # rubocop:disable Rails/SaveBang
|
||||
expect(gl_user).to be_valid
|
||||
expect(gl_user).not_to be_blocked
|
||||
end
|
||||
end
|
||||
|
||||
context 'block on create (LDAP)' do
|
||||
it_behaves_like 'being blocked on creation' do
|
||||
before do
|
||||
allow_next_instance_of(Gitlab::Auth::Ldap::Config) do |instance|
|
||||
allow(instance).to receive_messages(block_auto_created_users: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it do
|
||||
oauth_user.save # rubocop:disable Rails/SaveBang
|
||||
expect(gl_user).to be_valid
|
||||
expect(gl_user).to be_blocked
|
||||
it_behaves_like 'not being blocked on creation' do
|
||||
before do
|
||||
allow_next_instance_of(Gitlab::Auth::Ldap::Config) do |instance|
|
||||
allow(instance).to receive_messages(block_auto_created_users: false)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -646,32 +672,20 @@ RSpec.describe Gitlab::Auth::OAuth::User do
|
|||
context 'and LDAP user has an account already' do
|
||||
let!(:existing_user) { create(:omniauth_user, email: 'john@example.com', extern_uid: dn, provider: 'ldapmain', username: 'john') }
|
||||
|
||||
context 'dont block on create (LDAP)' do
|
||||
it_behaves_like 'not being blocked on creation' do
|
||||
before do
|
||||
allow_next_instance_of(Gitlab::Auth::Ldap::Config) do |instance|
|
||||
allow(instance).to receive_messages(block_auto_created_users: false)
|
||||
end
|
||||
end
|
||||
|
||||
it do
|
||||
oauth_user.save # rubocop:disable Rails/SaveBang
|
||||
expect(gl_user).to be_valid
|
||||
expect(gl_user).not_to be_blocked
|
||||
end
|
||||
end
|
||||
|
||||
context 'block on create (LDAP)' do
|
||||
it_behaves_like 'not being blocked on creation' do
|
||||
before do
|
||||
allow_next_instance_of(Gitlab::Auth::Ldap::Config) do |instance|
|
||||
allow(instance).to receive_messages(block_auto_created_users: true)
|
||||
end
|
||||
end
|
||||
|
||||
it do
|
||||
oauth_user.save # rubocop:disable Rails/SaveBang
|
||||
expect(gl_user).to be_valid
|
||||
expect(gl_user).not_to be_blocked
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -682,56 +696,32 @@ RSpec.describe Gitlab::Auth::OAuth::User do
|
|||
oauth_user.gl_user.activate
|
||||
end
|
||||
|
||||
context 'dont block on create' do
|
||||
it_behaves_like 'not being blocked on creation' do
|
||||
before do
|
||||
stub_omniauth_config(block_auto_created_users: false)
|
||||
end
|
||||
|
||||
it do
|
||||
oauth_user.save # rubocop:disable Rails/SaveBang
|
||||
expect(gl_user).to be_valid
|
||||
expect(gl_user).not_to be_blocked
|
||||
end
|
||||
end
|
||||
|
||||
context 'block on create' do
|
||||
it_behaves_like 'not being blocked on creation' do
|
||||
before do
|
||||
stub_omniauth_config(block_auto_created_users: true)
|
||||
end
|
||||
|
||||
it do
|
||||
oauth_user.save # rubocop:disable Rails/SaveBang
|
||||
expect(gl_user).to be_valid
|
||||
expect(gl_user).not_to be_blocked
|
||||
end
|
||||
end
|
||||
|
||||
context 'dont block on create (LDAP)' do
|
||||
it_behaves_like 'not being blocked on creation' do
|
||||
before do
|
||||
allow_next_instance_of(Gitlab::Auth::Ldap::Config) do |instance|
|
||||
allow(instance).to receive_messages(block_auto_created_users: false)
|
||||
end
|
||||
end
|
||||
|
||||
it do
|
||||
oauth_user.save # rubocop:disable Rails/SaveBang
|
||||
expect(gl_user).to be_valid
|
||||
expect(gl_user).not_to be_blocked
|
||||
end
|
||||
end
|
||||
|
||||
context 'block on create (LDAP)' do
|
||||
it_behaves_like 'not being blocked on creation' do
|
||||
before do
|
||||
allow_next_instance_of(Gitlab::Auth::Ldap::Config) do |instance|
|
||||
allow(instance).to receive_messages(block_auto_created_users: true)
|
||||
end
|
||||
end
|
||||
|
||||
it do
|
||||
oauth_user.save # rubocop:disable Rails/SaveBang
|
||||
expect(gl_user).to be_valid
|
||||
expect(gl_user).not_to be_blocked
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1057,4 +1047,10 @@ RSpec.describe Gitlab::Auth::OAuth::User do
|
|||
expect(oauth_user.bypass_two_factor?).to be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
describe '#protocol_name' do
|
||||
it 'is OAuth' do
|
||||
expect(oauth_user.protocol_name).to eq('OAuth')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3090,7 +3090,7 @@ RSpec.describe User do
|
|||
|
||||
describe '#ldap_identity' do
|
||||
it 'returns ldap identity' do
|
||||
user = create :omniauth_user
|
||||
user = create(:omniauth_user, :ldap)
|
||||
|
||||
expect(user.ldap_identity.provider).not_to be_empty
|
||||
end
|
||||
|
|
|
@ -11,6 +11,7 @@ RSpec.describe API::Users do
|
|||
|
||||
let(:blocked_user) { create(:user, :blocked) }
|
||||
let(:omniauth_user) { create(:omniauth_user) }
|
||||
let(:ldap_user) { create(:omniauth_user, provider: 'ldapmain') }
|
||||
let(:ldap_blocked_user) { create(:omniauth_user, provider: 'ldapmain', state: 'ldap_blocked') }
|
||||
let(:private_user) { create(:user, private_profile: true) }
|
||||
let(:deactivated_user) { create(:user, state: 'deactivated') }
|
||||
|
@ -1293,10 +1294,10 @@ RSpec.describe API::Users do
|
|||
end
|
||||
|
||||
it "updates user's existing identity" do
|
||||
put api("/users/#{omniauth_user.id}", admin), params: { provider: 'ldapmain', extern_uid: '654321' }
|
||||
put api("/users/#{ldap_user.id}", admin), params: { provider: 'ldapmain', extern_uid: '654321' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(omniauth_user.reload.identities.first.extern_uid).to eq('654321')
|
||||
expect(ldap_user.reload.identities.first.extern_uid).to eq('654321')
|
||||
end
|
||||
|
||||
it 'updates user with new identity' do
|
||||
|
|
|
@ -130,41 +130,42 @@ RSpec.describe API::Wikis do
|
|||
describe 'GET /projects/:id/wikis/:slug' do
|
||||
let(:page) { create(:wiki_page, wiki: project.wiki) }
|
||||
let(:url) { "/projects/#{project.id}/wikis/#{page.slug}" }
|
||||
let(:params) { {} }
|
||||
|
||||
subject(:request) { get api(url, user), params: params }
|
||||
|
||||
context 'when wiki is disabled' do
|
||||
let(:project) { project_wiki_disabled }
|
||||
|
||||
context 'when user is guest' do
|
||||
before do
|
||||
get api(url)
|
||||
request
|
||||
end
|
||||
|
||||
context 'when user is guest' do
|
||||
let(:user) { nil }
|
||||
|
||||
include_examples 'wiki API 404 Project Not Found'
|
||||
end
|
||||
|
||||
context 'when user is developer' do
|
||||
before do
|
||||
get api(url, developer)
|
||||
end
|
||||
let(:user) { developer }
|
||||
|
||||
include_examples 'wiki API 403 Forbidden'
|
||||
end
|
||||
|
||||
context 'when user is maintainer' do
|
||||
before do
|
||||
get api(url, maintainer)
|
||||
end
|
||||
let(:user) { maintainer }
|
||||
|
||||
include_examples 'wiki API 403 Forbidden'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when wiki is available only for team members' do
|
||||
let(:project) { create(:project, :wiki_repo, :wiki_private) }
|
||||
let_it_be_with_reload(:project) { create(:project, :wiki_repo, :wiki_private) }
|
||||
|
||||
context 'when user is guest' do
|
||||
before do
|
||||
get api(url)
|
||||
request
|
||||
end
|
||||
|
||||
include_examples 'wiki API 404 Project Not Found'
|
||||
|
@ -173,7 +174,8 @@ RSpec.describe API::Wikis do
|
|||
context 'when user is developer' do
|
||||
before do
|
||||
project.add_developer(user)
|
||||
get api(url, user)
|
||||
|
||||
request
|
||||
end
|
||||
|
||||
include_examples 'wikis API returns wiki page'
|
||||
|
@ -189,7 +191,7 @@ RSpec.describe API::Wikis do
|
|||
before do
|
||||
project.add_maintainer(user)
|
||||
|
||||
get api(url, user)
|
||||
request
|
||||
end
|
||||
|
||||
include_examples 'wikis API returns wiki page'
|
||||
|
@ -203,11 +205,13 @@ RSpec.describe API::Wikis do
|
|||
end
|
||||
|
||||
context 'when wiki is available for everyone with access' do
|
||||
let(:project) { create(:project, :wiki_repo) }
|
||||
let_it_be_with_reload(:project) { create(:project, :wiki_repo) }
|
||||
|
||||
context 'when user is guest' do
|
||||
let(:user) { nil }
|
||||
|
||||
before do
|
||||
get api(url)
|
||||
request
|
||||
end
|
||||
|
||||
include_examples 'wiki API 404 Project Not Found'
|
||||
|
@ -217,7 +221,7 @@ RSpec.describe API::Wikis do
|
|||
before do
|
||||
project.add_developer(user)
|
||||
|
||||
get api(url, user)
|
||||
request
|
||||
end
|
||||
|
||||
include_examples 'wikis API returns wiki page'
|
||||
|
@ -233,7 +237,7 @@ RSpec.describe API::Wikis do
|
|||
before do
|
||||
project.add_maintainer(user)
|
||||
|
||||
get api(url, user)
|
||||
request
|
||||
end
|
||||
|
||||
include_examples 'wikis API returns wiki page'
|
||||
|
|
|
@ -44,14 +44,32 @@ RSpec.shared_examples_for 'wikis API returns list of wiki pages' do
|
|||
end
|
||||
|
||||
RSpec.shared_examples_for 'wikis API returns wiki page' do
|
||||
it 'returns the wiki page' do
|
||||
shared_examples 'returns wiki page' do
|
||||
specify do
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response.size).to eq(5)
|
||||
expect(json_response.keys).to match_array(expected_keys_with_content)
|
||||
expect(json_response['content']).to eq(page.content)
|
||||
expect(json_response['content']).to eq(expected_content)
|
||||
expect(json_response['slug']).to eq(page.slug)
|
||||
expect(json_response['title']).to eq(page.title)
|
||||
expect(json_response['encoding']).to eq('UTF-8')
|
||||
end
|
||||
end
|
||||
|
||||
let(:expected_content) { page.content }
|
||||
|
||||
it_behaves_like 'returns wiki page'
|
||||
|
||||
context 'when render param is false' do
|
||||
let(:params) { { render_html: false } }
|
||||
|
||||
it_behaves_like 'returns wiki page'
|
||||
end
|
||||
|
||||
context 'when render param is true' do
|
||||
let(:params) { { render_html: true } }
|
||||
let(:expected_content) { '<p data-sourcepos="1:1-1:21" dir="auto">Content for wiki page</p>' }
|
||||
|
||||
it_behaves_like 'returns wiki page'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ RSpec.describe 'shared/wikis/_sidebar.html.haml' do
|
|||
|
||||
context 'The sidebar comes from a custom page' do
|
||||
before do
|
||||
assign(:sidebar_page, double('WikiPage', path: 'sidebar.md', slug: 'sidebar', content: 'Some sidebar content'))
|
||||
assign(:sidebar_page, double('WikiPage', path: 'sidebar.md', slug: 'sidebar', content: 'Some sidebar content', wiki: wiki))
|
||||
end
|
||||
|
||||
it 'does not show an alert' do
|
||||
|
|
Loading…
Reference in a new issue