From 92f1c3c1bdeb3832fe5a0255fca2ceaacf927c4d Mon Sep 17 00:00:00 2001 From: James Smith Date: Wed, 20 Nov 2024 13:49:46 +0000 Subject: [PATCH 1/6] add user list for moderation --- app/controllers/settings/users_controller.rb | 16 +++++++++++ app/helpers/settings_helper.rb | 3 +++ app/policies/federails/actor_policy.rb | 27 +++++++++++++++++++ app/views/layouts/settings.html.erb | 9 ++++--- app/views/settings/users/index.html.erb | 26 ++++++++++++++++++ config/locales/settings/en.yml | 3 +++ config/routes.rb | 6 +++++ spec/requests/settings/users_spec.rb | 11 ++++++++ spec/routing/moderation/users_routing_spec.rb | 9 +++++++ 9 files changed, 107 insertions(+), 3 deletions(-) create mode 100644 app/controllers/settings/users_controller.rb create mode 100644 app/policies/federails/actor_policy.rb create mode 100644 app/views/settings/users/index.html.erb create mode 100644 spec/requests/settings/users_spec.rb create mode 100644 spec/routing/moderation/users_routing_spec.rb diff --git a/app/controllers/settings/users_controller.rb b/app/controllers/settings/users_controller.rb new file mode 100644 index 000000000..7ed1f0546 --- /dev/null +++ b/app/controllers/settings/users_controller.rb @@ -0,0 +1,16 @@ +class Settings::UsersController < ApplicationController + def index + @users = policy_scope(Federails::Actor).where(entity_type: "User") + render layout: "settings" + end + + private + + def get_user + policy_scope(User).find_param(:id) + end + + def user_params + params.require(:user).permit + end +end diff --git a/app/helpers/settings_helper.rb b/app/helpers/settings_helper.rb index ffbedbaf4..f21985b15 100644 --- a/app/helpers/settings_helper.rb +++ b/app/helpers/settings_helper.rb @@ -1,2 +1,5 @@ module SettingsHelper + def masked_email(email) + email.gsub(/(?<=^.)[^@]*|(?<=@.).*(?=\.[^.]+$)/, "****") + end end diff --git a/app/policies/federails/actor_policy.rb b/app/policies/federails/actor_policy.rb new file mode 100644 index 000000000..ded3777b1 --- /dev/null +++ b/app/policies/federails/actor_policy.rb @@ -0,0 +1,27 @@ +class Federails::ActorPolicy < ApplicationPolicy + def index? + all_of( + SiteSettings.multiuser_enabled?, + @user.is_moderator? + ) + end + + def show? + index? + end + + def edit? + index? + end + + def update? + index? + end + + def destroy? + index? + end + + class Scope < ApplicationPolicy::Scope + end +end diff --git a/app/views/layouts/settings.html.erb b/app/views/layouts/settings.html.erb index 3bcde304b..c6a441770 100644 --- a/app/views/layouts/settings.html.erb +++ b/app/views/layouts/settings.html.erb @@ -9,13 +9,16 @@ <%= link_to t(".libraries"), settings_libraries_path, class: "nav-link" %> + <% unless SiteSettings.demo_mode_enabled? %> diff --git a/app/views/settings/users/index.html.erb b/app/views/settings/users/index.html.erb new file mode 100644 index 000000000..733a31627 --- /dev/null +++ b/app/views/settings/users/index.html.erb @@ -0,0 +1,26 @@ +

<%= t(".title") %>

+ +

<%= t(".description") %>

