Feature #44337 » webhooks_admin_page.patch
| app/controllers/admin_controller.rb | ||
|---|---|---|
| 22 | 22 |
self.main_menu = false |
| 23 | 23 |
menu_item :projects, :only => :projects |
| 24 | 24 |
menu_item :plugins, :only => :plugins |
| 25 |
menu_item :webhooks, :only => :webhooks |
|
| 25 | 26 |
menu_item :info, :only => :info |
| 26 | 27 | |
| 27 | 28 |
before_action :require_admin |
| ... | ... | |
| 48 | 49 |
@plugins = Redmine::Plugin.all |
| 49 | 50 |
end |
| 50 | 51 | |
| 52 |
def webhooks |
|
| 53 |
@webhooks = Webhook.eager_load(:user).order(*User.fields_for_order_statement, :url) |
|
| 54 |
end |
|
| 55 | ||
| 51 | 56 |
# Loads the default configuration |
| 52 | 57 |
# (roles, trackers, statuses, workflow, enumerations) |
| 53 | 58 |
def default_configuration |
| app/controllers/webhooks_controller.rb | ||
|---|---|---|
| 20 | 20 |
class WebhooksController < ApplicationController |
| 21 | 21 |
self.main_menu = false |
| 22 | 22 | |
| 23 |
ADMIN_CUSTODY_ACTIONS = %w(edit update destroy).freeze |
|
| 24 | ||
| 23 | 25 |
before_action :require_login |
| 24 | 26 |
before_action :check_enabled |
| 25 | 27 |
before_action :authorize |
| ... | ... | |
| 28 | 30 | |
| 29 | 31 |
require_sudo_mode :create, :update, :destroy |
| 30 | 32 | |
| 33 |
helper_method :secret_hidden? |
|
| 34 | ||
| 31 | 35 |
def index |
| 32 | 36 |
@webhooks = webhooks.order(:url) |
| 33 | 37 |
end |
| ... | ... | |
| 42 | 46 |
def create |
| 43 | 47 |
@webhook = webhooks.build(webhook_params) |
| 44 | 48 |
if @webhook.save |
| 45 |
redirect_to webhooks_path
|
|
| 49 |
redirect_back_or_default webhooks_path
|
|
| 46 | 50 |
else |
| 47 | 51 |
render :new |
| 48 | 52 |
end |
| ... | ... | |
| 50 | 54 | |
| 51 | 55 |
def update |
| 52 | 56 |
if @webhook.update(webhook_params) |
| 53 |
redirect_to webhooks_path
|
|
| 57 |
redirect_back_or_default webhooks_path
|
|
| 54 | 58 |
else |
| 55 | 59 |
render :edit |
| 56 | 60 |
end |
| ... | ... | |
| 58 | 62 | |
| 59 | 63 |
def destroy |
| 60 | 64 |
@webhook.destroy |
| 61 |
redirect_to webhooks_path
|
|
| 65 |
redirect_back_or_default webhooks_path
|
|
| 62 | 66 |
end |
| 63 | 67 | |
| 64 | 68 |
private |
| 65 | 69 | |
| 70 |
# True when the stored secret must not be disclosed to the current user |
|
| 71 |
def secret_hidden? |
|
| 72 |
@webhook&.persisted? && @webhook.user != User.current |
|
| 73 |
end |
|
| 74 | ||
| 66 | 75 |
def webhook_params |
| 67 |
params.require(:webhook).permit(:url, :secret, :active, events: [], project_ids: []) |
|
| 76 |
attrs = params.require(:webhook).permit(:url, :secret, :active, events: [], project_ids: []) |
|
| 77 |
attrs.delete(:secret) if secret_hidden? && attrs[:secret].blank? |
|
| 78 |
attrs |
|
| 68 | 79 |
end |
| 69 | 80 | |
| 70 | 81 |
def find_webhook |
| 71 |
@webhook = webhooks.find(params[:id]) |
|
| 82 |
@webhook = editable_webhooks.find(params[:id])
|
|
| 72 | 83 |
rescue ActiveRecord::RecordNotFound |
| 73 | 84 |
render_404 |
| 74 | 85 |
end |
| ... | ... | |
| 77 | 88 |
User.current.webhooks |
| 78 | 89 |
end |
| 79 | 90 | |
| 91 |
# Administrators may edit any webhook, without ever becoming its owner |
|
| 92 |
def editable_webhooks |
|
| 93 |
User.current.admin? ? Webhook.all : webhooks |
|
| 94 |
end |
|
| 95 | ||
| 80 | 96 |
def authorize |
| 81 | 97 |
deny_access unless User.current.allowed_to?(:use_webhooks, nil, global: true) |
| 82 | 98 |
end |
| 83 | 99 | |
| 84 | 100 |
def check_enabled |
| 85 |
render_403 unless Webhook.enabled? |
|
| 101 |
return if Webhook.enabled? |
|
| 102 |
return if User.current.admin? && ADMIN_CUSTODY_ACTIONS.include?(action_name) |
|
| 103 | ||
| 104 |
render_403 |
|
| 86 | 105 |
end |
| 87 | 106 |
end |
| app/models/webhook.rb | ||
|---|---|---|
| 133 | 133 | |
| 134 | 134 |
def setable_projects |
| 135 | 135 |
user = self.user || User.current |
| 136 |
Project.visible(user).to_a.select{|p| user.allowed_to?(:use_webhooks, p)}
|
|
| 136 |
Project.visible(user).allowed_to(user, :use_webhooks).to_a
|
|
| 137 | 137 |
end |
| 138 | 138 | |
| 139 | 139 |
def setable_events |
| app/views/admin/webhooks.html.erb | ||
|---|---|---|
| 1 |
<% if Webhook.enabled? %> |
|
| 2 |
<div class="contextual"> |
|
| 3 |
<%= link_to sprite_icon('add', l(:label_webhook_new)), new_webhook_path(:back_url => admin_webhooks_path), class: 'icon icon-add' %>
|
|
| 4 |
</div> |
|
| 5 |
<% end %> |
|
| 6 | ||
| 7 |
<%= title l(:label_webhook_plural) %> |
|
| 8 | ||
| 9 |
<% unless Webhook.enabled? %> |
|
| 10 |
<p class="warning"><%= t(:webhook_disabled_info_html, :link => link_to(l(:label_integrations), settings_path(:tab => 'integrations'))) %></p> |
|
| 11 |
<% end %> |
|
| 12 | ||
| 13 |
<%= render :partial => 'webhooks/list', :locals => { :webhooks => @webhooks, :show_author => true, :back_url => admin_webhooks_path } %>
|
|
| app/views/webhooks/_form.html.erb | ||
|---|---|---|
| 3 | 3 |
<div class="splitcontent"> |
| 4 | 4 |
<div class="splitcontentleft"> |
| 5 | 5 |
<div class="box tabular"> |
| 6 |
<% if secret_hidden? %> |
|
| 7 |
<p><label for="webhook_user"><%= l :field_user %></label><%= text_field_tag 'webhook_user', @webhook.user.name, disabled: true %></p> |
|
| 8 |
<% end %> |
|
| 6 | 9 |
<p><%= f.text_field :url, required: true, size: 60 %> |
| 7 | 10 |
<em class="info"><%= l :webhook_url_info %></em> |
| 8 | 11 |
</p> |
| 9 | 12 |
<p> |
| 10 |
<%= f.text_field :secret %> |
|
| 13 |
<%= f.text_field :secret, :value => (secret_hidden? ? '' : @webhook.secret) %>
|
|
| 11 | 14 |
<em class="info"><%= raw l :webhook_secret_info_html %></em> |
| 15 |
<% if secret_hidden? %><em class="info"><%= l :webhook_secret_keep_info %></em><% end %> |
|
| 12 | 16 |
</p> |
| 13 | 17 |
<p><%= f.check_box :active %></p> |
| 14 | 18 |
</div> |
| app/views/webhooks/_list.html.erb | ||
|---|---|---|
| 1 |
<% if webhooks.any? %> |
|
| 2 |
<div class="autoscroll"> |
|
| 3 |
<table class="list webhooks"> |
|
| 4 |
<thead><tr> |
|
| 5 |
<% if show_author %><th><%= l :field_user %></th><% end %> |
|
| 6 |
<th><%= l :field_active %></th> |
|
| 7 |
<th><%= l :field_url %></th> |
|
| 8 |
<th><%= l :label_webhook_events %></th> |
|
| 9 |
<th><%= l :label_project_plural %></th> |
|
| 10 |
<th></th> |
|
| 11 |
</tr></thead> |
|
| 12 |
<tbody> |
|
| 13 |
<% webhooks.each do |webhook| %> |
|
| 14 |
<% link_params = back_url.present? ? {:back_url => back_url} : {} %>
|
|
| 15 |
<tr id="webhook_<%= webhook.id %>" class="<%= cycle("odd", "even") %>">
|
|
| 16 |
<% if show_author %><td><%= link_to_user webhook.user %></td><% end %> |
|
| 17 |
<td><%= webhook.active ? l(:general_text_Yes) : l(:general_text_No) %></td> |
|
| 18 |
<td title="<%= webhook.url %>"><%= truncate webhook.url, length: 40 %></td> |
|
| 19 |
<td><%= safe_join webhook.events.map{|e| content_tag :code, e }, ', ' %></td>
|
|
| 20 |
<td><%= safe_join webhook.projects.visible.map{|p| link_to_project(p) }, ', ' %></td>
|
|
| 21 |
<td class="buttons"> |
|
| 22 |
<%= link_to sprite_icon('edit', l(:button_edit)), edit_webhook_path(webhook, link_params), class: 'icon icon-edit' %>
|
|
| 23 |
<%= link_to sprite_icon('del', l(:button_delete)), webhook_path(webhook, link_params), :data => {:confirm => l(:text_are_you_sure)}, :method => :delete, :class => 'icon icon-del' %>
|
|
| 24 |
</td> |
|
| 25 |
</tr> |
|
| 26 |
<% end %> |
|
| 27 |
</tbody> |
|
| 28 |
</table> |
|
| 29 |
</div> |
|
| 30 |
<% else %> |
|
| 31 |
<p class="nodata"><%= l(:label_no_data) %></p> |
|
| 32 |
<% end %> |
|
| app/views/webhooks/edit.html.erb | ||
|---|---|---|
| 2 | 2 | |
| 3 | 3 |
<%= labelled_form_for @webhook, html: { method: :patch } do |f| %>
|
| 4 | 4 |
<%= render :partial => 'form', :locals => { :f => f } %>
|
| 5 |
<%= back_url_hidden_field_tag %> |
|
| 5 | 6 |
<%= submit_tag l(:button_save) %> |
| 7 |
<%= cancel_button_tag webhooks_path %> |
|
| 6 | 8 |
<% end %> |
| app/views/webhooks/index.html.erb | ||
|---|---|---|
| 4 | 4 | |
| 5 | 5 |
<%= title l :label_webhook_plural %> |
| 6 | 6 | |
| 7 |
<% if @webhooks.any? %> |
|
| 8 |
<div class="autoscroll"> |
|
| 9 |
<table class="list"> |
|
| 10 |
<thead><tr> |
|
| 11 |
<th><%= l :field_active %></th> |
|
| 12 |
<th><%= l :field_url %></th> |
|
| 13 |
<th><%= l :label_webhook_events %></th> |
|
| 14 |
<th><%= l :label_project_plural %></th> |
|
| 15 |
<th></th> |
|
| 16 |
</tr></thead> |
|
| 17 |
<tbody> |
|
| 18 |
<% @webhooks.each do |webhook| %> |
|
| 19 |
<tr id="webhook_<%= webhook.id %>" class="<%= cycle("odd", "even") %>">
|
|
| 20 |
<td><%= webhook.active ? l(:general_text_Yes) : l(:general_text_No) %></td> |
|
| 21 |
<td><%= truncate webhook.url, length: 40 %></td> |
|
| 22 |
<td><%= safe_join webhook.events.map{|e| content_tag :code, e }, ', ' %></td>
|
|
| 23 |
<td><%= safe_join webhook.projects.visible.map{|p| link_to_project(p) }, ', ' %></td>
|
|
| 24 |
<td class="buttons"> |
|
| 25 |
<%= link_to sprite_icon('edit', l(:button_edit)), edit_webhook_path(webhook), class: 'icon icon-edit' %>
|
|
| 26 |
<%= link_to sprite_icon('del', l(:button_delete)), webhook_path(webhook), :data => {:confirm => l(:text_are_you_sure)}, :method => :delete, :class => 'icon icon-del' %>
|
|
| 27 |
</td> |
|
| 28 |
</tr> |
|
| 29 |
<% end %> |
|
| 30 |
</tbody> |
|
| 31 |
</table> |
|
| 32 |
</div> |
|
| 33 |
<% else %> |
|
| 34 |
<p class="nodata"><%= l(:label_no_data) %></p> |
|
| 35 |
<% end %> |
|
| 7 |
<%= render :partial => 'webhooks/list', :locals => { :webhooks => @webhooks, :show_author => false, :back_url => nil } %>
|
|
| app/views/webhooks/new.html.erb | ||
|---|---|---|
| 2 | 2 | |
| 3 | 3 |
<%= labelled_form_for @webhook, url: webhooks_path do |f| %> |
| 4 | 4 |
<%= render :partial => 'webhooks/form', locals: { f: f } %>
|
| 5 |
<%= back_url_hidden_field_tag %> |
|
| 5 | 6 |
<%= submit_tag l(:button_create) %> |
| 6 |
<%= link_to l(:button_cancel), webhooks_path %>
|
|
| 7 |
<%= cancel_button_tag webhooks_path %>
|
|
| 7 | 8 |
<% end %> |
| config/locales/en.yml | ||
|---|---|---|
| 1197 | 1197 |
webhook_event_deleted: "%{object_name} deleted"
|
| 1198 | 1198 |
webhook_url_info: Redmine will send a POST request to this URL whenever one of the selected events occurs in one of the selected projects. |
| 1199 | 1199 |
webhook_secret_info_html: If provided, Redmine will use this to create a hash signature that is sent with each delivery as the value of the X-Redmine-Signature-256 header. |
| 1200 |
webhook_secret_keep_info: The secret of another user is not displayed. Leave this field blank to keep it unchanged. |
|
| 1201 |
webhook_disabled_info_html: Webhooks are disabled, so none of the webhooks below is triggered. They are still listed here so that they can be reviewed or removed. Webhooks can be enabled in the %{link} settings.
|
|
| 1200 | 1202 | |
| 1201 | 1203 |
button_login: Login |
| 1202 | 1204 |
button_submit: Submit |
| config/locales/fr.yml | ||
|---|---|---|
| 1508 | 1508 |
selected events occurs in one of the selected projects. |
| 1509 | 1509 |
webhook_secret_info_html: If provided, Redmine will use this to create a hash signature |
| 1510 | 1510 |
that is sent with each delivery as the value of the X-Redmine-Signature-256 header. |
| 1511 |
webhook_secret_keep_info: "Le secret d'un autre utilisateur n'est pas affiché. Laisser |
|
| 1512 |
ce champ vide pour le conserver." |
|
| 1513 |
webhook_disabled_info_html: "Les webhooks sont désactivés : aucun des webhooks ci-dessous |
|
| 1514 |
n'est déclenché. Ils restent listés ici afin de pouvoir être examinés ou supprimés. |
|
| 1515 |
Les webhooks peuvent être activés dans la configuration %{link}."
|
|
| 1511 | 1516 |
setting_webhooks_enabled: Enable webhooks |
| 1512 | 1517 |
label_integrations: Integrations |
| 1513 | 1518 |
label_alert_note: Note |
| config/routes.rb | ||
|---|---|---|
| 380 | 380 |
get 'admin', :to => 'admin#index' |
| 381 | 381 |
get 'admin/projects', :to => 'admin#projects' |
| 382 | 382 |
get 'admin/plugins', :to => 'admin#plugins' |
| 383 |
get 'admin/webhooks', :to => 'admin#webhooks' |
|
| 383 | 384 |
get 'admin/info', :to => 'admin#info' |
| 384 | 385 |
post 'admin/test_email', :to => 'admin#test_email', :as => 'test_email' |
| 385 | 386 |
post 'admin/default_configuration', :to => 'admin#default_configuration' |
| lib/redmine/preparation.rb | ||
|---|---|---|
| 293 | 293 |
:caption => :'doorkeeper.layouts.admin.nav.applications', |
| 294 | 294 |
:icon => 'apps', |
| 295 | 295 |
:html => {:class => 'icon icon-applications'}
|
| 296 |
menu.push :webhooks, {:controller => 'admin', :action => 'webhooks'},
|
|
| 297 |
:caption => :label_webhook_plural, |
|
| 298 |
:icon => 'webhook', |
|
| 299 |
:html => {:class => 'icon icon-webhook'}
|
|
| 296 | 300 |
menu.push :plugins, {:controller => 'admin', :action => 'plugins'},
|
| 297 | 301 |
:last => true, |
| 298 | 302 |
:icon => 'plugins', |
| test/functional/admin_controller_test.rb | ||
|---|---|---|
| 168 | 168 |
end |
| 169 | 169 |
end |
| 170 | 170 | |
| 171 |
def test_webhooks |
|
| 172 |
hook = Webhook.create!(:url => 'https://example.com/dlopper/hook', :user => User.find_by_login('dlopper'),
|
|
| 173 |
:events => %w(issue.created), :projects => [Project.find(1)]) |
|
| 174 |
other_hook = Webhook.create!(:url => 'https://example.com/admin/hook', :user => User.find(1), |
|
| 175 |
:events => %w(issue.updated), :projects => [Project.find(1)]) |
|
| 176 | ||
| 177 |
with_settings :webhooks_enabled => '1' do |
|
| 178 |
get :webhooks |
|
| 179 |
end |
|
| 180 |
assert_response :success |
|
| 181 | ||
| 182 |
assert_select "tr#webhook_#{hook.id}" do
|
|
| 183 |
assert_select 'td', :text => hook.user.name |
|
| 184 |
assert_select 'td', :text => hook.url |
|
| 185 |
assert_select "td.buttons a[href=?]", "/webhooks/#{hook.id}/edit?back_url=%2Fadmin%2Fwebhooks"
|
|
| 186 |
end |
|
| 187 |
assert_select "tr#webhook_#{other_hook.id}"
|
|
| 188 |
assert_select 'div.contextual a[href=?]', '/webhooks/new?back_url=%2Fadmin%2Fwebhooks' |
|
| 189 |
end |
|
| 190 | ||
| 191 |
def test_webhooks_should_remain_accessible_without_creation_link_when_disabled |
|
| 192 |
Webhook.create!(:url => 'https://example.com/dlopper/hook', :user => User.find_by_login('dlopper'),
|
|
| 193 |
:events => %w(issue.created), :projects => [Project.find(1)]) |
|
| 194 | ||
| 195 |
with_settings :webhooks_enabled => '0' do |
|
| 196 |
get :webhooks |
|
| 197 |
assert_response :success |
|
| 198 |
assert_select 'table.webhooks tr[id^=?]', 'webhook_' |
|
| 199 |
assert_select 'div.contextual a', 0 |
|
| 200 |
assert_select 'p.warning a[href=?]', '/settings?tab=integrations' |
|
| 201 |
end |
|
| 202 |
end |
|
| 203 | ||
| 204 |
def test_webhooks_should_be_denied_to_non_admin_users |
|
| 205 |
@request.session[:user_id] = 2 |
|
| 206 |
get :webhooks |
|
| 207 |
assert_response :forbidden |
|
| 208 |
end |
|
| 209 | ||
| 171 | 210 |
def test_info |
| 172 | 211 |
get :info |
| 173 | 212 |
assert_response :success |
| test/functional/webhooks_controller_test.rb | ||
|---|---|---|
| 62 | 62 |
test "should get new" do |
| 63 | 63 |
get :new |
| 64 | 64 |
assert_response :success |
| 65 |
assert_select 'em.info', text: I18n.t(:webhook_secret_keep_info), count: 0 |
|
| 65 | 66 |
end |
| 66 | 67 | |
| 67 | 68 |
test "should create webhook" do |
| ... | ... | |
| 74 | 75 |
test "should get edit" do |
| 75 | 76 |
get :edit, params: { id: @hook.id }
|
| 76 | 77 |
assert_response :success |
| 77 | 78 |
end |
| 78 | 79 | |
| 79 | 80 |
test "should update webhook" do |
| ... | ... | |
| 87 | 97 |
assert_response :not_found |
| 88 | 98 |
end |
| 89 | 99 | |
| 100 |
test 'index should not list hooks of other users to admins' do |
|
| 101 |
admin_hook = @other_hook |
|
| 102 |
dlopper_hook = @hook |
|
| 103 |
login_as_admin |
|
| 104 |
get :index |
|
| 105 |
assert_response :success |
|
| 106 |
assert_select 'td', text: admin_hook.url |
|
| 107 |
assert_select 'td', text: dlopper_hook.url, count: 0 |
|
| 108 |
end |
|
| 109 | ||
| 110 |
test 'admin should edit hook of other user' do |
|
| 111 |
login_as_admin |
|
| 112 |
get :edit, params: { id: @hook.id }
|
|
| 113 |
assert_response :success |
|
| 114 |
assert_select 'input#webhook_user[disabled][value=?]', @dlopper.name |
|
| 115 |
end |
|
| 116 | ||
| 117 |
test 'edit should not show the owner of ones own hook' do |
|
| 118 |
get :edit, params: { id: @hook.id }
|
|
| 119 |
assert_response :success |
|
| 120 |
assert_select 'input#webhook_user', count: 0 |
|
| 121 |
end |
|
| 122 | ||
| 123 |
test 'admin should update hook of other user without becoming its owner' do |
|
| 124 |
login_as_admin |
|
| 125 |
patch :update, params: { id: @hook.id, webhook: { url: 'https://example.com/fixed/hook' } }
|
|
| 126 |
assert_redirected_to webhooks_path |
|
| 127 |
@hook.reload |
|
| 128 |
assert_equal 'https://example.com/fixed/hook', @hook.url |
|
| 129 |
assert_equal @dlopper, @hook.user |
|
| 130 |
end |
|
| 131 | ||
| 132 |
test 'admin should deactivate hook of other user' do |
|
| 133 |
@hook.update_column :active, true |
|
| 134 |
login_as_admin |
|
| 135 |
patch :update, params: { id: @hook.id, webhook: { active: '0' } }
|
|
| 136 |
assert_not @hook.reload.active |
|
| 137 |
end |
|
| 138 | ||
| 139 |
test 'admin should destroy hook of other user' do |
|
| 140 |
login_as_admin |
|
| 141 |
assert_difference 'Webhook.count', -1 do |
|
| 142 |
delete :destroy, params: { id: @hook.id }
|
|
| 143 |
end |
|
| 144 |
end |
|
| 145 | ||
| 146 |
test 'create should redirect to back_url' do |
|
| 147 |
post :create, params: { webhook: { url: 'https://example.com/new/hook', events: %w(issue.created), project_ids: [@project.id] }, back_url: '/admin/webhooks' }
|
|
| 148 |
assert_redirected_to '/admin/webhooks' |
|
| 149 |
end |
|
| 150 | ||
| 151 |
test 'update should redirect to back_url' do |
|
| 152 |
login_as_admin |
|
| 153 |
patch :update, params: { id: @hook.id, webhook: { url: 'https://example.com/fixed/hook' }, back_url: '/admin/webhooks' }
|
|
| 154 |
assert_redirected_to '/admin/webhooks' |
|
| 155 |
end |
|
| 156 | ||
| 157 |
test 'edit should not disclose secret of other user' do |
|
| 158 |
@hook.update_column :secret, 'v3rys3cret' |
|
| 159 |
login_as_admin |
|
| 160 |
get :edit, params: { id: @hook.id }
|
|
| 161 |
assert_response :success |
|
| 162 |
assert_select 'input#webhook_secret' |
|
| 163 |
assert_not_include 'v3rys3cret', response.body |
|
| 164 |
end |
|
| 165 | ||
| 166 |
test 'update should keep secret of other user when submitted blank' do |
|
| 167 |
@hook.update_column :secret, 'v3rys3cret' |
|
| 168 |
login_as_admin |
|
| 169 |
patch :update, params: { id: @hook.id, webhook: { url: @hook.url, secret: '' } }
|
|
| 170 |
assert_equal 'v3rys3cret', @hook.reload.secret |
|
| 171 |
end |
|
| 172 | ||
| 173 |
test 'update should replace secret of other user when a new one is submitted' do |
|
| 174 |
@hook.update_column :secret, 'v3rys3cret' |
|
| 175 |
login_as_admin |
|
| 176 |
patch :update, params: { id: @hook.id, webhook: { url: @hook.url, secret: 'newsecret' } }
|
|
| 177 |
assert_equal 'newsecret', @hook.reload.secret |
|
| 178 |
end |
|
| 179 | ||
| 180 |
test 'owner should see and be able to clear their own secret' do |
|
| 181 |
@hook.update_column :secret, 'v3rys3cret' |
|
| 182 |
get :edit, params: { id: @hook.id }
|
|
| 183 |
assert_select 'input#webhook_secret[value=?]', 'v3rys3cret' |
|
| 184 | ||
| 185 |
patch :update, params: { id: @hook.id, webhook: { url: @hook.url, secret: '' } }
|
|
| 186 |
assert_equal '', @hook.reload.secret |
|
| 187 |
end |
|
| 188 | ||
| 189 |
test 'admin should keep access to existing hooks when disabled' do |
|
| 190 |
login_as_admin |
|
| 191 |
with_settings webhooks_enabled: '0' do |
|
| 192 |
get :edit, params: { id: @hook.id }
|
|
| 193 |
assert_response :success |
|
| 194 | ||
| 195 |
patch :update, params: { id: @hook.id, webhook: { url: 'https://example.com/fixed/hook' } }
|
|
| 196 |
assert_redirected_to webhooks_path |
|
| 197 | ||
| 198 |
assert_difference 'Webhook.count', -1 do |
|
| 199 |
delete :destroy, params: { id: @hook.id }
|
|
| 200 |
end |
|
| 201 |
end |
|
| 202 |
end |
|
| 203 | ||
| 204 |
test 'admin should not create hooks when disabled' do |
|
| 205 |
login_as_admin |
|
| 206 |
with_settings webhooks_enabled: '0' do |
|
| 207 |
get :index |
|
| 208 |
assert_response :forbidden |
|
| 209 | ||
| 210 |
get :new |
|
| 211 |
assert_response :forbidden |
|
| 212 |
end |
|
| 213 |
end |
|
| 214 | ||
| 90 | 215 |
private |
| 91 | 216 | |
| 217 |
def login_as_admin |
|
| 218 |
@request.session[:user_id] = User.find_by_login('admin').id
|
|
| 219 |
end |
|
| 220 | ||
| 92 | 221 |
def create_hook(url: 'https://example.com/some/hook', |
| 93 | 222 |
user: User.find_by_login('dlopper'),
|
| 94 | 223 |
events: %w(issue.created issue.updated), |
| test/integration/routing/admin_test.rb | ||
|---|---|---|
| 24 | 24 |
should_route 'GET /admin' => 'admin#index' |
| 25 | 25 |
should_route 'GET /admin/projects' => 'admin#projects' |
| 26 | 26 |
should_route 'GET /admin/plugins' => 'admin#plugins' |
| 27 |
should_route 'GET /admin/webhooks' => 'admin#webhooks' |
|
| 27 | 28 |
should_route 'GET /admin/info' => 'admin#info' |
| 28 | 29 |
should_route 'POST /admin/test_email' => 'admin#test_email' |
| 29 | 30 |
should_route 'POST /admin/default_configuration' => 'admin#default_configuration' |