diff --git a/app/controllers/emails_controller.rb b/app/controllers/emails_controller.rb index 37b385e48..2eb034ecf 100644 --- a/app/controllers/emails_controller.rb +++ b/app/controllers/emails_controller.rb @@ -1,8 +1,19 @@ class EmailsController < ApplicationController respond_to :html, :xml, :json + def index + @email_addresses = authorize EmailAddress.visible(CurrentUser.user).paginated_search(params, count_pages: true) + @email_addresses = @email_addresses.includes(:user) + respond_with(@email_addresses) + end + def show - @email_address = authorize EmailAddress.find_by_user_id!(params[:user_id]) + if params[:user_id] + @email_address = authorize EmailAddress.find_by_user_id!(params[:user_id]) + else + @email_address = authorize EmailAddress.find(params[:id]) + end + respond_with(@email_address) end diff --git a/app/models/email_address.rb b/app/models/email_address.rb index 367af7a97..9d4dd6f22 100644 --- a/app/models/email_address.rb +++ b/app/models/email_address.rb @@ -10,6 +10,14 @@ class EmailAddress < ApplicationRecord validate :validate_deliverable, on: :deliverable after_save :update_user + def self.visible(user) + if user.is_moderator? + where(user: User.where("level < ?", user.level)).or(where(user: user)) + else + none + end + end + def address=(value) self.normalized_address = EmailValidator.normalize(value) || address super @@ -29,6 +37,15 @@ class EmailAddress < ApplicationRecord user.update!(is_verified: is_verified? && nondisposable?) end + def self.search(params) + q = super + + q = q.search_attributes(params, :user, :address, :normalized_address, :is_verified, :is_deliverable) + q = q.apply_default_order(params) + + q + end + concerning :VerificationMethods do def verifier @verifier ||= Danbooru::MessageVerifier.new(:email_verification_key) diff --git a/app/policies/email_address_policy.rb b/app/policies/email_address_policy.rb index 152469125..f9ddcdfdb 100644 --- a/app/policies/email_address_policy.rb +++ b/app/policies/email_address_policy.rb @@ -1,4 +1,8 @@ class EmailAddressPolicy < ApplicationPolicy + def index? + user.is_moderator? + end + def show? record.user_id == user.id || (user.is_moderator? && record.user.level < user.level) end diff --git a/app/views/emails/index.html.erb b/app/views/emails/index.html.erb new file mode 100644 index 000000000..c1f7429ac --- /dev/null +++ b/app/views/emails/index.html.erb @@ -0,0 +1,32 @@ +
+
+ <%= search_form_for(emails_path) do |f| %> + <%= f.simple_fields_for :user do |fa| %> + <%= fa.input :name, label: "User Name", input_html: { value: params.dig(:search, :user, :name), "data-autocomplete": "user" } %> + <% end %> + + <%= f.input :address_ilike, label: "Address", input_html: { value: params[:search][:address] }, hint: "Use * for wildcard" %> + <%= f.input :is_verified, label: "Verified?", collection: %w[Yes No], selected: params[:search][:is_verified] %> + <%= f.submit "Search" %> + <% end %> + + <%= table_for @email_addresses, class: "striped autofit" do |t| %> + <% t.column :user do |email| %> + <%= link_to_user email.user %> + <% end %> + <% t.column :address %> + <% t.column :is_verified, name: "Verified?" do |email| %> + <% if email.is_verified? %> + <%= link_to "Yes", emails_path(search: { is_verified: true }) %> + <% else %> + <%= link_to "No", emails_path(search: { is_verified: false }) %> + <% end %> + <% end %> + <% t.column :updated_at, name: "Updated" do |email| %> + <%= time_ago_in_words_tagged(email.updated_at) %> + <% end %> + <% end %> + + <%= numbered_paginator(@email_addresses) %> +
+
diff --git a/app/views/static/site_map.html.erb b/app/views/static/site_map.html.erb index b88b8c138..4e82675d2 100644 --- a/app/views/static/site_map.html.erb +++ b/app/views/static/site_map.html.erb @@ -154,6 +154,10 @@
  • <%= link_to("Moderation Reports", moderation_reports_path) %>
  • <% end %> + <% if policy(EmailAddress).index? %> +
  • <%= link_to("Email Addresses", emails_path) %>
  • + <% end %> + <% if policy(IpAddress).index? %>
  • <%= link_to("IP Addresses", ip_addresses_path) %>
  • <% end %> diff --git a/config/routes.rb b/config/routes.rb index ce3a273c1..11d7e7d28 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -103,6 +103,7 @@ Rails.application.routes.draw do end resource :dtext_preview, :only => [:create] resources :dtext_links, only: [:index] + resources :emails, only: [:index, :show] resources :favorites, :only => [:index, :create, :destroy] resources :favorite_groups do member do diff --git a/test/functional/emails_controller_test.rb b/test/functional/emails_controller_test.rb index ec08a2c03..4da9bb9a5 100644 --- a/test/functional/emails_controller_test.rb +++ b/test/functional/emails_controller_test.rb @@ -10,6 +10,26 @@ class EmailsControllerTest < ActionDispatch::IntegrationTest @restricted_user = create(:user, requires_verification: true, is_verified: false) end + context "#index" do + should "not let regular users see emails belonging to other users" do + get_auth emails_path, @user + assert_response 403 + end + + should "let mods see emails belonging to themselves and all users below mod level" do + @mod1 = create(:moderator_user, email_address: build(:email_address)) + @mod2 = create(:moderator_user, email_address: build(:email_address)) + + get_auth emails_path, @mod1 + + assert_response :success + assert_select "#email-address-#{@user.email_address.id}", count: 1 + assert_select "#email-address-#{@other_user.email_address.id}", count: 1 + assert_select "#email-address-#{@mod1.email_address.id}", count: 1 + assert_select "#email-address-#{@mod2.email_address.id}", count: 0 + end + end + context "#show" do should "render" do get_auth user_email_path(@user), @user, as: :json