Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-06-08 09:09:42 +00:00
parent 208f195a9b
commit dca8df0c90
34 changed files with 790 additions and 42 deletions

View File

@ -800,7 +800,6 @@ RSpec/ContextWording:
- 'ee/spec/services/projects/operations/update_service_spec.rb'
- 'ee/spec/services/projects/protect_default_branch_service_spec.rb'
- 'ee/spec/services/projects/restore_service_spec.rb'
- 'ee/spec/services/projects/slack_application_install_service_spec.rb'
- 'ee/spec/services/projects/transfer_service_spec.rb'
- 'ee/spec/services/projects/update_mirror_service_spec.rb'
- 'ee/spec/services/projects/update_service_spec.rb'

View File

@ -4,7 +4,6 @@ RSpec/ExpectInHook:
- 'ee/spec/controllers/ee/projects/merge_requests/content_controller_spec.rb'
- 'ee/spec/controllers/groups/analytics/productivity_analytics_controller_spec.rb'
- 'ee/spec/controllers/groups/seat_usage_controller_spec.rb'
- 'ee/spec/controllers/projects/settings/slacks_controller_spec.rb'
- 'ee/spec/controllers/subscriptions_controller_spec.rb'
- 'ee/spec/elastic/migrate/20220118150500_delete_orphaned_commits_spec.rb'
- 'ee/spec/elastic/migrate/20220119120500_populate_commit_permissions_in_main_index_spec.rb'

View File

@ -412,7 +412,6 @@ Style/ClassAndModuleChildren:
- 'ee/app/controllers/groups/wikis_controller.rb'
- 'ee/app/controllers/oauth/geo_auth_controller.rb'
- 'ee/app/controllers/profiles/billings_controller.rb'
- 'ee/app/controllers/profiles/slacks_controller.rb'
- 'ee/app/controllers/projects/analytics/issues_analytics_controller.rb'
- 'ee/app/controllers/projects/analytics/merge_request_analytics_controller.rb'
- 'ee/app/controllers/projects/approver_groups_controller.rb'

View File

@ -94,7 +94,6 @@ Style/EmptyMethod:
- 'ee/app/controllers/projects/security/dast_scanner_profiles_controller.rb'
- 'ee/app/controllers/projects/security/dast_site_profiles_controller.rb'
- 'ee/app/controllers/projects/security/sast_configuration_controller.rb'
- 'ee/app/controllers/projects/settings/slacks_controller.rb'
- 'ee/app/controllers/subscriptions/groups_controller.rb'
- 'ee/app/models/ee/epic.rb'
- 'ee/app/services/feature_flag_issues/destroy_service.rb'

View File

@ -274,7 +274,6 @@ Style/GuardClause:
- 'ee/app/controllers/profiles/billings_controller.rb'
- 'ee/app/controllers/projects/path_locks_controller.rb'
- 'ee/app/controllers/projects/security/policies_controller.rb'
- 'ee/app/controllers/projects/settings/slacks_controller.rb'
- 'ee/app/controllers/smartcard_controller.rb'
- 'ee/app/finders/ee/template_finder.rb'
- 'ee/app/finders/iterations_finder.rb'

View File

@ -411,7 +411,6 @@ Style/IfUnlessModifier:
- 'ee/app/controllers/projects/integrations/zentao/issues_controller.rb'
- 'ee/app/controllers/projects/path_locks_controller.rb'
- 'ee/app/controllers/projects/push_rules_controller.rb'
- 'ee/app/controllers/projects/settings/slacks_controller.rb'
- 'ee/app/finders/security/pipeline_vulnerabilities_finder.rb'
- 'ee/app/finders/security/vulnerabilities_finder.rb'
- 'ee/app/graphql/mutations/audit_events/external_audit_event_destinations/create.rb'

View File