+ + + + + + <%= content_tag :th, t(".oidc") if SiteSettings.oidc_enabled? %> + <%= content_tag :th, t(".local") if SiteSettings.federation_enabled? %> + <%= content_tag :th, t(".fediverse_address") if SiteSettings.federation_enabled? %> + + + + <% @users.each do |user| %> + + + + <%= content_tag :td, (user.auth_uid? ? "✅" : "❌") if SiteSettings.oidc_enabled? %> + <%= content_tag :td, (user.local? ? "✅" : "❌") if SiteSettings.federation_enabled? %> + <%= content_tag :td, user.at_address if SiteSettings.federation_enabled? %> + + + + <% end %> +
<%= User.human_attribute_name(:username) %><%= User.human_attribute_name(:email) %><%= User.human_attribute_name(:roles) %>
<%= user.entity.is_a?(User) ? user.entity.username : user.username %><%= user.entity.is_a?(User) ? masked_email(user.entity.email) : nil %><%= user.entity.roles.map(&:name).join(", ") if user.entity.is_a?(User) %><%= link_to safe_join([icon("pencil", t("general.edit")), t("general.edit")], " "), settings_user_path(user), class: "btn btn-primary" %>
diff --git a/config/locales/settings/en.yml b/config/locales/settings/en.yml index 5bf20f4a1..dde7eb992 100644 --- a/config/locales/settings/en.yml +++ b/config/locales/settings/en.yml @@ -68,3 +68,6 @@ en: label: Create tags from model directory name update: success: Settings saved. + users: + index: + title: Manage Users diff --git a/config/routes.rb b/config/routes.rb index fce930f9e..e440a2c80 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -31,6 +31,12 @@ get "/activity" => "activity#index", :as => :activity end + authenticate :user, lambda { |u| u.is_moderator? } do + namespace :settings do + resources :users + end + end + mount Federails::Engine => "/" if SiteSettings.multiuser_enabled? || SiteSettings.federation_enabled? || Rails.env.test? root to: "home#index" diff --git a/spec/requests/settings/users_spec.rb b/spec/requests/settings/users_spec.rb new file mode 100644 index 000000000..0523fc493 --- /dev/null +++ b/spec/requests/settings/users_spec.rb @@ -0,0 +1,11 @@ +require "rails_helper" + +RSpec.describe "/settings/users", type: :request do + + describe "GET /index", :as_moderator do + it "renders a successful response" do + create(:user) + get users_url + expect(response).to be_successful + end + end diff --git a/spec/routing/moderation/users_routing_spec.rb b/spec/routing/moderation/users_routing_spec.rb new file mode 100644 index 000000000..28a1ecaa4 --- /dev/null +++ b/spec/routing/moderation/users_routing_spec.rb @@ -0,0 +1,9 @@ +require "rails_helper" + +RSpec.describe Settings::UsersController, type: :routing do + describe "routing" do + it "routes to #index" do + expect(get: "/settings/users").to route_to("settings/users#index") + end + end +end From 2f2d57444dd8c88c943826c5441b1a548c90722d Mon Sep 17 00:00:00 2001 From: James Smith Date: Wed, 20 Nov 2024 14:15:59 +0000 Subject: [PATCH 2/6] only mount user moderation if multiuser is enabled --- config/routes.rb | 23 ++++++++++--------- spec/requests/settings/users_spec.rb | 4 ++-- spec/routing/moderation/users_routing_spec.rb | 2 +- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/config/routes.rb b/config/routes.rb index e440a2c80..3dd2ec234 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -31,13 +31,20 @@ get "/activity" => "activity#index", :as => :activity end - authenticate :user, lambda { |u| u.is_moderator? } do - namespace :settings do - resources :users + if SiteSettings.multiuser_enabled? + authenticate :user, lambda { |u| u.is_moderator? } do + namespace :settings do + resources :users + end end - end + mount Federails::Engine => "/" if SiteSettings.federation_enabled? || Rails.env.test? - mount Federails::Engine => "/" if SiteSettings.multiuser_enabled? || SiteSettings.federation_enabled? || Rails.env.test? + get "/follow" => "follows#index", :as => :follow + get "/authorize_interaction" => "follows#new", :as => :new_follow + post "/remote_follow" => "follows#remote_follow", :as => :remote_follow + post "/perform_remote_follow" => "follows#perform_remote_follow", :as => :perform_remote_follow + post "/follow_remote_actor/:id" => "follows#follow_remote_actor", :as => :follow_remote_actor + end root to: "home#index" @@ -50,12 +57,6 @@ end end - get "/follow" => "follows#index", :as => :follow - get "/authorize_interaction" => "follows#new", :as => :new_follow - post "/remote_follow" => "follows#remote_follow", :as => :remote_follow - post "/perform_remote_follow" => "follows#perform_remote_follow", :as => :perform_remote_follow - post "/follow_remote_actor/:id" => "follows#follow_remote_actor", :as => :follow_remote_actor - concern :followable do |options| if SiteSettings.multiuser_enabled? resources :follows, {only: [:create]}.merge(options) do diff --git a/spec/requests/settings/users_spec.rb b/spec/requests/settings/users_spec.rb index 0523fc493..4ed2fd22b 100644 --- a/spec/requests/settings/users_spec.rb +++ b/spec/requests/settings/users_spec.rb @@ -1,7 +1,6 @@ require "rails_helper" -RSpec.describe "/settings/users", type: :request do - +RSpec.describe "/settings/users" do describe "GET /index", :as_moderator do it "renders a successful response" do create(:user) @@ -9,3 +8,4 @@ expect(response).to be_successful end end +end diff --git a/spec/routing/moderation/users_routing_spec.rb b/spec/routing/moderation/users_routing_spec.rb index 28a1ecaa4..4a931ace1 100644 --- a/spec/routing/moderation/users_routing_spec.rb +++ b/spec/routing/moderation/users_routing_spec.rb @@ -1,6 +1,6 @@ require "rails_helper" -RSpec.describe Settings::UsersController, type: :routing do +RSpec.describe Settings::UsersController do describe "routing" do it "routes to #index" do expect(get: "/settings/users").to route_to("settings/users#index") From 53502720f15c243560a56096b1ed61e0d8d8531b Mon Sep 17 00:00:00 2001 From: James Smith Date: Wed, 20 Nov 2024 15:10:02 +0000 Subject: [PATCH 3/6] add user details page --- app/controllers/settings/users_controller.rb | 9 +++- app/views/settings/users/index.html.erb | 6 +-- app/views/settings/users/show.html.erb | 44 +++++++++++++++++++ config/locales/settings/en.yml | 6 +++ spec/requests/settings/users_spec.rb | 10 +++++ spec/routing/moderation/users_routing_spec.rb | 4 ++ 6 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 app/views/settings/users/show.html.erb diff --git a/app/controllers/settings/users_controller.rb b/app/controllers/settings/users_controller.rb index 7ed1f0546..e93f54f88 100644 --- a/app/controllers/settings/users_controller.rb +++ b/app/controllers/settings/users_controller.rb @@ -1,13 +1,20 @@ class Settings::UsersController < ApplicationController + before_action :get_user, except: [:index] + def index @users = policy_scope(Federails::Actor).where(entity_type: "User") render layout: "settings" end + def show + render layout: "settings" + end + private def get_user - policy_scope(User).find_param(:id) + @user = policy_scope(User).find_param(params[:id]) + authorize @user end def user_params diff --git a/app/views/settings/users/index.html.erb b/app/views/settings/users/index.html.erb index 733a31627..1c87d2a98 100644 --- a/app/views/settings/users/index.html.erb +++ b/app/views/settings/users/index.html.erb @@ -6,7 +6,7 @@ <%= User.human_attribute_name(:username) %> <%= User.human_attribute_name(:email) %> - <%= content_tag :th, t(".oidc") if SiteSettings.oidc_enabled? %> + <%= content_tag :th, User.human_attribute_name(:auth_uid) if SiteSettings.oidc_enabled? %> <%= content_tag :th, t(".local") if SiteSettings.federation_enabled? %> <%= content_tag :th, t(".fediverse_address") if SiteSettings.federation_enabled? %> <%= User.human_attribute_name(:roles) %> @@ -16,11 +16,11 @@ <%= user.entity.is_a?(User) ? user.entity.username : user.username %> <%= user.entity.is_a?(User) ? masked_email(user.entity.email) : nil %> - <%= content_tag :td, (user.auth_uid? ? "✅" : "❌") if SiteSettings.oidc_enabled? %> + <%= content_tag :td, (user.entity.auth_uid ? "✅" : "❌") if SiteSettings.oidc_enabled? %> <%= content_tag :td, (user.local? ? "✅" : "❌") if SiteSettings.federation_enabled? %> <%= content_tag :td, user.at_address if SiteSettings.federation_enabled? %> <%= user.entity.roles.map(&:name).join(", ") if user.entity.is_a?(User) %> - <%= link_to safe_join([icon("pencil", t("general.edit")), t("general.edit")], " "), settings_user_path(user), class: "btn btn-primary" %> + <%= link_to safe_join([icon("search", t(".view")), t(".view")], " "), settings_user_path(user.entity), class: "btn btn-primary" %> <% end %> diff --git a/app/views/settings/users/show.html.erb b/app/views/settings/users/show.html.erb new file mode 100644 index 000000000..6db08a9bb --- /dev/null +++ b/app/views/settings/users/show.html.erb @@ -0,0 +1,44 @@ +

