From 0e96ff7ad414cef2ec80aaf71acec1e51f2d0341 Mon Sep 17 00:00:00 2001 From: Riccardo Graziosi <31478034+riggraz@users.noreply.github.com> Date: Wed, 18 Jan 2023 21:11:27 +0100 Subject: [PATCH] Add role 'owner' to users (#185) --- app/controllers/site_settings_controller.rb | 2 +- app/controllers/users_controller.rb | 3 + app/helpers/application_helper.rb | 4 +- .../SiteSettings/Users/UserEditable.tsx | 15 ++- .../SiteSettings/Users/UserForm.tsx | 11 +- app/javascript/interfaces/IUser.ts | 4 +- app/models/user.rb | 14 +-- app/policies/comment_policy.rb | 8 +- app/policies/o_auth_policy.rb | 4 +- app/policies/post_policy.rb | 6 +- app/policies/user_policy.rb | 10 +- app/views/layouts/_header.html.erb | 2 +- app/views/posts/show.html.erb | 2 +- config/locales/en.yml | 4 +- db/seeds.rb | 16 +-- spec/factories/users.rb | 8 ++ spec/models/user_spec.rb | 28 ++++- spec/policies/board_policy_spec.rb | 43 +++++++ spec/policies/comment_policy_spec.rb | 60 ++++++++++ spec/policies/o_auth_policy_spec.rb | 43 +++++++ spec/policies/post_policy_spec.rb | 53 +++++++++ spec/policies/post_status_policy_spec.rb | 43 +++++++ spec/policies/tenant_policy_spec.rb | 34 ++++++ spec/policies/user_policy_spec.rb | 106 ++++++++++++++++++ spec/support/pundit_matcher.rb | 13 +++ 25 files changed, 482 insertions(+), 54 deletions(-) create mode 100644 spec/policies/board_policy_spec.rb create mode 100644 spec/policies/comment_policy_spec.rb create mode 100644 spec/policies/o_auth_policy_spec.rb create mode 100644 spec/policies/post_policy_spec.rb create mode 100644 spec/policies/post_status_policy_spec.rb create mode 100644 spec/policies/tenant_policy_spec.rb create mode 100644 spec/policies/user_policy_spec.rb create mode 100644 spec/support/pundit_matcher.rb diff --git a/app/controllers/site_settings_controller.rb b/app/controllers/site_settings_controller.rb index 6cbdbee4..662cbb71 100644 --- a/app/controllers/site_settings_controller.rb +++ b/app/controllers/site_settings_controller.rb @@ -4,7 +4,7 @@ class SiteSettingsController < ApplicationController before_action :authenticate_admin, only: [:general, :boards, :post_statuses, :roadmap, :authentication] - before_action :authenticate_power_user, + before_action :authenticate_moderator, only: [:users] def general diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index bad4a5b3..21f1ab19 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -17,6 +17,9 @@ class UsersController < ApplicationController @user.assign_attributes user_update_params + # Handle special case: trying to set user role to 'owner' + raise Pundit::NotAuthorizedError if @user.owner? + if @user.save render json: @user else diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index d4e8b9f1..f1d9c38e 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -18,10 +18,10 @@ module ApplicationHelper end end - def authenticate_power_user + def authenticate_moderator return if check_user_signed_in == false - unless current_user.admin? or current_user.moderator? + unless current_user.moderator? flash[:alert] = t('errors.not_enough_privileges') redirect_to root_path return diff --git a/app/javascript/components/SiteSettings/Users/UserEditable.tsx b/app/javascript/components/SiteSettings/Users/UserEditable.tsx index e9948bc2..8112b563 100644 --- a/app/javascript/components/SiteSettings/Users/UserEditable.tsx +++ b/app/javascript/components/SiteSettings/Users/UserEditable.tsx @@ -2,7 +2,7 @@ import * as React from "react"; import Gravatar from 'react-gravatar'; import I18n from 'i18n-js'; -import IUser, { UserRoles, USER_ROLE_ADMIN, USER_ROLE_USER, USER_STATUS_ACTIVE, USER_STATUS_BLOCKED, USER_STATUS_DELETED } from "../../../interfaces/IUser"; +import IUser, { UserRoles, USER_ROLE_ADMIN, USER_ROLE_MODERATOR, USER_ROLE_OWNER, USER_ROLE_USER, USER_STATUS_ACTIVE, USER_STATUS_BLOCKED, USER_STATUS_DELETED } from "../../../interfaces/IUser"; import Separator from "../../common/Separator"; import UserForm from "./UserForm"; import { MutedText } from "../../common/CustomTexts"; @@ -79,15 +79,14 @@ class UserEditable extends React.Component { const { user, currentUserRole, currentUserEmail } = this.props; const { editMode } = this.state; - const editEnabled = - user.status === USER_STATUS_ACTIVE && - currentUserRole === USER_ROLE_ADMIN && + const hasPrivilege = user.role !== USER_ROLE_OWNER && + (currentUserRole === USER_ROLE_OWNER || + (currentUserRole === USER_ROLE_ADMIN && (user.role === USER_ROLE_MODERATOR || user.role === USER_ROLE_USER)) || + (currentUserRole === USER_ROLE_MODERATOR && user.role === USER_ROLE_USER)) && currentUserEmail !== user.email; - const blockEnabled = - user.status !== USER_STATUS_DELETED && - (currentUserRole === USER_ROLE_ADMIN || user.role === USER_ROLE_USER) && - currentUserEmail !== user.email; + const editEnabled = hasPrivilege && user.status === USER_STATUS_ACTIVE; + const blockEnabled = hasPrivilege && user.status !== USER_STATUS_DELETED; return (
  • diff --git a/app/javascript/components/SiteSettings/Users/UserForm.tsx b/app/javascript/components/SiteSettings/Users/UserForm.tsx index 306f64cd..6516f47b 100644 --- a/app/javascript/components/SiteSettings/Users/UserForm.tsx +++ b/app/javascript/components/SiteSettings/Users/UserForm.tsx @@ -23,12 +23,15 @@ class UserForm extends React.Component { this._handleUpdateUserRole = this._handleUpdateUserRole.bind(this); } - _handleUpdateUserRole(selectedRole: UserRoles) { + _handleUpdateUserRole(selectedRole: UserRoles, currentRole: UserRoles) { const { user, updateUserRole } = this.props; let confirmation = true; - if (selectedRole === 'admin') { - confirmation = confirm(I18n.t('site_settings.users.role_to_admin_confirmation', { name: user.fullName })); + if (selectedRole !== currentRole) { + if (selectedRole === 'moderator') + confirmation = confirm(I18n.t('site_settings.users.role_to_moderator_confirmation', { name: user.fullName })); + else if (selectedRole === 'admin') + confirmation = confirm(I18n.t('site_settings.users.role_to_admin_confirmation', { name: user.fullName })); } if (confirmation) updateUserRole(selectedRole); @@ -68,7 +71,7 @@ class UserForm extends React.Component { - diff --git a/app/javascript/interfaces/IUser.ts b/app/javascript/interfaces/IUser.ts index 25f32afb..77a81201 100644 --- a/app/javascript/interfaces/IUser.ts +++ b/app/javascript/interfaces/IUser.ts @@ -2,11 +2,13 @@ export const USER_ROLE_USER = 'user'; export const USER_ROLE_MODERATOR = 'moderator'; export const USER_ROLE_ADMIN = 'admin'; +export const USER_ROLE_OWNER = 'owner'; export type UserRoles = typeof USER_ROLE_USER | typeof USER_ROLE_MODERATOR | - typeof USER_ROLE_ADMIN; + typeof USER_ROLE_ADMIN | + typeof USER_ROLE_OWNER; // Statuses export const USER_STATUS_ACTIVE = 'active'; diff --git a/app/models/user.rb b/app/models/user.rb index 4c9a8b76..7fb4a15c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -8,7 +8,7 @@ class User < ApplicationRecord has_many :likes, dependent: :destroy has_many :comments, dependent: :destroy - enum role: [:user, :moderator, :admin] + enum role: [:user, :moderator, :admin, :owner] enum status: [:active, :blocked, :deleted] after_initialize :set_default_role, if: :new_record? @@ -63,20 +63,16 @@ class User < ApplicationRecord "https://secure.gravatar.com/avatar/#{gravatar_id}" end - def power_user? - role == 'admin' || role == 'moderator' + def owner? + role == 'owner' end def admin? - role == 'admin' + owner? || role == 'admin' end def moderator? - role == 'moderator' - end - - def user? - role == 'user' + admin? || role == 'moderator' end def active? diff --git a/app/policies/comment_policy.rb b/app/policies/comment_policy.rb index 069dfb17..c1245460 100644 --- a/app/policies/comment_policy.rb +++ b/app/policies/comment_policy.rb @@ -1,6 +1,6 @@ class CommentPolicy < ApplicationPolicy def permitted_attributes_for_create - if user.power_user? + if user.moderator? [:body, :parent_id, :is_post_update] else [:body, :parent_id] @@ -8,7 +8,7 @@ class CommentPolicy < ApplicationPolicy end def permitted_attributes_for_update - if user.power_user? + if user.moderator? [:body, :is_post_update] else [:body] @@ -16,10 +16,10 @@ class CommentPolicy < ApplicationPolicy end def update? - user == record.user or user.power_user? + user == record.user or user.moderator? end def destroy? - user == record.user or user.power_user? + user == record.user or user.moderator? end end \ No newline at end of file diff --git a/app/policies/o_auth_policy.rb b/app/policies/o_auth_policy.rb index 9a263320..90b6f031 100644 --- a/app/policies/o_auth_policy.rb +++ b/app/policies/o_auth_policy.rb @@ -28,10 +28,10 @@ class OAuthPolicy < ApplicationPolicy end def update? - user.admin? and user.tenant_id == record.tenant_id + user.admin? end def destroy? - user.admin? and user.tenant_id == record.tenant_id + user.admin? end end \ No newline at end of file diff --git a/app/policies/post_policy.rb b/app/policies/post_policy.rb index 8e15623d..8ca21096 100644 --- a/app/policies/post_policy.rb +++ b/app/policies/post_policy.rb @@ -4,7 +4,7 @@ class PostPolicy < ApplicationPolicy end def permitted_attributes_for_update - if user.power_user? + if user.moderator? [:title, :description, :board_id, :post_status_id] else [:title, :description] @@ -12,10 +12,10 @@ class PostPolicy < ApplicationPolicy end def update? - user == record.user or user.power_user? + user == record.user or user.moderator? end def destroy? - user == record.user or user.power_user? + user == record.user or user.moderator? end end \ No newline at end of file diff --git a/app/policies/user_policy.rb b/app/policies/user_policy.rb index bdb87441..53ec5565 100644 --- a/app/policies/user_policy.rb +++ b/app/policies/user_policy.rb @@ -10,14 +10,16 @@ class UserPolicy < ApplicationPolicy end def index? - user.power_user? + user.moderator? end def update? - if user.admin? - record.id != user.id + if user.owner? + true + elsif user.admin? + record.role == 'moderator' || record.role == 'user' elsif user.moderator? - record.user? + record.role == 'user' else false end diff --git a/app/views/layouts/_header.html.erb b/app/views/layouts/_header.html.erb index 086062b6..3242ab24 100644 --- a/app/views/layouts/_header.html.erb +++ b/app/views/layouts/_header.html.erb @@ -40,7 +40,7 @@ <%= current_user.full_name %>