Allow trusted user to access response templates (#17978)
* Allow trusted user to access response templates * Add request specs for trusted user templates * WIP: was just investigating... * Move permission check to authorizer * Revert "WIP: was just investigating..." This reverts commit c38c60f7ab19ce43c174729c2542a9041e9322e2. * Improved accessibility Co-authored-by: Suzanne Aitchison <suzanne@forem.com> Co-authored-by: Suzanne Aitchison <suzanne@forem.com>
This commit is contained in:
parent
10c44013be
commit
5dee4a11c1
9 changed files with 223 additions and 79 deletions
|
|
@ -31,9 +31,12 @@ class ResponseTemplatesController < ApplicationController
|
|||
|
||||
def create
|
||||
authorize ResponseTemplate
|
||||
response_template.user_id = current_user.id
|
||||
|
||||
unless tries_to_create_a_mod_response_template? && can_create_mod_response_templates?
|
||||
response_template.user_id = current_user.id
|
||||
response_template.type_of = "personal_comment"
|
||||
end
|
||||
response_template.content_type = "body_markdown"
|
||||
response_template.type_of = "personal_comment"
|
||||
|
||||
if response_template.save
|
||||
flash[:settings_notice] =
|
||||
|
|
@ -87,6 +90,18 @@ class ResponseTemplatesController < ApplicationController
|
|||
|
||||
private
|
||||
|
||||
def authorized_user
|
||||
@authorized_user ||= Authorizer.for(user: current_user)
|
||||
end
|
||||
|
||||
def can_create_mod_response_templates?
|
||||
authorized_user.accesses_mod_response_templates?
|
||||
end
|
||||
|
||||
def tries_to_create_a_mod_response_template?
|
||||
params[:response_template][:type_of] == "mod_comment"
|
||||
end
|
||||
|
||||
def response_template
|
||||
@response_template ||= if params[:id].present?
|
||||
ResponseTemplate.find(params[:id])
|
||||
|
|
|
|||
|
|
@ -218,8 +218,6 @@ class UsersController < ApplicationController
|
|||
def signout_confirm; end
|
||||
|
||||
def handle_settings_tab
|
||||
return @tab = "profile" if @tab.blank?
|
||||
|
||||
case @tab
|
||||
when "profile"
|
||||
handle_integrations_tab
|
||||
|
|
@ -302,8 +300,10 @@ class UsersController < ApplicationController
|
|||
end
|
||||
|
||||
def handle_response_templates_tab
|
||||
@response_templates = current_user.response_templates
|
||||
@response_template = ResponseTemplate.find_or_initialize_by(id: params[:id], user: current_user)
|
||||
@personal_response_templates = current_user.response_templates
|
||||
@trusted_response_templates = policy_scope(ResponseTemplate).where(type_of: "mod_comment")
|
||||
@response_template = policy_scope(ResponseTemplate).find_by(id: params[:id]) ||
|
||||
ResponseTemplate.new
|
||||
end
|
||||
|
||||
def set_user
|
||||
|
|
|
|||
|
|
@ -75,6 +75,10 @@ module Authorizer
|
|||
has_role?(:creator)
|
||||
end
|
||||
|
||||
def accesses_mod_response_templates?
|
||||
has_trusted_role? || any_admin? || moderator? || tag_moderator?
|
||||
end
|
||||
|
||||
# When you need to know if we trust the user, but don't want to
|
||||
# have stale information that the `trusted?` method might give
|
||||
# you.
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ class ResponseTemplatePolicy < ApplicationPolicy
|
|||
|
||||
class Scope < Scope
|
||||
def resolve
|
||||
if user.has_trusted_role? || user.any_admin? || user.moderator? || user.tag_moderator?
|
||||
scope.where(user: user, type_of: "personal_comment") + scope.where.not(type_of: "personal_comment")
|
||||
if Authorizer.for(user: user).accesses_mod_response_templates?
|
||||
scope.where(user: user, type_of: "personal_comment").or(scope.where.not(type_of: "personal_comment"))
|
||||
else
|
||||
scope.where(user: user, type_of: "personal_comment")
|
||||
end
|
||||
|
|
@ -30,14 +30,21 @@ class ResponseTemplatePolicy < ApplicationPolicy
|
|||
user_moderator? && mod_comment?
|
||||
end
|
||||
|
||||
def destroy?
|
||||
def modify?
|
||||
return true if mod_comment? && user_trusted?
|
||||
|
||||
user_owner?
|
||||
end
|
||||
|
||||
alias update? destroy?
|
||||
alias update? modify?
|
||||
alias destroy? modify?
|
||||
|
||||
def permitted_attributes_for_create
|
||||
PERMITTED_ATTRIBUTES
|
||||
if user_trusted?
|
||||
PERMITTED_ATTRIBUTES + [:type_of]
|
||||
else
|
||||
PERMITTED_ATTRIBUTES
|
||||
end
|
||||
end
|
||||
|
||||
def permitted_attributes_for_update
|
||||
|
|
@ -50,6 +57,10 @@ class ResponseTemplatePolicy < ApplicationPolicy
|
|||
user.id == record.user_id
|
||||
end
|
||||
|
||||
def user_trusted?
|
||||
Authorizer.for(user: user).accesses_mod_response_templates?
|
||||
end
|
||||
|
||||
def user_moderator?
|
||||
user_any_admin? || user.moderator_for_tags&.present?
|
||||
end
|
||||
|
|
|
|||
13
app/views/users/_response_template.html.erb
Normal file
13
app/views/users/_response_template.html.erb
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<div class="<%= "github-repo-row-featured" if response_template.id == params[:id].to_i %> mb-4">
|
||||
<div class="flex items-center py-2 mt-2">
|
||||
<div class="flex-1 ff-monospace">
|
||||
<%= response_template.title %>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<%= form_with url: response_template_path(response_template.id), method: :delete, local: true, html: { onsubmit: "return confirm('#{t('views.settings.extensions.comment.confirm', template: response_template.title)}');" } do %>
|
||||
<button type="submit" class="crayons-btn crayons-btn--secondary"><%= t("views.settings.extensions.comment.remove") %></button>
|
||||
<% end %>
|
||||
<a class="crayons-btn crayons-btn--secondary ml-2" href="/settings/response-templates/<%= response_template.id %>" role="button"><%= t("views.settings.extensions.comment.edit") %></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -7,28 +7,23 @@
|
|||
</p>
|
||||
</header>
|
||||
|
||||
<% if @response_templates.present? %>
|
||||
<% if @personal_response_templates.present? %>
|
||||
<section>
|
||||
<h3 class="crayons-subtitle-3">
|
||||
<%= t("views.settings.extensions.comment.saved") %>
|
||||
</h3>
|
||||
<% @response_templates.each do |response_template| %>
|
||||
<div class="<%= "github-repo-row-featured" if response_template.id == params[:id].to_i %> mb-4">
|
||||
<div class="flex items-center py-2 mt-2">
|
||||
<div class="flex-1 ff-monospace">
|
||||
<%= response_template.title %>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<%= form_with url: response_template_path(response_template.id), method: :delete, local: true, html: { onsubmit: "return confirm('#{t('views.settings.extensions.comment.confirm', template: response_template.title)}');" } do %>
|
||||
<button type="submit" class="crayons-btn crayons-btn--secondary"><%= t("views.settings.extensions.comment.remove") %></button>
|
||||
<% end %>
|
||||
<a class="crayons-btn crayons-btn--secondary ml-2" href="/settings/response-templates/<%= response_template.id %>" role="button"><%= t("views.settings.extensions.comment.edit") %></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</section>
|
||||
<% end %>
|
||||
<%= render partial: "response_template", collection: @personal_response_templates %>
|
||||
</section>
|
||||
<% end %>
|
||||
|
||||
<% if @trusted_response_templates.present? %>
|
||||
<section>
|
||||
<h3 class="crayons-subtitle-3">
|
||||
<%= t("views.settings.extensions.comment.trusted") %>
|
||||
</h3>
|
||||
<%= render partial: "response_template", collection: @trusted_response_templates %>
|
||||
</section>
|
||||
<% end %>
|
||||
|
||||
<%= form_with model: @response_template do |f| %>
|
||||
<section class="grid gap-3">
|
||||
|
|
@ -60,6 +55,22 @@
|
|||
<%= f.text_area :content, value: content, class: "crayons-textfield" %>
|
||||
</div>
|
||||
|
||||
<% if current_user.has_trusted_role? && @response_template.new_record? %>
|
||||
<div class="crayons-field">
|
||||
<fieldset>
|
||||
<legend class="screen-reader-only">Template type</legend>
|
||||
<%= f.radio_button :type_of, "personal_comment",
|
||||
checked: @response_template.type_of != "mod_comment",
|
||||
class: "crayons-radio", role: "radio" %>
|
||||
<%= label_tag "response_template_type_of_personal_comment", "Personal" %>
|
||||
|
||||
<%= f.radio_button :type_of, "mod_comment",
|
||||
class: "crayons-radio", role: "radio" %>
|
||||
<%= label_tag "response_template_type_of_mod_comment", "Trusted User" %>
|
||||
</fieldset>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div>
|
||||
<button type="submit" class="crayons-btn"><%= t("views.settings.extensions.comment.save") %></button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -130,6 +130,7 @@ en:
|
|||
remove: Remove
|
||||
save: Save template
|
||||
saved: Saved templates
|
||||
trusted: Trusted user templates
|
||||
github:
|
||||
heading: GitHub
|
||||
desc1: Pin your GitHub repositories to your profile.
|
||||
|
|
|
|||
|
|
@ -39,15 +39,14 @@ RSpec.describe ResponseTemplatePolicy, type: :policy do
|
|||
let(:user) { create(:user, :tag_moderator) }
|
||||
let(:response_template) { create(:response_template, type_of: "mod_comment", user: nil) }
|
||||
|
||||
it { is_expected.to permit_actions(%i[moderator_index create moderator_create]) }
|
||||
it { is_expected.to forbid_actions(%i[update destroy admin_index]) }
|
||||
it { is_expected.to permit_actions(%i[moderator_index create moderator_create update destroy]) }
|
||||
it { is_expected.to forbid_actions(%i[admin_index]) }
|
||||
end
|
||||
|
||||
context "when user is an admin" do
|
||||
let(:user) { create(:user, :admin) }
|
||||
let(:response_template) { create(:response_template, type_of: "mod_comment", user: nil) }
|
||||
|
||||
it { is_expected.to permit_actions(%i[moderator_index create moderator_create admin_index]) }
|
||||
it { is_expected.to forbid_actions(%i[update destroy]) }
|
||||
it { is_expected.to permit_actions(%i[moderator_index create moderator_create admin_index update destroy]) }
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ require "rails_helper"
|
|||
|
||||
RSpec.describe "ResponseTemplate", type: :request do
|
||||
let(:user) { create(:user) }
|
||||
let(:trusted_user) { create(:user, :trusted) }
|
||||
let(:moderator) { create(:user, :tag_moderator) }
|
||||
let(:admin) { create(:user, :admin) }
|
||||
|
||||
|
|
@ -30,7 +31,6 @@ RSpec.describe "ResponseTemplate", type: :request do
|
|||
|
||||
headers = { HTTP_ACCEPT: "application/json" }
|
||||
get response_templates_path, params: { type_of: "personal_comment" }, headers: headers
|
||||
|
||||
expect(response.parsed_body.class).to eq Array
|
||||
expect(response.parsed_body.length).to eq total_response_templates
|
||||
end
|
||||
|
|
@ -120,65 +120,155 @@ RSpec.describe "ResponseTemplate", type: :request do
|
|||
end
|
||||
|
||||
describe "POST /response_templates #create" do
|
||||
before { sign_in user }
|
||||
context "when signed in as normal user" do
|
||||
before { sign_in user }
|
||||
|
||||
let(:attributes) do
|
||||
{
|
||||
title: "some_title",
|
||||
content: "some content",
|
||||
type_of: "personal_comment"
|
||||
}
|
||||
let(:attributes) do
|
||||
{
|
||||
title: "some_title",
|
||||
content: "some content",
|
||||
type_of: "personal_comment"
|
||||
}
|
||||
end
|
||||
|
||||
it "successfully creates the proper response template" do
|
||||
post response_templates_path, params: {
|
||||
response_template: {
|
||||
title: attributes[:title],
|
||||
content: attributes[:content]
|
||||
}
|
||||
}
|
||||
|
||||
response_template = ResponseTemplate.last
|
||||
expect(response_template.user_id).to eq user.id
|
||||
expect(response_template.title).to eq attributes[:title]
|
||||
expect(response_template.content).to eq attributes[:content]
|
||||
expect(response_template.type_of).to eq attributes[:type_of]
|
||||
end
|
||||
|
||||
it "can only create personal response templates" do
|
||||
post response_templates_path, params: {
|
||||
response_template: {
|
||||
title: attributes[:title],
|
||||
content: attributes[:content],
|
||||
type_of: "mod_comment"
|
||||
}
|
||||
}
|
||||
|
||||
response_template = ResponseTemplate.last
|
||||
expect(response_template.type_of).to eq "personal_comment"
|
||||
end
|
||||
|
||||
it "redirects to the edit page upon success" do
|
||||
post response_templates_path, params: {
|
||||
response_template: {
|
||||
title: attributes[:title],
|
||||
content: attributes[:content]
|
||||
}
|
||||
}
|
||||
expect(response.redirect_url).to include user_settings_path(tab: "response-templates",
|
||||
id: ResponseTemplate.last.id)
|
||||
end
|
||||
end
|
||||
|
||||
it "successfully creates the proper response template" do
|
||||
post response_templates_path, params: {
|
||||
response_template: {
|
||||
title: attributes[:title],
|
||||
content: attributes[:content]
|
||||
}
|
||||
}
|
||||
context "when signed in as trusted user" do
|
||||
before { sign_in trusted_user }
|
||||
|
||||
response_template = ResponseTemplate.last
|
||||
expect(response_template.user_id).to eq user.id
|
||||
expect(response_template.title).to eq attributes[:title]
|
||||
expect(response_template.content).to eq attributes[:content]
|
||||
expect(response_template.type_of).to eq attributes[:type_of]
|
||||
end
|
||||
|
||||
it "redirects to the edit page upon success" do
|
||||
post response_templates_path, params: {
|
||||
response_template: {
|
||||
title: attributes[:title],
|
||||
content: attributes[:content]
|
||||
let(:attributes) do
|
||||
{
|
||||
title: "some_title",
|
||||
content: "some content",
|
||||
type_of: "mod_comment"
|
||||
}
|
||||
}
|
||||
expect(response.redirect_url).to include user_settings_path(tab: "response-templates",
|
||||
id: ResponseTemplate.last.id)
|
||||
end
|
||||
|
||||
it "successfully creates a personal response template" do
|
||||
post response_templates_path, params: {
|
||||
response_template: {
|
||||
title: attributes[:title],
|
||||
content: attributes[:content]
|
||||
}
|
||||
}
|
||||
|
||||
response_template = ResponseTemplate.last
|
||||
expect(response_template.user_id).to eq trusted_user.id
|
||||
expect(response_template.title).to eq attributes[:title]
|
||||
expect(response_template.content).to eq attributes[:content]
|
||||
expect(response_template.type_of).to eq "personal_comment"
|
||||
end
|
||||
|
||||
it "successfully creates a mod_comment response template" do
|
||||
post response_templates_path, params: {
|
||||
response_template: {
|
||||
title: attributes[:title],
|
||||
content: attributes[:content],
|
||||
type_of: attributes[:type_of]
|
||||
}
|
||||
}
|
||||
|
||||
response_template = ResponseTemplate.last
|
||||
expect(response_template.user_id).to be_nil
|
||||
expect(response_template.title).to eq attributes[:title]
|
||||
expect(response_template.content).to eq attributes[:content]
|
||||
expect(response_template.type_of).to eq "mod_comment"
|
||||
end
|
||||
|
||||
it "redirects to the edit page upon success" do
|
||||
post response_templates_path, params: {
|
||||
response_template: {
|
||||
title: attributes[:title],
|
||||
content: attributes[:content]
|
||||
}
|
||||
}
|
||||
expect(response.redirect_url).to include user_settings_path(tab: "response-templates",
|
||||
id: ResponseTemplate.last.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "PATCH /response_templates/:id #update" do
|
||||
before { sign_in user }
|
||||
context "when signed-in as normal user updating a personal template" do
|
||||
before { sign_in user }
|
||||
|
||||
let(:response_template) { create(:response_template, user: user) }
|
||||
let(:response_template) { create(:response_template, user: user) }
|
||||
|
||||
it "successfully updates the response template" do
|
||||
title = "something else"
|
||||
patch response_template_path(response_template.id), params: { response_template: { title: title } }
|
||||
expect(ResponseTemplate.first.title).to eq title
|
||||
it "successfully updates the response template" do
|
||||
title = "something else"
|
||||
patch response_template_path(response_template.id), params: { response_template: { title: title } }
|
||||
expect(ResponseTemplate.first.title).to eq title
|
||||
end
|
||||
|
||||
it "redirects back to the response template" do
|
||||
patch response_template_path(response_template.id), params: { response_template: { title: "something else" } }
|
||||
expect(response.redirect_url).to include user_settings_path(tab: "response-templates",
|
||||
id: ResponseTemplate.first.id)
|
||||
end
|
||||
|
||||
it "shows the previously written content on a failed submission" do
|
||||
content = "something something something"
|
||||
patch response_template_path(response_template.id),
|
||||
params: { response_template: { title: "", content: content } }
|
||||
follow_redirect!
|
||||
expect(response.body).to include content
|
||||
end
|
||||
end
|
||||
|
||||
it "redirects back to the response template" do
|
||||
patch response_template_path(response_template.id), params: { response_template: { title: "something else" } }
|
||||
expect(response.redirect_url).to include user_settings_path(tab: "response-templates",
|
||||
id: ResponseTemplate.first.id)
|
||||
end
|
||||
context "when signed-in as trusted user updating a mod_comment template" do
|
||||
before { sign_in trusted_user }
|
||||
|
||||
it "shows the previously written content on a failed submission" do
|
||||
content = "something something something"
|
||||
patch response_template_path(response_template.id), params: { response_template: { title: "", content: content } }
|
||||
follow_redirect!
|
||||
expect(response.body).to include content
|
||||
let(:response_template) { create :response_template, user: nil, type_of: "mod_comment" }
|
||||
|
||||
it "successfully updates the response template" do
|
||||
title = "something else"
|
||||
patch response_template_path(response_template.id), params: { response_template: { title: title } }
|
||||
expect(ResponseTemplate.first.title).to eq title
|
||||
end
|
||||
|
||||
it "does not permit changing template type_of" do
|
||||
patch response_template_path(response_template.id),
|
||||
params: { response_template: { type_of: "personal_comment" } }
|
||||
expect(ResponseTemplate.first.type_of).to eq("mod_comment")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue