diff --git a/cosmetics-web/Gemfile b/cosmetics-web/Gemfile index 07034d05bb..8adf882659 100644 --- a/cosmetics-web/Gemfile +++ b/cosmetics-web/Gemfile @@ -97,3 +97,5 @@ gem "database_cleaner-active_record" gem "graphiql-rails", group: :development gem "graphql", "~> 2.3" + +gem "rolify" diff --git a/cosmetics-web/Gemfile.lock b/cosmetics-web/Gemfile.lock index 7e2435345e..cb1d2ab282 100644 --- a/cosmetics-web/Gemfile.lock +++ b/cosmetics-web/Gemfile.lock @@ -447,6 +447,7 @@ GEM reverse_markdown (2.1.1) nokogiri rexml (3.3.8) + rolify (6.0.1) rotp (6.3.0) rqrcode (2.2.0) chunky_png (~> 1.0) @@ -690,6 +691,7 @@ DEPENDENCIES redis (~> 5.2) request_store (~> 1.6) rest-client (~> 2.1) + rolify rotp (~> 6.2) rqrcode (~> 2.1) rspec-collection_matchers diff --git a/cosmetics-web/app/models/role.rb b/cosmetics-web/app/models/role.rb new file mode 100644 index 0000000000..8ae3ff9f55 --- /dev/null +++ b/cosmetics-web/app/models/role.rb @@ -0,0 +1,13 @@ +class Role < ApplicationRecord + has_and_belongs_to_many :users, join_table: :users_roles + + belongs_to :resource, + polymorphic: true, + optional: true + + validates :resource_type, + inclusion: { in: Rolify.resource_types }, + allow_nil: true + + scopify +end diff --git a/cosmetics-web/app/models/user.rb b/cosmetics-web/app/models/user.rb index f694860b02..ecfb5dd7a3 100644 --- a/cosmetics-web/app/models/user.rb +++ b/cosmetics-web/app/models/user.rb @@ -1,4 +1,5 @@ class User < ApplicationRecord + rolify NAME_MAX_LENGTH = 50 NEW_EMAIL_TOKEN_VALID_FOR = 600 # 10 minutes SECONDARY_AUTHENTICATION_METHODS = %w[app sms].freeze diff --git a/cosmetics-web/config/initializers/rolify.rb b/cosmetics-web/config/initializers/rolify.rb new file mode 100644 index 0000000000..da8907ef18 --- /dev/null +++ b/cosmetics-web/config/initializers/rolify.rb @@ -0,0 +1,10 @@ +Rolify.configure do |config| + # By default ORM adapter is ActiveRecord. uncomment to use mongoid + # config.use_mongoid + + # Dynamic shortcuts for User class (user.is_admin? like methods). Default is: false + # config.use_dynamic_shortcuts + + # Configuration to remove roles from database once the last resource is removed. Default is: true + # config.remove_role_if_empty = false +end diff --git a/cosmetics-web/db/migrate/20241021154308_rolify_create_roles.rb b/cosmetics-web/db/migrate/20241021154308_rolify_create_roles.rb new file mode 100644 index 0000000000..31ecf4b1c1 --- /dev/null +++ b/cosmetics-web/db/migrate/20241021154308_rolify_create_roles.rb @@ -0,0 +1,18 @@ +class RolifyCreateRoles < ActiveRecord::Migration[7.1] + def change + create_table(:roles) do |t| + t.string :name + t.references :resource, polymorphic: true + + t.timestamps + end + + create_table(:users_roles, id: false) do |t| + t.references :user + t.references :role + end + + add_index(:roles, %i[name resource_type resource_id]) + add_index(:users_roles, %i[user_id role_id]) + end +end diff --git a/cosmetics-web/db/schema.rb b/cosmetics-web/db/schema.rb index 056ffdf719..9db83dcc21 100644 --- a/cosmetics-web/db/schema.rb +++ b/cosmetics-web/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2024_10_08_153814) do +ActiveRecord::Schema[7.1].define(version: 2024_10_21_154308) do # These are extensions that must be enabled in order to support this database enable_extension "citext" enable_extension "pg_trgm" @@ -312,6 +312,16 @@ t.datetime "updated_at", precision: nil, null: false end + create_table "roles", force: :cascade do |t| + t.string "name" + t.string "resource_type" + t.bigint "resource_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["name", "resource_type", "resource_id"], name: "index_roles_on_name_and_resource_type_and_resource_id" + t.index ["resource_type", "resource_id"], name: "index_roles_on_resource" + end + create_table "search_histories", force: :cascade do |t| t.string "query" t.integer "results" @@ -396,6 +406,14 @@ t.index ["unlock_token"], name: "index_users_on_unlock_token", unique: true end + create_table "users_roles", id: false, force: :cascade do |t| + t.bigint "user_id" + t.bigint "role_id" + t.index ["role_id"], name: "index_users_roles_on_role_id" + t.index ["user_id", "role_id"], name: "index_users_roles_on_user_id_and_role_id" + t.index ["user_id"], name: "index_users_roles_on_user_id" + end + create_table "versions", force: :cascade do |t| t.string "item_type", null: false t.bigint "item_id", null: false diff --git a/cosmetics-web/spec/factories/roles.rb b/cosmetics-web/spec/factories/roles.rb new file mode 100644 index 0000000000..5e9178e9bf --- /dev/null +++ b/cosmetics-web/spec/factories/roles.rb @@ -0,0 +1,4 @@ +FactoryBot.define do + factory :role do + end +end diff --git a/cosmetics-web/spec/models/role_spec.rb b/cosmetics-web/spec/models/role_spec.rb new file mode 100644 index 0000000000..47536104d7 --- /dev/null +++ b/cosmetics-web/spec/models/role_spec.rb @@ -0,0 +1,5 @@ +require "rails_helper" + +RSpec.describe Role, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end