<%= t(".title", username: @user.username) %>

+ + + + + + + + + + + + + + + + + + + + + + <% if SiteSettings.oidc_enabled? %> + + + + + <% end %> + <% if SiteSettings.federation_enabled? %> + + + + + <% else %> + + + + + <% end %> + + + + +
<%= User.human_attribute_name(:username) %><%= @user.username %>
<%= User.human_attribute_name(:email) %><%= @user.email %>
<%= User.human_attribute_name(:created_at) %><%= @user.created_at.to_fs(:long) %>
<%= User.human_attribute_name(:updated_at) %><%= @user.updated_at.to_fs(:long) %>
<%= User.human_attribute_name(:interface_language) %><%= @user.interface_language.present? ? I18nData.languages(@user.interface_language)[@user.interface_language.to_s]&.capitalize : t("devise.registrations.general_settings.interface_language.autodetect") %>
<%= User.human_attribute_name(:auth_uid) %><%= (user.auth_uid ? "✅" : "❌") %>
<%= t("settings.users.index.fediverse_address") %><%= @user.actor.at_address %>
<%= User.human_attribute_name(:public_id) %><%= @user.public_id %>
<%= User.human_attribute_name(:roles) %><%= @user.roles.map(&:name).join(", ") %>
diff --git a/config/locales/settings/en.yml b/config/locales/settings/en.yml index dde7eb992..8d7acf5a7 100644 --- a/config/locales/settings/en.yml +++ b/config/locales/settings/en.yml @@ -70,4 +70,10 @@ en: success: Settings saved. users: index: + description: View and edit registered user accounts. title: Manage Users + fediverse_address: Fediverse address + local: Local + view: View + show: + title: "User details: %{username}" diff --git a/spec/requests/settings/users_spec.rb b/spec/requests/settings/users_spec.rb index 4ed2fd22b..f9bc9ec0b 100644 --- a/spec/requests/settings/users_spec.rb +++ b/spec/requests/settings/users_spec.rb @@ -8,4 +8,14 @@ expect(response).to be_successful end end + + describe "GET /show" do + it "renders a successful response" do + user = Settings::User.create! valid_attributes + get user_url(user) + expect(response).to be_successful + end + end + + describe "GET /new" do end diff --git a/spec/routing/moderation/users_routing_spec.rb b/spec/routing/moderation/users_routing_spec.rb index 4a931ace1..64c81d0c2 100644 --- a/spec/routing/moderation/users_routing_spec.rb +++ b/spec/routing/moderation/users_routing_spec.rb @@ -5,5 +5,9 @@ it "routes to #index" do expect(get: "/settings/users").to route_to("settings/users#index") end + + it "routes to #show" do + expect(get: "/settings/users/1").to route_to("settings/users#show", id: "1") + end end end From 3c73ff3b72e0ca889190181b2f978a2aa4a36751 Mon Sep 17 00:00:00 2001 From: James Smith Date: Wed, 20 Nov 2024 15:27:49 +0000 Subject: [PATCH 4/6] normalize translation file --- config/locales/settings/en.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/locales/settings/en.yml b/config/locales/settings/en.yml index 8d7acf5a7..d4948fabc 100644 --- a/config/locales/settings/en.yml +++ b/config/locales/settings/en.yml @@ -71,9 +71,9 @@ en: users: index: description: View and edit registered user accounts. - title: Manage Users fediverse_address: Fediverse address local: Local + title: Manage Users view: View show: - title: "User details: %{username}" + title: 'User details: %{username}' From 2ac664eb1472c44c9e716d9ca7d459e974985e29 Mon Sep 17 00:00:00 2001 From: James Smith Date: Wed, 20 Nov 2024 15:29:09 +0000 Subject: [PATCH 5/6] update tests --- config/routes.rb | 2 +- spec/requests/settings/users_spec.rb | 12 +++++------- spec/routing/moderation/users_routing_spec.rb | 13 ------------- 3 files changed, 6 insertions(+), 21 deletions(-) delete mode 100644 spec/routing/moderation/users_routing_spec.rb diff --git a/config/routes.rb b/config/routes.rb index 3dd2ec234..87a2d090d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -31,7 +31,7 @@ get "/activity" => "activity#index", :as => :activity end - if SiteSettings.multiuser_enabled? + if SiteSettings.multiuser_enabled? || Rails.env.test? authenticate :user, lambda { |u| u.is_moderator? } do namespace :settings do resources :users diff --git a/spec/requests/settings/users_spec.rb b/spec/requests/settings/users_spec.rb index f9bc9ec0b..9d607919c 100644 --- a/spec/requests/settings/users_spec.rb +++ b/spec/requests/settings/users_spec.rb @@ -1,21 +1,19 @@ require "rails_helper" -RSpec.describe "/settings/users" do +RSpec.describe "/settings/users", :multiuser do describe "GET /index", :as_moderator do it "renders a successful response" do create(:user) - get users_url + get "/settings/users" expect(response).to be_successful end end - describe "GET /show" do + describe "GET /show", :as_moderator do it "renders a successful response" do - user = Settings::User.create! valid_attributes - get user_url(user) + user = create(:user) + get "/settings/users/#{user.to_param}" expect(response).to be_successful end end - - describe "GET /new" do end diff --git a/spec/routing/moderation/users_routing_spec.rb b/spec/routing/moderation/users_routing_spec.rb deleted file mode 100644 index 64c81d0c2..000000000 --- a/spec/routing/moderation/users_routing_spec.rb +++ /dev/null @@ -1,13 +0,0 @@ -require "rails_helper" - -RSpec.describe Settings::UsersController do - describe "routing" do - it "routes to #index" do - expect(get: "/settings/users").to route_to("settings/users#index") - end - - it "routes to #show" do - expect(get: "/settings/users/1").to route_to("settings/users#show", id: "1") - end - end -end From 95ae91634de91f1ea10becea3624ab588ee5cfc6 Mon Sep 17 00:00:00 2001 From: James Smith Date: Wed, 20 Nov 2024 16:05:45 +0000 Subject: [PATCH 6/6] moderators can manage users --- app/policies/user_policy.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/policies/user_policy.rb b/app/policies/user_policy.rb index b7198b237..e074ff7c4 100644 --- a/app/policies/user_policy.rb +++ b/app/policies/user_policy.rb @@ -1,7 +1,7 @@ class UserPolicy < ApplicationPolicy def index? all_of( - user&.is_administrator?, + user&.is_moderator?, none_of( SiteSettings.demo_mode_enabled? ) @@ -12,7 +12,7 @@ def show? all_of( one_of( user == record, - user&.is_administrator? + user&.is_moderator? ) ) end @@ -22,7 +22,7 @@ def create? SiteSettings.multiuser_enabled?, one_of( SiteSettings.registration_enabled?, - user&.is_administrator? + user&.is_moderator? ), none_of( SiteSettings.demo_mode_enabled? @@ -41,7 +41,7 @@ def destroy? all_of( one_of( user == record, - user&.is_administrator? + user&.is_moderator? ), SiteSettings.multiuser_enabled?, none_of(