...
 
Commits (28)
......@@ -208,9 +208,6 @@ gem 'connection_pool', '~> 2.0'
# Redis session store
gem 'redis-rails', '~> 5.0.2'
# Discord integration
gem 'discordrb-webhooks-blackst0ne', '~> 3.3', require: false
# HipChat integration
gem 'hipchat', '~> 1.5.0'
......
......@@ -194,8 +194,6 @@ GEM
rotp (~> 2.0)
diff-lcs (1.3)
diffy (3.1.0)
discordrb-webhooks-blackst0ne (3.3.0)
rest-client (~> 2.0)
docile (1.3.1)
domain_name (0.5.20180417)
unf (>= 0.0.5, < 1.0.0)
......@@ -1071,7 +1069,6 @@ DEPENDENCIES
devise (~> 4.6)
devise-two-factor (~> 3.0.0)
diffy (~> 3.1.0)
discordrb-webhooks-blackst0ne (~> 3.3)
doorkeeper (~> 4.3)
doorkeeper-openid_connect (~> 1.5)
ed25519 (~> 1.2)
......
......@@ -24,6 +24,9 @@ class GroupsController < Groups::ApplicationController
before_action :user_actions, only: [:show]
before_action :validate_name, only: [:create, :update, :transfer]
skip_cross_project_access_check :index, :new, :create, :edit, :update,
:destroy, :projects
# When loading show as an atom feed, we render events that could leak cross
......@@ -221,6 +224,19 @@ class GroupsController < Groups::ApplicationController
end
end
def validate_name
group = Group.new(group_params)
unless !group.path || group.path.empty? || group.path.length > 4 || current_user.admin?
flash.now[:alert] = 'Path must have at least a length of 5.'
if action_name == 'update'
render action: 'edit'
else
@group = group
render action: 'new'
end
end
end
def build_canonical_path(group)
return group_path(group) if action_name == 'show' # root group path
......
......@@ -5,6 +5,7 @@ class PasswordsController < Devise::PasswordsController
before_action :resource_from_email, only: [:create]
before_action :check_password_authentication_available, only: [:create]
before_action :prevent_cas_reset, only: [:create]
before_action :throttle_reset, only: [:create]
# rubocop: disable CodeReuse/ActiveRecord
......@@ -55,6 +56,13 @@ class PasswordsController < Devise::PasswordsController
alert: _("Password authentication is unavailable.")
end
def prevent_cas_reset
return unless resource && resource.cas_user?
redirect_to after_sending_reset_password_instructions_path_for(resource_name),
alert: "Cannot reset password for CAS user."
end
def throttle_reset
return unless resource && resource.recently_sent_password_reset?
......
......@@ -8,20 +8,6 @@ class Profiles::AccountsController < Profiles::ApplicationController
end
# rubocop: disable CodeReuse/ActiveRecord
def unlink
provider = params[:provider]
identity = current_user.identities.find_by(provider: provider)
return render_404 unless identity
if unlink_provider_allowed?(provider)
identity.destroy
else
flash[:alert] = _("You are not allowed to unlink your primary login account")
end
redirect_to profile_account_path
end
# rubocop: enable CodeReuse/ActiveRecord
private
......
......@@ -406,7 +406,7 @@ module ProjectsHelper
snippets: :read_project_snippet,
settings: :admin_project,
builds: :read_build,
clusters: :read_cluster,
#clusters: :read_cluster,
serverless: :read_cluster,
error_tracking: :read_sentry_issue,
labels: :read_label,
......@@ -468,11 +468,7 @@ module ProjectsHelper
end
def default_clone_protocol
if allowed_protocols_present?
enabled_protocol
else
extra_default_clone_protocol
end
extra_default_clone_protocol
end
def extra_default_clone_protocol
......
# frozen_string_literal: true
class DeviseMailer < Devise::Mailer
default from: "#{Gitlab.config.gitlab.email_display_name} <#{Gitlab.config.gitlab.email_from}>"
default reply_to: Gitlab.config.gitlab.email_reply_to
default from: proc { default_sender_address.format }
default reply_to: proc { default_reply_to_address.format }
layout 'mailer/devise'
......@@ -17,4 +17,16 @@ class DeviseMailer < Devise::Mailer
subject.join(' | ')
end
def default_sender_address
address = Mail::Address.new(Gitlab.config.gitlab.email_from)
address.display_name = Gitlab.config.gitlab.email_display_name
address
end
def default_reply_to_address
address = Mail::Address.new(Gitlab.config.gitlab.email_reply_to)
address.display_name = Gitlab.config.gitlab.email_display_name
address
end
end
......@@ -54,6 +54,7 @@ class Group < Namespace
validate :visibility_level_allowed_by_sub_groups
validate :visibility_level_allowed_by_parent
validates :variables, variable_duplicates: true
validates :path, format: { without: /\A([a-zA-Z]{4}[0-9]{2}|hg[0-9]+)\z/, message: "must not match the format of THM usernames" }
validates :two_factor_grace_period, presence: true, numericality: { greater_than_or_equal_to: 0 }
......
......@@ -137,7 +137,6 @@ class Project < ApplicationRecord
# Project services
has_one :campfire_service
has_one :discord_service
has_one :drone_ci_service
has_one :emails_on_push_service
has_one :pipelines_email_service
......@@ -344,6 +343,7 @@ class Project < ApplicationRecord
inclusion: { in: ->(_object) { Gitlab.config.repositories.storages.keys } }
validates :variables, variable_duplicates: { scope: :environment_scope }
validates :bfg_object_map, file_size: { maximum: :max_attachment_size }
validates :path, format: { without: /\A([a-zA-Z]{4}[0-9]{2}|hg[0-9]+)\z/, message: "must not match the format of THM usernames" }
# Scopes
scope :pending_delete, -> { where(pending_delete: true) }
......
# frozen_string_literal: true
require "discordrb/webhooks"
class DiscordService < ChatNotificationService
def title
s_("DiscordService|Discord Notifications")
end
def description
s_("DiscordService|Receive event notifications in Discord")
end
def self.to_param
"discord"
end
def help
"This service sends notifications about project events to Discord channels.<br />
To set up this service:
<ol>
<li><a href='https://support.discordapp.com/hc/en-us/articles/228383668-Intro-to-Webhooks'>Setup a custom Incoming Webhook</a>.</li>
<li>Paste the <strong>Webhook URL</strong> into the field below.</li>
<li>Select events below to enable notifications.</li>
</ol>"
end
def event_field(event)
# No-op.
end
def default_channel_placeholder
# No-op.
end
def self.supported_events
%w[push issue confidential_issue merge_request note confidential_note tag_push
pipeline wiki_page]
end
def default_fields
[
{ type: "text", name: "webhook", placeholder: "e.g. https://discordapp.com/api/webhooks/…" },
{ type: "checkbox", name: "notify_only_broken_pipelines" },
{ type: "checkbox", name: "notify_only_default_branch" }
]
end
private
def notify(message, opts)
client = Discordrb::Webhooks::Client.new(url: webhook)
client.execute do |builder|
builder.content = message.pretext
end
end
def custom_data(data)
super(data).merge(markdown: true)
end
end
......@@ -251,7 +251,6 @@ class Service < ApplicationRecord
bugzilla
campfire
custom_issue_tracker
discord
drone_ci
emails_on_push
external_wiki
......
......@@ -849,11 +849,11 @@ class User < ApplicationRecord
end
def allow_password_authentication_for_web?
Gitlab::CurrentSettings.password_authentication_enabled_for_web? && !ldap_user? && !ultraauth_user?
Gitlab::CurrentSettings.password_authentication_enabled_for_web? && !ldap_user? && !cas_user? && !ultraauth_user?
end
def allow_password_authentication_for_git?
Gitlab::CurrentSettings.password_authentication_enabled_for_git? && !ldap_user? && !ultraauth_user?
Gitlab::CurrentSettings.password_authentication_enabled_for_git? && !ldap_user? && !cas_user? && !ultraauth_user?
end
def can_change_username?
......@@ -945,6 +945,14 @@ class User < ApplicationRecord
@ldap_identity ||= identities.find_by(["provider LIKE ?", "ldap%"])
end
def cas_user?
identities.exists?(["provider = ? AND extern_uid IS NOT NULL", "cas3"])
end
def cas_identity
@cas_identity ||= identities.find_by(["provider = ?", "cas3"])
end
def project_deploy_keys
DeployKey.in_projects(authorized_projects.select(:id)).distinct(:id)
end
......
......@@ -256,6 +256,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
end
def autodevops_anchor_data(show_auto_devops_callout: false)
return
if current_user && can?(current_user, :admin_pipeline, project) && repository.gitlab_ci_yml.blank? && !show_auto_devops_callout
if auto_devops_enabled?
AnchorData.new(false,
......@@ -275,6 +276,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
end
def kubernetes_cluster_anchor_data
return
if current_user && can?(current_user, :create_cluster, project)
if clusters.empty?
......
......@@ -67,10 +67,6 @@ class FileUploader < GitlabUploader
SecureRandom.hex
end
def self.extract_dynamic_path(path)
DYNAMIC_PATH_PATTERN.match(path)
end
def upload_paths(identifier)
[
File.join(secret, identifier),
......@@ -145,7 +141,7 @@ class FileUploader < GitlabUploader
return if apply_context!(value.uploader_context)
# fallback to the regex based extraction
if matches = self.class.extract_dynamic_path(value.path)
if matches = DYNAMIC_PATH_PATTERN.match(value.path)
@secret = matches[:secret]
@identifier = matches[:identifier]
end
......
= render "dashboard/projects", projects: @projects
.prepend-top-20
= render 'shared/promo'
- page_title "Sign in"
#signin-container
- if omniauth_enabled? && devise_mapping.omniauthable? && button_based_providers_enabled?
%div.append-bottom-default
= render 'devise/shared/omniauth_box'
#signin-container.prepend-top-15
- if form_based_providers.any?
= render 'devise/shared/tabs_ldap'
- else
......@@ -17,7 +21,3 @@
- if !password_authentication_enabled_for_web? && !ldap_enabled? && !(omniauth_enabled? && devise_mapping.omniauthable?)
%div
No authentication methods configured.
- if omniauth_enabled? && devise_mapping.omniauthable? && button_based_providers_enabled?
.clearfix
= render 'devise/shared/omniauth_box'
.omniauth-container.prepend-top-15
.omniauth-container
%label.label-bold.d-block
Sign in with
- providers = enabled_button_based_providers
......
......@@ -7,8 +7,8 @@
%h1
= default_brand_title
- if user_signed_in?
%span= link_to_version
= version_status_badge
%span= Gitlab::VERSION + '-thm'
%small= link_to Gitlab.revision, namespace_project_commits_path('projects.thm.de', 'gitlab', Gitlab.revision)
%hr
- unless Gitlab::CurrentSettings.help_page_hide_commercial_content?
......
......@@ -78,7 +78,7 @@
%li.nav-item
%div
- sign_in_text = allow_signup? ? _('Sign in / Register') : _('Sign in')
= link_to sign_in_text, new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in'
= link_to "Sign in", omniauth_authorize_path(:user, :cas3), method: :post, class: 'btn btn-sign-in', "data-no-turbolink" => "true"
%button.navbar-toggler.d-block.d-sm-none{ type: 'button' }
%span.sr-only= _('Toggle navigation')
......
......@@ -41,12 +41,6 @@
= link_to applications_profile_path do
%strong.fly-out-top-item-name
= _('Applications')
= nav_link(controller: :chat_names) do
= link_to profile_chat_names_path do
.nav-icon-container
= sprite_icon('comment')
%span.nav-item-name
= _('Chat')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :chat_names, html_options: { class: "fly-out-top-item" } ) do
= link_to profile_chat_names_path do
......@@ -74,7 +68,7 @@
= link_to profile_emails_path do
%strong.fly-out-top-item-name
= _('Emails')
- if current_user.allow_password_authentication?
- if current_user.allow_password_authentication? && !current_user.cas_user?
= nav_link(controller: :passwords) do
= link_to edit_profile_password_path do
.nav-icon-container
......
......@@ -8,28 +8,22 @@
.row.prepend-top-default
.col-lg-4.profile-settings-sidebar
%h4.prepend-top-0
= s_('Profiles|Two-Factor Authentication')
%p
= s_("Profiles|Increase your account's security by enabling Two-Factor Authentication (2FA)")
.col-lg-8
%p
#{_('Status')}: #{current_user.two_factor_enabled? ? _('Enabled') : _('Disabled')}
- if current_user.two_factor_enabled?
= link_to _('Manage two-factor authentication'), profile_two_factor_auth_path, class: 'btn btn-info'
- else
.append-bottom-10
= link_to _('Enable two-factor authentication'), profile_two_factor_auth_path, class: 'btn btn-success'
%hr
- if display_providers_on_profile?
- if !current_user.cas_user?
.row.prepend-top-default
.col-lg-4.profile-settings-sidebar
%h4.prepend-top-0
= s_('Profiles|Social sign-in')
= s_('Profiles|Two-Factor Authentication')
%p
= s_('Profiles|Activate signin with one of the following services')
= s_("Profiles|Increase your account's security by enabling Two-Factor Authentication (2FA)")
.col-lg-8
= render 'providers', providers: button_based_providers, group_saml_identities: local_assigns[:group_saml_identities]
%p
#{_('Status')}: #{current_user.two_factor_enabled? ? _('Enabled') : _('Disabled')}
- if current_user.two_factor_enabled?
= link_to _('Manage two-factor authentication'), profile_two_factor_auth_path, class: 'btn btn-info'
- else
.append-bottom-10
= link_to _('Enable two-factor authentication'), profile_two_factor_auth_path, class: 'btn btn-success'
%hr
- if current_user.can_change_username?
.row.prepend-top-default
......
......@@ -35,10 +35,6 @@
%a.nav-link.active{ href: '#blank-project-pane', id: 'blank-project-tab', data: { toggle: 'tab', track_label: 'blank_project', track_event: "click_tab", track_value: "" }, role: 'tab' }
%span.d-none.d-sm-block= s_('ProjectsNew|Blank project')
%span.d-block.d-sm-none= s_('ProjectsNew|Blank')
%li.nav-item{ role: 'presentation' }
%a.nav-link{ href: '#create-from-template-pane', id: 'create-from-template-tab', data: { toggle: 'tab', track_label: 'create_from_template', track_event: "click_tab", track_value: "" }, role: 'tab' }
%span.d-none.d-sm-block.qa-project-create-from-template-tab= s_('ProjectsNew|Create from template')
%span.d-block.d-sm-none= s_('ProjectsNew|Template')
%li.nav-item{ role: 'presentation' }
%a.nav-link{ href: '#import-project-pane', id: 'import-project-tab', data: { toggle: 'tab', track_label: 'import_project', track_event: "click_tab", track_value: "" }, role: 'tab' }
%span.d-none.d-sm-block= s_('ProjectsNew|Import project')
......@@ -50,18 +46,6 @@
= form_for @project, html: { class: 'new_project' } do |f|
= render 'new_project_fields', f: f, project_name_id: "blank-project-name"
#create-from-template-pane.tab-pane.js-toggle-container.px-0.pb-0{ class: active_when(active_tab == 'template'), role: 'tabpanel' }
.card.card-slim.m-4.p-4
%div
- contributing_templates_url = 'https://gitlab.com/gitlab-org/project-templates/contributing'
- link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: contributing_templates_url }
= _('Learn how to %{link_start}contribute to the built-in templates%{link_end}').html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
= form_for @project, html: { class: 'new_project' } do |f|
.project-template
.form-group
%div
= render 'project_templates', f: f, project: @project
.tab-pane.import-project-pane.js-toggle-container{ id: 'import-project-pane', class: active_when(active_tab == 'import'), role: 'tabpanel' }
- if import_sources_enabled?
= render 'import_project_pane', active_tab: active_tab
......
- if @project
= render 'projects/services/prometheus/configuration_banner', project: @project, service: @service
%h4.append-bottom-default
= s_('PrometheusService|Manual configuration')
......
......@@ -16,7 +16,8 @@
.settings-content
= render 'form'
%section.settings#autodevops-settings.no-animate{ class: ('expanded' if expanded), data: { qa_selector: 'autodevops_settings_content' } }
/
%section.settings#autodevops-settings.no-animate{ class: ('expanded' if expanded), data: { qa_selector: 'autodevops_settings_content' } }
.settings-header
%h4
= s_('CICD|Auto DevOps')
......
......@@ -2,7 +2,7 @@
.git-clone-holder.js-git-clone-holder.input-group
.input-group-prepend
- if allowed_protocols_present?
- if allowed_protocols_present? && @project.visibility_level == Gitlab::VisibilityLevel::PUBLIC || (current_user && !current_user.cas_user?)
.input-group-text.clone-dropdown-btn.btn
%span.js-clone-dropdown-label
= enabled_project_button(project, enabled_protocol)
......@@ -14,8 +14,9 @@
%ul.dropdown-menu.dropdown-menu-selectable.clone-options-dropdown
%li
= ssh_clone_button(project)
%li
= http_clone_button(project)
- if @project.visibility_level == Gitlab::VisibilityLevel::PUBLIC || (current_user && !current_user.cas_user?)
%li
= http_clone_button(project)
= render_if_exists 'shared/kerberos_clone_button', project: project
= text_field_tag :project_clone, default_url_to_repo(project), class: "js-select-on-focus form-control", readonly: true, aria: { label: 'Project clone URL' }
......
......@@ -7,7 +7,7 @@
%span
= _('Git repository URL')
= f.text_field :import_url, value: import_url.sanitized_url,
autocomplete: 'off', class: 'form-control', placeholder: 'https://gitlab.company.com/group/project.git', required: true
autocomplete: 'off', class: 'form-control', placeholder: 'https://git.thm.de/project/repository.git', required: true
.row
.form-group.col-md-6
......
......@@ -21,11 +21,6 @@ Rails.application.routes.draw do
draw :development
draw :ci
use_doorkeeper do
controllers applications: 'oauth/applications',
authorized_applications: 'oauth/authorized_applications',
authorizations: 'oauth/authorizations'
end
# This prefixless path is required because Jira gets confused if we set it up with a path
# More information: https://gitlab.com/gitlab-org/gitlab-ee/issues/6752
......
......@@ -5,6 +5,6 @@ class AddPrivateProfileToUsers < ActiveRecord::Migration[4.2]
DOWNTIME = false
def change
add_column :users, :private_profile, :boolean
add_column :users, :private_profile, :boolean, :default => true
end
end
# Discord Notifications service
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22684) in GitLab 11.6.
The Discord Notifications service sends event notifications from GitLab to the channel for which the webhook was created.
To send GitLab event notifications to a Discord channel, create a webhook in Discord and configure it in GitLab.
## Create webhook
1. Open the Discord channel you want to receive GitLab event notifications.
1. From the channel menu, select **Edit channel**.
1. Click on **Webhooks** menu item.
1. Click the **Create Webhook** button and fill in the name of the bot that will post the messages. Optionally, edit the avatar.
1. Note the URL from the **WEBHOOK URL** field.
1. Click the **Save** button.
## Configure created webhook in GitLab
With the webhook URL created in the Discord channel, you can set up the Discord Notifications service in GitLab.
1. Navigate to the [Integrations page](project_services.md#accessing-the-project-services) in your project's settings. That is, **Project > Settings > Integrations**.
1. Select the **Discord Notifications** project service to configure it.
1. Check the **Active** checkbox to turn on the service.
1. Check the checkboxes corresponding to the GitLab events for which you want to send notifications to Discord.
1. Paste the webhook URL that you copied from the create Discord webhook step.
1. Configure the remaining options and click the **Save changes** button.
The Discord channel you created the webhook for will now receive notification of the GitLab events that were configured.
......@@ -29,7 +29,6 @@ Click on the service links to see further configuration instructions and details
| [Bugzilla](bugzilla.md) | Bugzilla issue tracker |
| Campfire | Simple web-based real-time group chat |
| Custom Issue Tracker | Custom issue tracker |
| [Discord Notifications](discord_notifications.md) | Receive event notifications in Discord |
| Drone CI | Continuous Integration platform built on Docker, written in Go |
| [Emails on push](emails_on_push.md) | Email the commits and diff of each push to a list of recipients |
| External Wiki | Replaces the link to the internal wiki with a link to an external wiki |
......
......@@ -314,14 +314,6 @@ module API
desc: 'Title'
}
],
'discord' => [
{
required: true,
name: :webhook,
type: String,
desc: 'Discord webhook. e.g. https://discordapp.com/api/webhooks/…'
}
],
'drone-ci' => [
{
required: true,
......@@ -704,7 +696,6 @@ module API
::BuildkiteService,
::CampfireService,
::CustomIssueTrackerService,
::DiscordService,
::DroneCiService,
::EmailsOnPushService,
::ExternalWikiService,
......
......@@ -81,7 +81,7 @@ module Gitlab
# Get the first part of the email address (before @)
# In addition in removes illegal characters
def generate_username(email)
email.match(/^[^@]*/)[0].mb_chars.normalize(:kd).gsub(/[^\x00-\x7F]/, '').to_s
auth_hash.uid.to_s
end
def generate_temporarily_email(username)
......
......@@ -38,7 +38,12 @@ module Gitlab
private
def add_upload(upload)
uploader_context = FileUploader.extract_dynamic_path(upload).named_captures.symbolize_keys
secret, identifier = upload.split('/').last(2)
uploader_context = {
secret: secret,
identifier: identifier
}
UploadService.new(@project, File.open(upload, 'r'), FileUploader, uploader_context).execute.to_h
end
......
......@@ -249,7 +249,6 @@ project:
- last_event
- services
- campfire_service
- discord_service
- drone_ci_service
- emails_on_push_service
- pipelines_email_service
......
# frozen_string_literal: true
require "spec_helper"
describe DiscordService do
it_behaves_like "chat service", "Discord notifications" do
let(:client) { Discordrb::Webhooks::Client }
let(:client_arguments) { { url: webhook_url } }
let(:content_key) { :content }
end
end
# frozen_string_literal: true
require "spec_helper"
require 'spec_helper'
describe HangoutsChatService do
it_behaves_like "chat service", "Hangouts Chat" do
let(:client) { HangoutsChat::Sender }
let(:client_arguments) { webhook_url }
let(:content_key) { :text }
describe 'Associations' do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
end
describe 'Validations' do
context 'when service is active' do
before do
subject.active = true
end
it { is_expected.to validate_presence_of(:webhook) }
it_behaves_like 'issue tracker service URL attribute', :webhook
end
context 'when service is inactive' do
before do
subject.active = false
end
it { is_expected.not_to validate_presence_of(:webhook) }
end
end
describe '#execute' do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
let(:webhook_url) { 'https://example.gitlab.com/' }
before do
allow(subject).to receive_messages(
project: project,
project_id: project.id,
service_hook: true,
webhook: webhook_url
)
WebMock.stub_request(:post, webhook_url)
end
shared_examples 'Hangouts Chat service' do
it 'calls Hangouts Chat API' do
subject.execute(sample_data)
expect(WebMock)
.to have_requested(:post, webhook_url)
.with { |req| req.body =~ /\A{"text":.+}\Z/ }
.once
end
end
context 'with push events' do
let(:sample_data) do
Gitlab::DataBuilder::Push.build_sample(project, user)
end
it_behaves_like 'Hangouts Chat service'
it 'specifies the webhook when it is configured' do
expect(HangoutsChat::Sender).to receive(:new).with(webhook_url).and_return(double(:hangouts_chat_service).as_null_object)
subject.execute(sample_data)
end
context 'with not default branch' do
let(:sample_data) do
Gitlab::DataBuilder::Push.build(project, user, nil, nil, 'not-the-default-branch')
end
context 'when notify_only_default_branch enabled' do
before do
subject.notify_only_default_branch = true
end
it 'does not call the Hangouts Chat API' do
result = subject.execute(sample_data)
expect(result).to be_falsy
end
end
context 'when notify_only_default_branch disabled' do
before do
subject.notify_only_default_branch = false
end
it_behaves_like 'Hangouts Chat service'
end
end
end
context 'with issue events' do
let(:opts) { { title: 'Awesome issue', description: 'please fix' } }
let(:sample_data) do
service = Issues::CreateService.new(project, user, opts)
issue = service.execute
service.hook_data(issue, 'open')
end
it_behaves_like 'Hangouts Chat service'
end
context 'with merge events' do
let(:opts) do
{
title: 'Awesome merge_request',
description: 'please fix',
source_branch: 'feature',
target_branch: 'master'
}
end
let(:sample_data) do
service = MergeRequests::CreateService.new(project, user, opts)
merge_request = service.execute
service.hook_data(merge_request, 'open')
end
before do
project.add_developer(user)
end
it_behaves_like 'Hangouts Chat service'
end
context 'with wiki page events' do
let(:opts) do
{
title: 'Awesome wiki_page',
content: 'Some text describing some thing or another',
format: 'md',
message: 'user created page: Awesome wiki_page'
}
end
let(:wiki_page) { create(:wiki_page, wiki: project.wiki, attrs: opts) }
let(:sample_data) { Gitlab::DataBuilder::WikiPage.build(wiki_page, user, 'create') }
it_behaves_like 'Hangouts Chat service'
end
context 'with note events' do
let(:sample_data) { Gitlab::DataBuilder::Note.build(note, user) }
context 'with commit comment' do
let(:note) do
create(:note_on_commit, author: user,
project: project,
commit_id: project.repository.commit.id,
note: 'a comment on a commit')
end
it_behaves_like 'Hangouts Chat service'
end
context 'with merge request comment' do
let(:note) do
create(:note_on_merge_request, project: project,
note: 'merge request note')
end
it_behaves_like 'Hangouts Chat service'
end
context 'with issue comment' do
let(:note) do
create(:note_on_issue, project: project, note: 'issue note')
end
it_behaves_like 'Hangouts Chat service'
end
context 'with snippet comment' do
let(:note) do
create(:note_on_project_snippet, project: project,
note: 'snippet note')
end
it_behaves_like 'Hangouts Chat service'
end
end
context 'with pipeline events' do
let(:pipeline) do
create(:ci_pipeline,
project: project, status: status,
sha: project.commit.sha, ref: project.default_branch)
end
let(:sample_data) { Gitlab::DataBuilder::Pipeline.build(pipeline) }
context 'with failed pipeline' do
let(:status) { 'failed' }
it_behaves_like 'Hangouts Chat service'
end
context 'with succeeded pipeline' do
let(:status) { 'success' }
context 'with default notify_only_broken_pipelines' do
it 'does not call Hangouts Chat API' do
result = subject.execute(sample_data)
expect(result).to be_falsy
end
end
context 'when notify_only_broken_pipelines is false' do
before do
subject.notify_only_broken_pipelines = false
end
it_behaves_like 'Hangouts Chat service'
end
end
context 'with not default branch' do
let(:pipeline) do
create(:ci_pipeline, project: project, status: 'failed', ref: 'not-the-default-branch')
end
context 'when notify_only_default_branch enabled' do
before do
subject.notify_only_default_branch = true
end
it 'does not call the Hangouts Chat API' do
result = subject.execute(sample_data)
expect(result).to be_falsy
end
end
context 'when notify_only_default_branch disabled' do
before do
subject.notify_only_default_branch = false
end
it_behaves_like 'Hangouts Chat service'
end
end
end
end
end
......@@ -38,7 +38,6 @@ describe Project do
it { is_expected.to have_one(:asana_service) }
it { is_expected.to have_many(:boards) }
it { is_expected.to have_one(:campfire_service) }
it { is_expected.to have_one(:discord_service) }
it { is_expected.to have_one(:drone_ci_service) }
it { is_expected.to have_one(:emails_on_push_service) }
it { is_expected.to have_one(:pipelines_email_service) }
......
# frozen_string_literal: true
require "spec_helper"
shared_examples_for "chat service" do |service_name|
describe "Associations" do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
end
describe "Validations" do
context "when service is active" do
before do
subject.active = true
end
it { is_expected.to validate_presence_of(:webhook) }
it_behaves_like "issue tracker service URL attribute", :webhook
end
context "when service is inactive" do
before do
subject.active = false
end
it { is_expected.not_to validate_presence_of(:webhook) }
end
end
describe '.supported_events' do
it 'does not support deployment_events' do
expect(described_class.supported_events).not_to include('deployment')
end
end
describe "#execute" do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
let(:webhook_url) { "https://example.gitlab.com/" }
before do
allow(subject).to receive_messages(
project: project,
project_id: project.id,
service_hook: true,
webhook: webhook_url
)
WebMock.stub_request(:post, webhook_url)
end
shared_examples "#{service_name} service" do
it "calls #{service_name} API" do
subject.execute(sample_data)
expect(WebMock).to have_requested(:post, webhook_url).with { |req| req.body =~ /\A{"#{content_key}":.+}\Z/ }.once
end
end
context "with push events" do
let(:sample_data) do
Gitlab::DataBuilder::Push.build_sample(project, user)
end
it_behaves_like "#{service_name} service"
it "specifies the webhook when it is configured" do
expect(client).to receive(:new).with(client_arguments).and_return(double(:chat_service).as_null_object)
subject.execute(sample_data)
end
context "with not default branch" do
let(:sample_data) do
Gitlab::DataBuilder::Push.build(project: project, user: user, ref: "not-the-default-branch")
end
context "when notify_only_default_branch enabled" do
before do
subject.notify_only_default_branch = true
end
it "does not call the Discord Webhooks API" do
result = subject.execute(sample_data)
expect(result).to be_falsy
end
end
context "when notify_only_default_branch disabled" do
before do
subject.notify_only_default_branch = false
end
it_behaves_like "#{service_name} service"
end
end
end
context "with issue events" do
let(:opts) { { title: "Awesome issue", description: "please fix" } }
let(:sample_data) do
service = Issues::CreateService.new(project, user, opts)
issue = service.execute
service.hook_data(issue, "open")
end
it_behaves_like "#{service_name} service"
end
context "with merge events" do
let(:opts) do
{
title: "Awesome merge_request",
description: "please fix",
source_branch: "feature",
target_branch: "master"
}
end
let(:sample_data) do
service = MergeRequests::CreateService.new(project, user, opts)
merge_request = service.execute
service.hook_data(merge_request, "open")
end
before do
project.add_developer(user)
end
it_behaves_like "#{service_name} service"
end
context "with wiki page events" do
let(:opts) do
{
title: "Awesome wiki_page",
content: "Some text describing some thing or another",
format: "md",
message: "user created page: Awesome wiki_page"
}
end
let(:wiki_page) { create(:wiki_page, wiki: project.wiki, attrs: opts) }
let(:sample_data) { Gitlab::DataBuilder::WikiPage.build(wiki_page, user, "create") }
it_behaves_like "#{service_name} service"
end
context "with note events" do
let(:sample_data) { Gitlab::DataBuilder::Note.build(note, user) }
context "with commit comment" do
let(:note) do
create(:note_on_commit,
author: user,
project: project,
commit_id: project.repository.commit.id,
note: "a comment on a commit")
end
it_behaves_like "#{service_name} service"
end
context "with merge request comment" do
let(:note) do
create(:note_on_merge_request, project: project, note: "merge request note")
end
it_behaves_like "#{service_name} service"
end
context "with issue comment" do
let(:note) do
create(:note_on_issue, project: project, note: "issue note")
end
it_behaves_like "#{service_name} service"
end
context "with snippet comment" do
let(:note) do
create(:note_on_project_snippet, project: project, note: "snippet note")
end
it_behaves_like "#{service_name} service"
end
end
context "with pipeline events" do
let(:pipeline) do
create(:ci_pipeline,
project: project, status: status,
sha: project.commit.sha, ref: project.default_branch)
end
let(:sample_data) { Gitlab::DataBuilder::Pipeline.build(pipeline) }
context "with failed pipeline" do
let(:status) { "failed" }
it_behaves_like "#{service_name} service"
end
context "with succeeded pipeline" do
let(:status) { "success" }
context "with default notify_only_broken_pipelines" do
it "does not call Discord Webhooks API" do
result = subject.execute(sample_data)
expect(result).to be_falsy
end
end
context "when notify_only_broken_pipelines is false" do
before do
subject.notify_only_broken_pipelines = false
end
it_behaves_like "#{service_name} service"
end
end
context "with not default branch" do
let(:pipeline) do
create(:ci_pipeline, :failed, project: project,
sha: project.commit.sha, ref: "not-the-default-branch")
end
context "when notify_only_default_branch enabled" do
before do
subject.notify_only_default_branch = true
end
it "does not call the Discord Webhooks API" do
result = subject.execute(sample_data)
expect(result).to be_falsy
end
end
context "when notify_only_default_branch disabled" do
before do
subject.notify_only_default_branch = false
end
it_behaves_like "#{service_name} service"
end
end
end
end
end
......@@ -141,15 +141,6 @@ describe FileUploader do
end
end
describe '.extract_dynamic_path' do
it 'works with hashed storage' do
path = 'export/4b227777d4dd1fc61c6f884f48641d02b4d121d3fd328cb08b5531fcacdabf8a/test/uploads/72a497a02fe3ee09edae2ed06d390038/dummy.txt'
expect(described_class.extract_dynamic_path(path)[:identifier]).to eq('dummy.txt')
expect(described_class.extract_dynamic_path(path)[:secret]).to eq('72a497a02fe3ee09edae2ed06d390038')
end
end
describe '#secret' do
it 'generates a secret if none is provided' do
expect(described_class).to receive(:generate_secret).and_return('secret')
......