@ -133,8 +133,3 @@ export default {
</div>
</div>
</template>
<style>
.gl-spinner-container {
text-align: left;
}
</style>

View File

@ -1,9 +1,11 @@
import { withGitLabAPIAccess } from 'storybook_addons/gitlab_api_access';
import Api from '~/api';
import { ContentEditor } from './index';
export default {
component: ContentEditor,
title: 'ce/content_editor/content_editor',
decorators: [withGitLabAPIAccess],
};
const Template = (_, { argTypes }) => ({

View File

@ -0,0 +1,32 @@
# frozen_string_literal: true
module Profiles
class SlacksController < Profiles::ApplicationController
include IntegrationsHelper
skip_before_action :authenticate_user!
layout 'application'
feature_category :integrations
def edit
@projects = disabled_projects.inc_routes if current_user
end
def slack_link
project = disabled_projects.find(params[:project_id])
link = add_to_slack_link(project, Gitlab::CurrentSettings.slack_app_id)
render json: { add_to_slack_link: link }
end
private
def disabled_projects
@disabled_projects ||= current_user
.authorized_projects(Gitlab::Access::MAINTAINER)
.with_slack_application_disabled
end
end
end

View File

@ -0,0 +1,78 @@
# frozen_string_literal: true
module Projects
module Settings
class SlacksController < Projects::ApplicationController
before_action :handle_oauth_error, only: :slack_auth
before_action :check_oauth_state, only: :slack_auth
before_action :authorize_admin_project!
before_action :slack_integration, only: [:edit, :update]
before_action :service, only: [:destroy, :edit, :update]
layout 'project_settings'
feature_category :integrations
def slack_auth
result = Projects::SlackApplicationInstallService.new(project, current_user, params).execute
flash[:alert] = result[:message] if result[:status] == :error
session[:slack_install_success] = true
redirect_to_service_page
end
def destroy
slack_integration.destroy
redirect_to_service_page
end
def edit; end
def update
if slack_integration.update(slack_integration_params)
flash[:notice] = 'The project alias was updated successfully'
redirect_to_service_page
else
render :edit
end
end
private
def redirect_to_service_page
redirect_to edit_project_settings_integration_path(
project,
project.gitlab_slack_application_integration || project.build_gitlab_slack_application_integration
)
end
def check_oauth_state
render_403 unless valid_authenticity_token?(session, params[:state])
true
end
def handle_oauth_error
return unless params[:error] == 'access_denied'
flash[:alert] = 'Access denied'
redirect_to_service_page
end
def slack_integration
@slack_integration ||= project.gitlab_slack_application_integration.slack_integration
end
def service
@service = project.gitlab_slack_application_integration
end
def slack_integration_params
params.require(:slack_integration).permit(:alias)
end
end
end
end

View File

@ -136,6 +136,11 @@ module IntegrationsHelper
form_data[:jira_issue_transition_id] = integration.jira_issue_transition_id
end
if integration.is_a?(::Integrations::GitlabSlackApplication)
form_data[:upgrade_slack_url] = add_to_slack_link(project, slack_app_id)
form_data[:should_upgrade_slack] = integration.upgrade_needed?.to_s
end
form_data
end
@ -212,6 +217,28 @@ module IntegrationsHelper
event_i18n_map[event] || event.to_s.humanize
end
def add_to_slack_link(project, slack_app_id)
query = {
scope: SlackIntegration::SCOPES.join(','),
client_id: slack_app_id,
redirect_uri: slack_auth_project_settings_slack_url(project),
state: form_authenticity_token
}
"#{::Projects::SlackApplicationInstallService::SLACK_AUTHORIZE_URL}?#{query.to_query}"
end
def gitlab_slack_application_data(projects)
{
projects: (projects || []).to_json(only: [:id, :name], methods: [:avatar_url, :name_with_namespace]),
sign_in_path: new_session_path(:user, redirect_to_referer: 'yes'),
is_signed_in: current_user.present?.to_s,
slack_link_path: slack_link_profile_slack_path,
gitlab_logo_path: image_path('illustrations/gitlab_logo.svg'),
slack_logo_path: image_path('illustrations/slack_logo.svg')
}
end
extend self
private

View File

@ -2279,6 +2279,14 @@ class User < ApplicationRecord
abuse_trust_scores.telesign.order(created_at: :desc).first&.score || 0.0
end
def arkose_global_score
abuse_trust_scores.arkose_global_score.order(created_at: :desc).first&.score || 0.0
end
def arkose_custom_score
abuse_trust_scores.arkose_custom_score.order(created_at: :desc).first&.score || 0.0
end
def trust_scores_for_source(source)
abuse_trust_scores.where(source: source)
end

View File

@ -0,0 +1,76 @@
# frozen_string_literal: true
module Projects
class SlackApplicationInstallService < BaseService
include Gitlab::Routing
# Endpoint to initiate the OAuth flow, redirects to Slack's authorization screen
# https://api.slack.com/authentication/oauth-v2#asking
SLACK_AUTHORIZE_URL = 'https://slack.com/oauth/v2/authorize'
# Endpoint to exchange the temporary authorization code for an access token
# https://api.slack.com/authentication/oauth-v2#exchanging
SLACK_EXCHANGE_TOKEN_URL = 'https://slack.com/api/oauth.v2.access'
def execute
slack_data = exchange_slack_token
return error("Slack: #{slack_data['error']}") unless slack_data['ok']
integration = project.gitlab_slack_application_integration \
|| project.create_gitlab_slack_application_integration!
installation = integration.slack_integration || integration.build_slack_integration
installation.update!(
bot_user_id: slack_data['bot_user_id'],
bot_access_token: slack_data['access_token'],
team_id: slack_data.dig('team', 'id'),
team_name: slack_data.dig('team', 'name'),
alias: project.full_path,
user_id: slack_data.dig('authed_user', 'id'),
authorized_scope_names: slack_data['scope']
)
update_legacy_installations!(installation)
success
end
private
def exchange_slack_token
query = {
client_id: Gitlab::CurrentSettings.slack_app_id,
client_secret: Gitlab::CurrentSettings.slack_app_secret,
code: params[:code],
# NOTE: Needs to match the `redirect_uri` passed to the authorization endpoint,
# otherwise we get a `bad_redirect_uri` error.
redirect_uri: slack_auth_project_settings_slack_url(project)
}
Gitlab::HTTP.get(SLACK_EXCHANGE_TOKEN_URL, query: query).to_hash
end
# Update any legacy SlackIntegration records for the Slack Workspace. Legacy SlackIntegration records
# are any created before our Slack App was upgraded to use Granular Bot Permissions and issue a
# bot_access_token. Any SlackIntegration records for the Slack Workspace will already have the same
# bot_access_token.
def update_legacy_installations!(installation)
updatable_attributes = installation.attributes.slice(
'user_id',
'bot_user_id',
'encrypted_bot_access_token',
'encrypted_bot_access_token_iv',
'updated_at'
)
SlackIntegration.by_team(installation.team_id).id_not_in(installation.id).each_batch do |batch|
batch_ids = batch.pluck(:id) # rubocop: disable CodeReuse/ActiveRecord
batch.update_all(updatable_attributes)
::Integrations::SlackWorkspace::IntegrationApiScope.update_scopes(batch_ids, installation.slack_api_scopes)
end
end
end
end

View File

@ -0,0 +1,6 @@
- add_to_breadcrumbs _('Profile'), profile_path
- @hide_top_links = true
- @content_class = 'limit-container-width'
- page_title s_('SlackIntegration|GitLab for Slack')
.js-gitlab-slack-application{ data: gitlab_slack_application_data(@projects) }

View File

@ -0,0 +1,20 @@
- page_title _('Edit Slack integration')
.row.gl-mt-3.gl-mb-3
.col-lg-3
%h4.gl-mt-0
= s_('Integrations|Edit project alias')
%p= s_('Integrations|You can use this alias in your Slack commands')
.col-lg-9
= form_errors(@slack_integration)
= form_for(@slack_integration, url: project_settings_slack_path(@project), method: :put, html: { class: 'gl-show-field-errors js-integration-settings-form'}) do |form|
.form-group.row
= form.label :alias, s_('Integrations|Enter your alias'), class: 'col-form-label'
.col-sm-10
= form.text_field :alias, class: 'form-control', placeholder: @slack_integration.alias, required: true
.footer-block.row-content-block
= form.submit _('Save changes'), pajamas_button: true
&nbsp;
= link_to _('Cancel'), edit_project_settings_integration_path(@project, @service), class: 'btn gl-button btn-cancel'

View File

@ -39,6 +39,13 @@ resource :profile, only: [:show, :update] do
put :reset
end
end
resource :slack, only: [:edit] do
member do
get :slack_link
end
end
resource :preferences, only: [:show, :update]
resources :comment_templates, only: [:index, :show], action: :index

View File

@ -144,6 +144,10 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end
end
resource :slack, only: [:destroy, :edit, :update] do
get :slack_auth
end
resource :repository, only: [:show, :update], controller: :repository do
# TODO: Removed this "create_deploy_token" route after change was made in app/helpers/ci_variables_helper.rb:14
# See MR comment for more detail: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/27059#note_311585356

View File

@ -224,6 +224,7 @@ const alias = {
test_fixtures_static: path.join(ROOT_PATH, 'spec/frontend/fixtures/static'),
test_helpers: path.join(ROOT_PATH, 'spec/frontend_integration/test_helpers'),
public: path.join(ROOT_PATH, 'public'),
storybook_addons: path.resolve(ROOT_PATH, 'storybook/config/addons'),
};
if (IS_EE) {

View File

@ -1747,7 +1747,7 @@ If the above steps are **not successful**, proceed through the next steps:
If different operating systems or different operating system versions are deployed across Geo sites, you should perform a locale data compatibility check before setting up Geo.
Geo uses PostgreSQL and Streaming Replication to replicate data across Geo sites. PostgreSQL uses locale data provided by the operating system's C library for sorting text. If the locale data in the C library is incompatible across Geo sites, erroneous query results that lead to [incorrect behavior on secondary sites](https://gitlab.com/gitlab-org/gitlab/-/issues/360723).
Geo uses PostgreSQL and Streaming Replication to replicate data across Geo sites. PostgreSQL uses locale data provided by the operating system's C library for sorting text. If the locale data in the C library is incompatible across Geo sites, it can cause erroneous query results that lead to [incorrect behavior on secondary sites](https://gitlab.com/gitlab-org/gitlab/-/issues/360723).
For example, Ubuntu 18.04 (and earlier) and RHEL/Centos7 (and earlier) are incompatible with their later releases.
See the [PostgreSQL wiki for more details](https://wiki.postgresql.org/wiki/Locale_data_changes).

View File

@ -65,9 +65,9 @@ This section documents the current behavior of GitLab when Silent Mode is enable
Incoming emails still raise issues, but the users who sent the emails to [Service Desk](../../user/project/service_desk.md) are not notified of issue creation or comments on their issues.
### Project and group webhooks
### Webhooks
Project and group webhooks are suppressed. The relevant Sidekiq jobs fail 4 times and then disappear, while Silent Mode is enabled. [Issue 393639](https://gitlab.com/gitlab-org/gitlab/-/issues/393639) discusses preventing the Sidekiq jobs from running in the first place.
[Project and group webhooks](../../user/project/integrations/webhooks.md), and [system hooks](../system_hooks.md) are suppressed. The relevant Sidekiq jobs fail 4 times and then disappear, while Silent Mode is enabled. [Issue 393639](https://gitlab.com/gitlab-org/gitlab/-/issues/393639) discusses preventing the Sidekiq jobs from running in the first place.
Triggering webhook tests via the UI results in HTTP status 500 responses.

View File

@ -2219,13 +2219,14 @@ Returns:
- `403 Forbidden` if not authenticated as an administrator.
- `404 User Not Found` if user cannot be found.
## Create a runner **(FREE SELF)**
## Create a runner **(FREE)**
Creates a runner linked to the current user.
Prerequisites:
- You must be an administrator or have the Owner role of the target namespace or project.
- For `instance_type`, you must be an administrator of the GitLab instance.
Be sure to copy or save the `token` in the response, the value cannot be retrieved again.

View File

@ -78,12 +78,29 @@ a starting point.
You can also use the GitLab API Access panel in the Storybook UI to set the GitLab instance URL and access token.
### Using REST API
### Set up API access in your stories
You should apply the `withGitLabAPIAccess` decorator to the stories that will consume GitLabs APIs. This decorator
will display a badge indicating that the story won't work without providing the API access parameters:
```javascript
import { withGitLabAPIAccess } from 'storybook_addons/gitlab_api_access';
import Api from '~/api';
import { ContentEditor } from './index';
export default {
component: ContentEditor,
title: 'ce/content_editor/content_editor',
decorators: [withGitLabAPIAccess],
};
```
#### Using REST API
The Storybook sets up `~/lib/utils/axios_utils` in `storybook/config/preview.js`. Components that use the REST API
should work out of the box as long as you provide a valid GitLab instance URL and access token.
### Using GraphQL
#### Using GraphQL
To write a story for a component that uses the GraphQL API, use the `createVueApollo` method provided in
the Story context.
@ -91,6 +108,7 @@ the Story context.
```javascript
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import { withGitLabAPIAccess } from 'storybook_addons/gitlab_api_access';
import WorkspacesList from './list.vue';
Vue.use(VueApollo);
@ -99,6 +117,9 @@ const Template = (_, { argTypes, createVueApollo }) => {
return {
components: { WorkspacesList },
apolloProvider: createVueApollo(),
provide: {
emptyStateSvgPath: '',
},
props: Object.keys(argTypes),
template: '<workspaces-list />',
};
@ -107,10 +128,10 @@ const Template = (_, { argTypes, createVueApollo }) => {
export default {
component: WorkspacesList,
title: 'ee/remote_development/workspaces_list',
decorators: [withGitLabAPIAccess],
};
export const Default = Template.bind({});
Default.args = {};
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -31,6 +31,11 @@ in that row:
- False positive **{false-positive}**: The scanner determined this vulnerability to be a false
positive.
To open an issue created for a vulnerability, hover over the **Activity** entry, then select the link.
The issue icon (**{issues}**) indicates the issue's status. If Jira issue support is enabled, the
issue link found in the **Activity** entry links out to the issue in Jira. Unlike GitLab issues, the
status of a Jira issue is not shown in the GitLab UI.
![Example project-level Vulnerability Report](img/project_level_vulnerability_report_v14_5.png)
## Project-level Vulnerability Report
@ -57,7 +62,6 @@ From the Vulnerability Report you can:
- [Filter the list of vulnerabilities](#filter-the-list-of-vulnerabilities).
- [View more details about a vulnerability](#view-details-of-a-vulnerability).
- [View vulnerable source location](#view-vulnerable-source-location) (if available).
- [View an issue raised for a vulnerability](#view-issues-raised-for-a-vulnerability).
- [Change the status of vulnerabilities](#change-status-of-vulnerabilities).
- [Export details of vulnerabilities](#export-vulnerability-details).
- [Sort vulnerabilities by date](#sort-vulnerabilities-by-date-detected).
@ -151,15 +155,6 @@ in the default branch.
To view the relevant file, select the filename in the vulnerability's details.
## View issues raised for a vulnerability
The **Activity** column indicates the number of issues that have been created for the vulnerability.
Hover over an **Activity** entry and select a link go to that issue. The status of whether the issue is open or closed also displays in the hover menu.
![Display attached issues](img/vulnerability_list_table_v13_9.png)
If Jira issue support is enabled, the issue link found in the Activity entry links out to the issue in Jira. Unlike GitLab issues, the status of whether a Jira issue is Open or Closed does not display in the GitLab UI.
## Change status of vulnerabilities
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/292636) in GitLab 13.10, all statuses became selectable.

View File

@ -20,8 +20,6 @@ module API
helpers do
# EE::API::Projects would override this method
def apply_filters(projects)
projects = projects.with_issues_available_for_user(current_user) if params[:with_issues_enabled]
projects = projects.with_merge_requests_enabled if params[:with_merge_requests_enabled]
projects = projects.with_statistics if params[:statistics]
projects = projects.joins(:statistics) if params[:order_by].include?('project_statistics') # rubocop: disable CodeReuse/ActiveRecord
projects = projects.created_by(current_user).imported.with_import_state if params[:imported]

View File

@ -0,0 +1,63 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Profiles::SlacksController, feature_category: :integrations do
let_it_be(:user) { create(:user) }
before do
sign_in(user)
allow(subject).to receive(:current_user).and_return(user)
end
describe 'GET edit' do
before do
get :edit
end
it 'renders' do
expect(response).to render_template :edit
end
it 'assigns projects' do
expect(assigns[:projects]).to eq []
end
it 'assigns disabled_projects' do
expect(assigns[:disabled_projects]).to eq []
end
end
describe 'GET slack_link' do
let_it_be(:project) { create(:project) }
context 'when user is not a maintainer of the project' do
before do
project.add_developer(user)
end
it 'renders 404' do
get :slack_link, params: { project_id: project.id }
expect(response).to have_gitlab_http_status(:not_found)
expect(response.body).to be_blank
end
end
context 'when user is a maintainer of the project' do
before do
project.add_maintainer(user)
end
it 'renders slack link' do
allow(controller).to receive(:add_to_slack_link).and_return('mock_redirect_link')
get :slack_link, params: { project_id: project.id }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to eq({ 'add_to_slack_link' => 'mock_redirect_link' })
end
end
end
end

View File

@ -0,0 +1,118 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Projects::Settings::SlacksController, feature_category: :integrations do
let_it_be_with_refind(:project) { create(:project, :public) }
let_it_be(:user) { create(:user) }
before do
project.add_maintainer(user)
sign_in(user)
end
def redirect_url(project)
edit_project_settings_integration_path(
project,
Integrations::GitlabSlackApplication.to_param
)
end
describe 'GET slack_auth' do
def stub_service(result)
service = double
expect(service).to receive(:execute).and_return(result)
expect(Projects::SlackApplicationInstallService)
.to receive(:new).with(project, user, anything).and_return(service)
end
context 'when valid CSRF token is provided' do
before do
allow(controller).to receive(:check_oauth_state).and_return(true)
end
it 'calls service and redirects with no alerts if result is successful' do
stub_service(status: :success)
get :slack_auth, params: { namespace_id: project.namespace, project_id: project }
expect(response).to have_gitlab_http_status(:found)
expect(response).to redirect_to(redirect_url(project))
expect(flash[:alert]).to be_nil
expect(session[:slack_install_success]).to be(true)
end
it 'calls service and redirects with the alert if there is error' do
stub_service(status: :error, message: 'error')
get :slack_auth, params: { namespace_id: project.namespace, project_id: project }
expect(response).to have_gitlab_http_status(:found)
expect(response).to redirect_to(redirect_url(project))
expect(flash[:alert]).to eq('error')
end
end
context 'when no CSRF token is provided' do
it 'returns 403' do
get :slack_auth, params: { namespace_id: project.namespace, project_id: project }
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'when there was an OAuth error' do
it 'redirects with an alert' do
get :slack_auth, params: { namespace_id: project.namespace, project_id: project, error: 'access_denied' }
expect(flash[:alert]).to eq('Access denied')
expect(response).to have_gitlab_http_status(:found)
expect(response).to redirect_to(redirect_url(project))
end
end
end
describe 'POST update' do
let_it_be(:integration) { create(:gitlab_slack_application_integration, project: project) }
let(:params) do
{ namespace_id: project.namespace, project_id: project, slack_integration: { alias: new_alias } }
end
context 'when alias is valid' do
let(:new_alias) { 'foo' }
it 'updates the record' do
expect do
post :update, params: params
end.to change { integration.reload.slack_integration.alias }.to(new_alias)
expect(response).to have_gitlab_http_status(:found)
expect(response).to redirect_to(redirect_url(project))
end
end
context 'when alias is invalid' do
let(:new_alias) { '' }
it 'does not update the record' do
expect do
post :update, params: params
end.not_to change { integration.reload.slack_integration.alias }
expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template('projects/settings/slacks/edit')
end
end
end
describe 'DELETE destroy' do
it 'destroys the record' do
create(:gitlab_slack_application_integration, project: project)
expect do
delete :destroy, params: { namespace_id: project.namespace, project_id: project }
end.to change { project.gitlab_slack_application_integration.reload.slack_integration }.to(nil)
expect(response).to have_gitlab_http_status(:found)
expect(response).to redirect_to(redirect_url(project))
end
end
end

View File

@ -2,7 +2,9 @@
require 'spec_helper'
RSpec.describe IntegrationsHelper do
RSpec.describe IntegrationsHelper, feature_category: :integrations do
let_it_be_with_refind(:project) { create(:project) }
shared_examples 'is defined for each integration event' do
Integration.available_integration_names.each do |integration|
events = Integration.integration_name_to_model(integration).new.configurable_events
@ -84,13 +86,60 @@ RSpec.describe IntegrationsHelper do
]
end
let(:slack_app_fields) do
[
:upgrade_slack_url,
:should_upgrade_slack
]
end
subject { helper.integration_form_data(integration) }
context 'with Slack integration' do
let(:integration) { build(:integrations_slack) }
context 'with a GitLab for Slack App integration' do
let(:integration) { build(:gitlab_slack_application_integration, project: project) }
let(:redirect_url) do
"http://test.host/#{project.full_path}/-/settings/slack/slack_auth"
end
before do
allow(helper).to receive(:slack_auth_project_settings_slack_url).and_return(redirect_url)
end
it { is_expected.to include(*fields, *slack_app_fields) }
it { is_expected.not_to include(*jira_fields) }
it 'includes app upgrade URL' do
stub_application_setting(slack_app_id: 'MOCK_APP_ID')
expect(subject[:upgrade_slack_url]).to start_with(
[
Projects::SlackApplicationInstallService::SLACK_AUTHORIZE_URL,
'?client_id=MOCK_APP_ID',
"&redirect_uri=#{CGI.escape(redirect_url)}"
].join
)
end
it 'includes the flag to upgrade Slack app, set to true' do
expect(subject[:should_upgrade_slack]).to eq 'true'
end
context 'when the integration includes all necessary scopes' do
let(:integration) { create(:gitlab_slack_application_integration, :all_features_supported, project: project) }
it 'includes the flag to upgrade Slack app, set to false' do
expect(subject[:should_upgrade_slack]).to eq 'false'
end
end
end
context 'with Jenkins integration' do
let(:integration) { build(:jenkins_integration) }
it { is_expected.to include(*fields) }
it { is_expected.not_to include(*jira_fields) }
it { is_expected.not_to include(*slack_app_fields) }
specify do
expect(subject[:reset_path]).to eq(helper.scoped_reset_integration_path(integration))
@ -101,10 +150,11 @@ RSpec.describe IntegrationsHelper do
end
end
context 'Jira service' do
context 'with Jira integration' do
let(:integration) { build(:jira_integration) }
it { is_expected.to include(*fields, *jira_fields) }
it { is_expected.not_to include(*slack_app_fields) }
end
end
@ -151,6 +201,66 @@ RSpec.describe IntegrationsHelper do
end
end
describe '#add_to_slack_link' do
let(:slack_link) { helper.add_to_slack_link(project, 'A12345') }
let(:query) { Rack::Utils.parse_query(URI.parse(slack_link).query) }
before do
allow(helper).to receive(:form_authenticity_token).and_return('a token')
allow(helper).to receive(:slack_auth_project_settings_slack_url).and_return('http://redirect')
end
it 'returns the endpoint URL with all needed params' do
expect(slack_link).to start_with(Projects::SlackApplicationInstallService::SLACK_AUTHORIZE_URL)
expect(slack_link).to include('&state=a+token')
expect(query).to include(
'scope' => 'commands,chat:write,chat:write.public',
'client_id' => 'A12345',
'redirect_uri' => 'http://redirect',
'state' => 'a token'
)
end
end
describe '#gitlab_slack_application_data' do
let_it_be(:projects) { create_list(:project, 3) }
def relation
Project.id_in(projects.pluck(:id)).inc_routes
end
before do
allow(helper).to receive(:current_user).and_return(build(:user))
allow(helper).to receive(:new_session_path).and_return('http://session-path')
end
it 'includes the required keys' do
additions = helper.gitlab_slack_application_data(relation)
expect(additions.keys).to include(
:projects,
:sign_in_path,
:is_signed_in,
:slack_link_path,
:gitlab_logo_path,
:slack_logo_path
)
end
it 'does not suffer from N+1 performance issues' do
baseline = ActiveRecord::QueryRecorder.new { helper.gitlab_slack_application_data(relation.limit(1)) }
expect do
helper.gitlab_slack_application_data(relation)
end.not_to exceed_query_limit(baseline)
end
it 'serializes nil projects without error' do
expect(helper.gitlab_slack_application_data(nil)).to include(projects: '[]')
end
end
describe '#integration_issue_type' do
using RSpec::Parameterized::TableSyntax
let_it_be(:issue) { create(:issue) }

View File

@ -8117,4 +8117,48 @@ RSpec.describe User, feature_category: :user_profile do
end
end
end
describe '#arkose_global_score' do
let_it_be(:user1) { create(:user) }
let_it_be(:user2) { create(:user) }
context 'when the user has an arkose global risk score' do
before do
create(:abuse_trust_score, user: user1, score: 12.0, source: :arkose_global_score)
create(:abuse_trust_score, user: user1, score: 24.0, source: :arkose_global_score)
end
it 'returns the latest score' do
expect(user1.arkose_global_score).to be(24.0)
end
end
context 'when the user does not have an arkose global risk score' do
it 'defaults to zero' do
expect(user2.arkose_global_score).to be(0.0)
end
end
end
describe '#arkose_custom_score' do
let_it_be(:user1) { create(:user) }
let_it_be(:user2) { create(:user) }
context 'when the user has an arkose custom risk score' do
before do
create(:abuse_trust_score, user: user1, score: 12.0, source: :arkose_custom_score)
create(:abuse_trust_score, user: user1, score: 24.0, source: :arkose_custom_score)
end
it 'returns the latest score' do
expect(user1.arkose_custom_score).to be(24.0)
end
end
context 'when the user does not have an arkose custom risk score' do
it 'defaults to zero' do
expect(user2.arkose_custom_score).to be(0.0)
end
end
end
end

View File

@ -0,0 +1,143 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Projects::SlackApplicationInstallService, feature_category: :integrations do
let_it_be(:user) { create(:user) }
let_it_be_with_refind(:project) { create(:project) }
let(:integration) { project.gitlab_slack_application_integration }
let(:installation) { integration.slack_integration }
let(:slack_app_id) { 'A12345' }
let(:slack_app_secret) { 'secret' }
let(:oauth_code) { 'code' }
let(:params) { { code: oauth_code } }
let(:exchange_url) { described_class::SLACK_EXCHANGE_TOKEN_URL }
let(:redirect_url) { Gitlab::Routing.url_helpers.slack_auth_project_settings_slack_url(project) }
subject(:service) { described_class.new(project, user, params) }
before do
stub_application_setting(slack_app_id: slack_app_id, slack_app_secret: slack_app_secret)
query = {
client_id: slack_app_id,
client_secret: slack_app_secret,
code: oauth_code,
redirect_uri: redirect_url
}
stub_request(:get, exchange_url)
.with(query: query)
.to_return(body: response.to_json, headers: { 'Content-Type' => 'application/json' })
end
context 'when Slack responds with an error' do
let(:response) do
{
ok: false,
error: 'something is wrong'
}
end
it 'returns error result' do
result = service.execute
expect(result).to eq(message: 'Slack: something is wrong', status: :error)
end
end
context 'when Slack responds with an access token' do
let_it_be(:team_id) { 'T11111' }
let_it_be(:team_name) { 'Team name' }
let_it_be(:user_id) { 'U11111' }
let_it_be(:bot_user_id) { 'U99999' }
let_it_be(:bot_access_token) { 'token-XXXXX' }
let(:response) do
{
ok: true,
app_id: 'A12345',
authed_user: { id: user_id },
token_type: 'bot',
access_token: bot_access_token,
bot_user_id: bot_user_id,
team: { id: team_id, name: 'Team name' },
enterprise: { is_enterprise_install: false },
scope: 'chat:a,chat:b,chat:c'
}
end
shared_examples 'success response' do
it 'returns success result and creates all needed records' do
result = service.execute
expect(result).to eq(status: :success)
expect(integration).to be_present
expect(installation).to be_present
expect(installation).to have_attributes(
integration_id: integration.id,
team_id: team_id,
team_name: team_name,
alias: project.full_path,
user_id: user_id,
bot_user_id: bot_user_id,
bot_access_token: bot_access_token,
authorized_scope_names: contain_exactly('chat:a', 'chat:b', 'chat:c')
)
end
end
it_behaves_like 'success response'
context 'when integration record already exists' do
before do
project.create_gitlab_slack_application_integration!
end
it_behaves_like 'success response'
context 'when installation record already exists' do
before do
integration.create_slack_integration!(
team_id: 'old value',
team_name: 'old value',
alias: 'old value',
user_id: 'old value',
bot_user_id: 'old value',
bot_access_token: 'old value'
)
end
it_behaves_like 'success response'
end
end
context 'when the team has other Slack installation records' do
let_it_be_with_reload(:other_installation) { create(:slack_integration, team_id: team_id) }
let_it_be_with_reload(:other_legacy_installation) { create(:slack_integration, :legacy, team_id: team_id) }
let_it_be_with_reload(:legacy_installation_for_other_team) { create(:slack_integration, :legacy) }
it_behaves_like 'success response'
it 'updates related legacy records' do
travel_to(1.minute.from_now) do
expected_attributes = {
'user_id' => user_id,
'bot_user_id' => bot_user_id,
'bot_access_token' => bot_access_token,
'updated_at' => Time.current,
'authorized_scope_names' => %w[chat:a chat:b chat:c]
}
service.execute
expect(other_installation).to have_attributes(expected_attributes)
expect(other_legacy_installation).to have_attributes(expected_attributes)
expect(legacy_installation_for_other_team).not_to have_attributes(expected_attributes)
end
end
end
end
end

View File

@ -109,7 +109,6 @@
- './ee/spec/controllers/profiles/billings_controller_spec.rb'
- './ee/spec/controllers/profiles_controller_spec.rb'
- './ee/spec/controllers/profiles/keys_controller_spec.rb'
- './ee/spec/controllers/profiles/slacks_controller_spec.rb'
- './ee/spec/controllers/profiles/usage_quotas_controller_spec.rb'
- './ee/spec/controllers/projects/analytics/cycle_analytics/summary_controller_spec.rb'
- './ee/spec/controllers/projects/analytics/issues_analytics_controller_spec.rb'
@ -157,7 +156,6 @@
- './ee/spec/controllers/projects/settings/integrations_controller_spec.rb'
- './ee/spec/controllers/projects/settings/operations_controller_spec.rb'
- './ee/spec/controllers/projects/settings/repository_controller_spec.rb'
- './ee/spec/controllers/projects/settings/slacks_controller_spec.rb'
- './ee/spec/controllers/projects/subscriptions_controller_spec.rb'
- './ee/spec/controllers/projects/vulnerability_feedback_controller_spec.rb'
- './ee/spec/controllers/registrations/company_controller_spec.rb'
@ -2872,7 +2870,6 @@
- './ee/spec/services/projects/protect_default_branch_service_spec.rb'
- './ee/spec/services/projects/restore_service_spec.rb'
- './ee/spec/services/projects/setup_ci_cd_spec.rb'
- './ee/spec/services/projects/slack_application_install_service_spec.rb'
- './ee/spec/services/projects/transfer_service_spec.rb'
- './ee/spec/services/projects/update_mirror_service_spec.rb'
- './ee/spec/services/projects/update_service_spec.rb'

View File

@ -0,0 +1 @@
export { withGitLabAPIAccess } from './preview';

View File

@ -1,8 +1,9 @@
import { addons } from '@storybook/addons';
import { GlBadge } from '@gitlab/ui';
import { FORCE_REMOUNT } from '@storybook/core-events';
import VueApollo from 'vue-apollo';
import axios from '~/lib/utils/axios_utils';
import createDefaultClient from '~/lib/graphql';
import VueApollo from 'vue-apollo';
import { GITLAB_API_ACCESS_UPDATE_EVENT } from './constants';
/**
@ -66,8 +67,16 @@ export const withGitLabAPIAccess = (story, context) => {
return {
components: {
story,
GlBadge,
},
template: '<story />',
template: `
<div>
<div class="gl-display-flex gl-justify-content-end">
<gl-badge variant="info">Requires API access</gl-badge>
</div>
<story />
</div>
`,
};
};

View File

@ -2,7 +2,7 @@
import './gon';
import Vue from 'vue';
import translateMixin from '~/vue_shared/translate';
import { withGitLabAPIAccess, initializeGitLabAPIAccess } from './addons/gitlab_api_access/preview';
import { initializeGitLabAPIAccess } from './addons/gitlab_api_access/preview';
const stylesheetsRequireCtx = require.context(
'../../app/assets/stylesheets',
@ -17,5 +17,3 @@ translateMixin(Vue);
stylesheetsRequireCtx('./application.scss');
stylesheetsRequireCtx('./application_utilities.scss');
stylesheetsRequireCtx('./highlight/themes/white.scss');
export const decorators = [withGitLabAPIAccess];