├── .github └── issue_template.md ├── .gitignore ├── .rspec ├── .rubocop.yml ├── .rubocop_todo.yml ├── .travis.yml ├── Appraisals ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── active_admin_role.gemspec ├── app └── models │ ├── active_admin │ ├── manageable_resource.rb │ ├── managed_resource.rb │ ├── permission.rb │ └── permission_reloader.rb │ └── active_admin_role │ └── can_can │ └── ability.rb ├── config └── locales │ ├── en.yml │ └── ja.yml ├── gemfiles ├── 5.0.gemfile ├── 5.1.gemfile └── 5.2.gemfile ├── lib ├── active_admin_role.rb ├── active_admin_role │ ├── active_admin │ │ ├── dsl.rb │ │ └── resource_controller.rb │ ├── config.rb │ ├── dsl.rb │ ├── engine.rb │ ├── role_based_authorizable.rb │ └── version.rb └── generators │ └── active_admin_role │ ├── USAGE │ ├── helper.rb │ ├── install_generator.rb │ └── templates │ ├── admin │ └── permissions.tt │ ├── initializer.tt │ ├── migration │ ├── add_role_to_admin_users.tt │ ├── create_active_admin_managed_resources.tt │ └── create_active_admin_permissions.tt │ └── model │ └── ability.tt └── spec ├── active_admin_role_spec.rb ├── config_spec.rb ├── features ├── admin_user_spec.rb ├── integration_spec.rb └── permission_spec.rb ├── models ├── manageable_resource_spec.rb ├── managed_resource_spec.rb ├── permission_spec.rb └── role_based_authorizable_spec.rb ├── spec_helper.rb └── support └── rails_template.rb /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | ### Steps to reproduce 2 | 3 | * Explain steps to reproduce. 4 | * Paste your error logs. 5 | * Tell us your repository of app, if you ok. 6 | 7 | ### Expected behavior 8 | 9 | Tell us what should happen 10 | 11 | ### Actual behavior 12 | 13 | Tell us what happens instead 14 | 15 | ### System configuration 16 | 17 | **ActiveAdmin version**: 18 | 19 | **ActiveAdminRole version**: 20 | 21 | **Rails version**: 22 | 23 | **Ruby version**: 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /spec/rails/ 9 | /spec/reports/ 10 | /tmp/ 11 | /gemfiles/*.gemfile.lock 12 | /gemfiles/.bundle/* 13 | /gemfiles/vendor/ 14 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format documentation 2 | --color 3 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | inherit_from: .rubocop_todo.yml 2 | 3 | inherit_gem: 4 | onkcop: 5 | - "config/rubocop.yml" 6 | # uncomment if use rails cops 7 | # - "config/rails.yml" 8 | # uncomment if use rspec cops 9 | # - "config/rspec.yml" 10 | 11 | AllCops: 12 | TargetRubyVersion: 2.2 13 | # uncomment if use rails cops 14 | # TargetRailsVersion: 5.1 15 | Exclude: 16 | - "spec/rails/**/*" 17 | - "vendor/bundle/**/*" 18 | - "gemfiles/vendor/**/*" 19 | - "gemfiles/.bundle/**/*" 20 | - "tmp/**/*" 21 | -------------------------------------------------------------------------------- /.rubocop_todo.yml: -------------------------------------------------------------------------------- 1 | # This configuration was generated by 2 | # `rubocop --auto-gen-config` 3 | # on 2018-04-08 11:32:33 +0900 using RuboCop version 0.53.0. 4 | # The point is for the user to remove these configuration records 5 | # one by one as the offenses are removed from the code base. 6 | # Note that changes in the inspected code, or installation of new 7 | # versions of RuboCop, may require this file to be generated again. 8 | 9 | # Offense count: 2 10 | # Configuration parameters: CheckForMethodsWithNoSideEffects. 11 | Lint/Void: 12 | Exclude: 13 | - 'spec/features/integration_spec.rb' 14 | 15 | # Offense count: 1 16 | Metrics/AbcSize: 17 | Max: 28 18 | 19 | # Offense count: 2 20 | Naming/MemoizedInstanceVariableName: 21 | Exclude: 22 | - 'app/models/active_admin/manageable_resource.rb' 23 | - 'lib/active_admin_role/role_based_authorizable.rb' 24 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | before_install: 3 | - gem update --system 4 | - gem install bundler 5 | gemfiles: 6 | - gemfiles/5.0.gemfile 7 | - gemfiles/5.1.gemfile 8 | - gemfiles/5.2.gemfile 9 | rvm: 10 | - 2.3.8 11 | - 2.4.5 12 | - 2.5.3 13 | script: 14 | - bundle exec rspec 15 | -------------------------------------------------------------------------------- /Appraisals: -------------------------------------------------------------------------------- 1 | appraise "5.0" do 2 | gem "rails", "~> 5.0.0" 3 | end 4 | 5 | appraise "5.1" do 6 | gem "rails", "~> 5.1.0" 7 | end 8 | 9 | appraise "5.2" do 10 | gem "rails", "~> 5.2.0.rc2" 11 | end 12 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } 4 | 5 | gemspec 6 | 7 | gem "appraisal" 8 | gem "devise" 9 | gem "jquery-ui-rails", "~> 6.0.1" 10 | gem "rails", ">= 5.0.0" 11 | 12 | group :development, :test do 13 | gem "sqlite3", platforms: :mri 14 | end 15 | 16 | group :development do 17 | gem "onkcop", require: false 18 | end 19 | 20 | group :test do 21 | gem "capybara" 22 | gem "database_cleaner" 23 | gem "poltergeist" 24 | gem "rspec-rails" 25 | gem "shoulda-matchers" 26 | end 27 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Yoshiyuki Hirano 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ActiveAdminRole 2 | 3 | *CAUTION: Sorry, this gem is not maintained now. I'm looking for maintainer has motivation for apps using ActiveAdmin. Please somebody help me. [See more](https://github.com/activeadmin-plugins/active_admin_role/issues/19)* 4 | 5 | Role based authorization with CanCanCan for Active Admin 6 | 7 | ## Installation 8 | 9 | Add this line to your application's Gemfile: 10 | 11 | ```ruby 12 | gem 'active_admin_role' 13 | ``` 14 | 15 | And run `bundle` 16 | 17 | ## Dependencies 18 | 19 | - rails (>= 5.0.0) 20 | - activeadmin (>= 1.2.0) 21 | - cancancan (>= 1.15.0) 22 | 23 | ## Sample application 24 | 25 | https://github.com/activeadmin-plugins/active_admin_role_sample 26 | 27 | ## Usage 28 | 29 | 1. Run this command after `rails generate active_admin:install`: 30 | 31 | ```sh 32 | $ bin/rails generate active_admin_role:install 33 | create config/initializers/active_admin_role.rb 34 | insert app/models/admin_user.rb 35 | create db/migrate/20161128090641_add_role_to_admin_users.rb 36 | create db/migrate/20161128090642_create_active_admin_managed_resources.rb 37 | create db/migrate/20161128090643_create_active_admin_permissions.rb 38 | create app/models/ability.rb 39 | gsub config/initializers/active_admin.rb 40 | create app/admin/permissions.rb 41 | insert app/admin/admin_users.rb 42 | 43 | $ bin/rails db:migrate 44 | ``` 45 | 46 | 2. You have to login as **admin** after migration. 47 | 48 | 3. You have to **Reload** permissions. 49 | 50 | ![](https://cloud.githubusercontent.com/assets/15371677/20662507/015c877c-b597-11e6-82dc-bf80dac8c6e9.png) 51 | 52 | 4. Edit permissions however you like. 53 | 54 | ![](https://cloud.githubusercontent.com/assets/15371677/20662765/2a8be9c0-b598-11e6-88c5-b9b7c018c876.png) 55 | 56 | 5. Of course, you can edit AdminUser's roles. 57 | 58 | ![](https://cloud.githubusercontent.com/assets/15371677/20662882/ba2f9f18-b598-11e6-8a4b-ed7c6d5b1246.png) 59 | 60 | ## Configuration 61 | 62 | ```ruby 63 | ActiveAdminRole.configure do |config| 64 | # [Required:Hash] 65 | # == Role | default: { guest: 0, support: 1, staff: 2, manager: 3, admin: 99 } 66 | config.roles = { guest: 0, support: 1, staff: 2, manager: 3, admin: 99 } 67 | 68 | # [Optional:Array] 69 | # == Special roles which don't need to manage on database 70 | config.super_user_roles = [:admin] 71 | config.guest_user_roles = [:guest] 72 | 73 | # [Optional:String] 74 | # == User class name | default: 'AdminUser' 75 | config.user_class_name = "AdminUser" 76 | 77 | # [Optional:Symbol] 78 | # == Default permission | default: :cannot 79 | config.default_state = :cannot 80 | end 81 | ``` 82 | 83 | ## License 84 | 85 | [MIT License](http://opensource.org/licenses/MIT) 86 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler" 2 | require "rake" 3 | 4 | Bundler.setup 5 | Bundler::GemHelper.install_tasks 6 | 7 | require "rspec/core/rake_task" 8 | RSpec::Core::RakeTask.new(:spec) 9 | task default: :spec 10 | 11 | desc "Creates a test rails app for the specs to run against" 12 | task :setup do 13 | require "rails/version" 14 | system "bundle exec rails --version" 15 | system <<-COMMAND 16 | bundle exec rails new tmp/rails-#{Rails::VERSION::STRING} \ 17 | -m spec/support/rails_template.rb \ 18 | --skip-spring \ 19 | --skip-listen \ 20 | --skip-turbolinks \ 21 | --skip-bootsnap \ 22 | --skip-test \ 23 | --skip-git \ 24 | --skip-yarn \ 25 | --skip-puma \ 26 | --skip-action-mailer \ 27 | --skip-action-cable 28 | COMMAND 29 | end 30 | 31 | namespace :tmp do 32 | task :clear do 33 | require "fileutils" 34 | FileUtils.rm_r("tmp") 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /active_admin_role.gemspec: -------------------------------------------------------------------------------- 1 | require File.join(File.dirname(__FILE__), "lib", "active_admin_role", "version") 2 | 3 | Gem::Specification.new do |gem| 4 | gem.name = "active_admin_role" 5 | gem.version = ActiveAdminRole::VERSION 6 | gem.authors = ["Yoshiyuki Hirano"] 7 | gem.email = ["yhirano@me.com"] 8 | 9 | gem.summary = "Role based authorization with CanCanCan for Active Admin" 10 | gem.description = gem.summary 11 | gem.homepage = "https://github.com/activeadmin-plugins/active_admin_role" 12 | gem.license = "MIT" 13 | 14 | gem.files = `git ls-files -z`.split("\x0").reject {|f| f.match(%r{^(test|spec|features)/}) } 15 | gem.require_paths = ["lib"] 16 | 17 | gem.required_ruby_version = ">= 2.2.2" 18 | 19 | gem.add_dependency "activeadmin", ">= 1.2.0" 20 | gem.add_dependency "cancancan", ">= 1.15.0" 21 | gem.add_dependency "railties", ">= 5.0.0" 22 | end 23 | -------------------------------------------------------------------------------- /app/models/active_admin/manageable_resource.rb: -------------------------------------------------------------------------------- 1 | require "set" 2 | 3 | module ActiveAdmin 4 | class ManageableResource 5 | class << self 6 | def call 7 | new.call 8 | end 9 | end 10 | 11 | def call 12 | namespace = ::ActiveAdmin.application.default_namespace 13 | ::ActiveAdmin.application.namespaces[namespace].resources.inject([]) do |result, resource| 14 | class_name = resource.controller.resource_class.to_s 15 | name = resource.resource_name.name 16 | actions = collect_defined_actions(resource) 17 | 18 | result += eval_actions(actions).map do |action| 19 | { class_name: class_name, name: name, action: action } 20 | end 21 | 22 | result 23 | end 24 | end 25 | 26 | private 27 | 28 | def collect_defined_actions(resource) 29 | if resource.respond_to?(:defined_actions) 30 | defined_actions = resource.defined_actions 31 | member_actions = resource.member_actions.map(&:name) 32 | collection_actions = resource.collection_actions.map(&:name) 33 | batch_actions = resource.batch_actions_enabled? ? [:batch_action] : [] 34 | 35 | defined_actions | member_actions | member_actions | collection_actions | batch_actions 36 | else 37 | resource.page_actions.map(&:name) | [:index] 38 | end 39 | end 40 | 41 | def eval_actions(actions) 42 | actions.inject(Set.new) do |result, action| 43 | result << (actions_dictionary[action] || action).to_s 44 | end 45 | end 46 | 47 | def actions_dictionary 48 | @_actions_dictionary ||= ::ActiveAdmin::BaseController::Authorization::ACTIONS_DICTIONARY.dup 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /app/models/active_admin/managed_resource.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | class ManagedResource < ActiveRecord::Base 3 | self.table_name = "active_admin_managed_resources" 4 | 5 | has_many :permissions, dependent: :destroy 6 | validates :class_name, presence: true 7 | validates :action, presence: true 8 | 9 | def const 10 | @const ||= class_name.try(:safe_constantize) 11 | end 12 | 13 | def active? 14 | !const.nil? 15 | end 16 | 17 | def for_active_admin_page? 18 | class_name == "ActiveAdmin::Page" 19 | end 20 | 21 | class << self 22 | def reload 23 | ActiveAdmin::PermissionReloader.reload 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /app/models/active_admin/permission.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | class Permission < ActiveRecord::Base 3 | self.table_name = :active_admin_permissions 4 | role_based_authorizable 5 | 6 | enum state: { cannot: 0, can: 1 } 7 | 8 | belongs_to :managed_resource 9 | delegate :class_name, :action, :name, :const, :active?, :for_active_admin_page?, to: :managed_resource 10 | 11 | validates :role, presence: true 12 | validates :state, presence: true 13 | validates :managed_resource_id, uniqueness: { scope: :role } 14 | 15 | after_update do 16 | self.class.clear_cache 17 | end 18 | 19 | def to_condition 20 | [].tap do |cond| 21 | cond << state 22 | cond << action.to_sym 23 | cond << const 24 | cond << { name: name } if for_active_admin_page? 25 | end 26 | end 27 | 28 | class << self 29 | def update_all_from_managed_resources 30 | ::ActiveAdmin::ManagedResource.all.find_each do |managed_resource| 31 | manageable_roles.values.each do |value_of_role| 32 | find_or_create_by!(managed_resource_id: managed_resource.id, role: value_of_role) do |permission| 33 | permission.state = default_state 34 | end 35 | end 36 | end 37 | end 38 | 39 | def indexed_cache 40 | @indexed_cache ||= eager_load(:managed_resource).all.group_by(&:role) 41 | end 42 | 43 | def clear_cache 44 | @indexed_cache = nil 45 | end 46 | 47 | private 48 | 49 | def default_state 50 | @default_state ||= ::ActiveAdminRole.config.default_state 51 | end 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /app/models/active_admin/permission_reloader.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdmin 2 | module PermissionReloader 3 | class << self 4 | def reload 5 | ActiveRecord::Base.transaction do 6 | clear_cache 7 | update_managed_resources 8 | cleanup_managed_resources 9 | update_permissions 10 | end 11 | end 12 | 13 | private 14 | 15 | def clear_cache 16 | @manageable_resources = nil 17 | end 18 | 19 | def update_managed_resources 20 | manageable_resources.each do |manageable_resource| 21 | ::ActiveAdmin::ManagedResource.find_or_create_by!(manageable_resource) 22 | end 23 | end 24 | 25 | def cleanup_managed_resources 26 | (persisted_resources - manageable_resources).each do |condition| 27 | ::ActiveAdmin::ManagedResource.where(condition).destroy_all 28 | end 29 | end 30 | 31 | def update_permissions 32 | ::ActiveAdmin::Permission.clear_cache 33 | ::ActiveAdmin::Permission.update_all_from_managed_resources 34 | end 35 | 36 | def persisted_resources 37 | ::ActiveAdmin::ManagedResource.all.map(&:attributes).map do |attribute| 38 | attribute.slice("class_name", "action", "name").symbolize_keys 39 | end 40 | end 41 | 42 | def manageable_resources 43 | @manageable_resources ||= ::ActiveAdmin::ManageableResource.call 44 | end 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /app/models/active_admin_role/can_can/ability.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdminRole 2 | module CanCan 3 | module Ability 4 | extend ActiveSupport::Concern 5 | 6 | private 7 | 8 | def register_role_based_abilities(user) 9 | return if user.guest_user? 10 | 11 | (::ActiveAdmin::Permission.indexed_cache[user.role] || []).select(&:active?).each do |permission| 12 | send(*permission.to_condition) 13 | end 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | activerecord: 3 | models: 4 | active_admin/managed_resource: Managed Resource 5 | active_admin/permission: Permission 6 | attributes: 7 | base: &base 8 | id: ID 9 | created_at: Created at 10 | updated_at: Updated at 11 | managed_resource: &managed_resource 12 | class_name: Class Name 13 | action: Action 14 | name: Name 15 | active_admin/managed_resource: 16 | <<: *base 17 | <<: *managed_resource 18 | active_admin/permission: 19 | <<: *base 20 | <<: *managed_resource 21 | managed_resource_id: Managed Resource 22 | role: Role 23 | state: State 24 | 25 | views: 26 | admin_user: 27 | notice: 28 | assigned: "Selected records have assigned as %{role}" 29 | permission: 30 | notice: 31 | state_changed: "Selected records have changed to %{state}" 32 | reloaded: "Reloaded" 33 | action_item: 34 | reload: "Reload" 35 | 36 | active_admin: 37 | batch_actions: 38 | labels: 39 | can: "Enable" 40 | cannot: "Disable" 41 | -------------------------------------------------------------------------------- /config/locales/ja.yml: -------------------------------------------------------------------------------- 1 | ja: 2 | activerecord: 3 | models: 4 | active_admin/managed_resource: リソース 5 | active_admin/permission: 権限 6 | attributes: 7 | base: &base 8 | id: ID 9 | created_at: 作成日時 10 | updated_at: 更新日時 11 | managed_resource: &managed_resource 12 | class_name: クラス 13 | action: 操作 14 | name: ページ名称 15 | active_admin/managed_resource: 16 | <<: *base 17 | <<: *managed_resource 18 | active_admin/permission: 19 | <<: *base 20 | <<: *managed_resource 21 | managed_resource_id: リソース 22 | role: 役割 23 | state: 状態 24 | 25 | views: 26 | permission: 27 | notice: 28 | state_changed: "選択した行を%{state}に変更しました" 29 | reloaded: "再読込しました" 30 | action_item: 31 | reload: "再読込" 32 | 33 | active_admin: 34 | batch_actions: 35 | labels: 36 | can: "有効にする" 37 | cannot: "無効にする" 38 | -------------------------------------------------------------------------------- /gemfiles/5.0.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "appraisal" 6 | gem "devise" 7 | gem "jquery-ui-rails", "~> 4.2.1" 8 | gem "rails", "~> 5.0.0" 9 | 10 | group :development, :test do 11 | gem "sqlite3", platforms: :mri 12 | end 13 | 14 | group :development do 15 | gem "onkcop", require: false 16 | end 17 | 18 | group :test do 19 | gem "capybara" 20 | gem "database_cleaner" 21 | gem "poltergeist" 22 | gem "rspec-rails" 23 | gem "shoulda-matchers" 24 | end 25 | 26 | gemspec path: "../" 27 | -------------------------------------------------------------------------------- /gemfiles/5.1.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "appraisal" 6 | gem "devise" 7 | gem "jquery-ui-rails", "~> 4.2.1" 8 | gem "rails", "~> 5.1.0" 9 | 10 | group :development, :test do 11 | gem "sqlite3", platforms: :mri 12 | end 13 | 14 | group :development do 15 | gem "onkcop", require: false 16 | end 17 | 18 | group :test do 19 | gem "capybara" 20 | gem "database_cleaner" 21 | gem "poltergeist" 22 | gem "rspec-rails" 23 | gem "shoulda-matchers" 24 | end 25 | 26 | gemspec path: "../" 27 | -------------------------------------------------------------------------------- /gemfiles/5.2.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "appraisal" 6 | gem "devise" 7 | gem "jquery-ui-rails", "~> 4.2.1" 8 | gem "rails", "~> 5.2.0" 9 | 10 | group :development, :test do 11 | gem "sqlite3", platforms: :mri 12 | end 13 | 14 | group :development do 15 | gem "onkcop", require: false 16 | end 17 | 18 | group :test do 19 | gem "capybara" 20 | gem "database_cleaner" 21 | gem "poltergeist" 22 | gem "rspec-rails" 23 | gem "shoulda-matchers" 24 | end 25 | 26 | gemspec path: "../" 27 | -------------------------------------------------------------------------------- /lib/active_admin_role.rb: -------------------------------------------------------------------------------- 1 | require "active_admin" 2 | 3 | module ActiveAdminRole 4 | def self.configure 5 | yield(config) 6 | end 7 | 8 | def self.config 9 | @config ||= Config.new 10 | end 11 | end 12 | 13 | require "active_admin_role/config" 14 | require "active_admin_role/engine" if defined?(Rails) 15 | -------------------------------------------------------------------------------- /lib/active_admin_role/active_admin/dsl.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdminRole 2 | module ActiveAdmin 3 | module Dsl 4 | def role_changeable 5 | scope(:all, default: true) 6 | 7 | controller.resource_class.roles.each_key(&method(:scope)) 8 | controller.resource_class.roles.each_key do |role| 9 | batch_action "assign as #{role}" do |ids| 10 | formatted_ids = ids - [active_admin_role_current_user.try!(:id).to_s] 11 | resource_class.where(id: formatted_ids).update_all(role: resource_class.roles[role]) 12 | 13 | if Rails::VERSION::MAJOR >= 5 14 | redirect_back fallback_location: admin_root_url, notice: t("views.admin_user.notice.assigned", role: role) 15 | else 16 | redirect_to :back, notice: t("views.admin_user.notice.assigned", role: role) 17 | end 18 | end 19 | end 20 | end 21 | end 22 | end 23 | end 24 | 25 | ::ActiveAdmin::DSL.send :include, ActiveAdminRole::ActiveAdmin::Dsl if defined?(::ActiveAdmin) 26 | -------------------------------------------------------------------------------- /lib/active_admin_role/active_admin/resource_controller.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdminRole 2 | module ActiveAdmin 3 | module ResourceController 4 | def self.included(klass) 5 | klass.class_eval do 6 | if Rails::VERSION::MAJOR >= 4 7 | before_action :authorize_access_resource!, except: %i[index new create show edit update destroy] 8 | else 9 | before_filter :authorize_access_resource!, except: %i[index new create show edit update destroy] 10 | end 11 | end 12 | end 13 | 14 | private 15 | 16 | def authorize_access_resource! 17 | authorize_resource!(active_admin_config.resource_class) 18 | end 19 | 20 | def active_admin_role_current_user 21 | send(active_admin_role_current_user_method_name) 22 | end 23 | 24 | def active_admin_role_current_user_method_name 25 | ActiveAdminRole.config.current_user_method_name 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/active_admin_role/config.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdminRole 2 | class Config 3 | attr_accessor :roles, :super_user_roles, :guest_user_roles, :user_class_name, :current_user_method_name 4 | attr_reader :default_state 5 | 6 | def initialize 7 | @roles = { guest: 0, support: 1, staff: 2, manager: 3, admin: 99 } 8 | @guest_user_roles = [:guest] 9 | @super_user_roles = [:admin] 10 | @user_class_name = "AdminUser" 11 | @default_state = :cannot 12 | @current_user_method_name = "current_admin_user" 13 | end 14 | 15 | def default_state=(value) 16 | @default_state = (value.to_s == "can") ? :can : :cannot 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/active_admin_role/dsl.rb: -------------------------------------------------------------------------------- 1 | require "active_admin_role/role_based_authorizable" 2 | 3 | module ActiveAdminRole 4 | module Dsl 5 | def role_based_authorizable 6 | include ::ActiveAdminRole::RoleBasedAuthorizable 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/active_admin_role/engine.rb: -------------------------------------------------------------------------------- 1 | require "rails/engine" 2 | require "active_admin_role/dsl" 3 | require "active_admin_role/active_admin/dsl" 4 | 5 | module ActiveAdminRole 6 | class Engine < ::Rails::Engine 7 | initializer "active_admin_role" do 8 | ActiveSupport.on_load :active_record do 9 | extend ActiveAdminRole::Dsl 10 | end 11 | 12 | ActiveSupport.on_load :after_initialize do 13 | require "active_admin_role/active_admin/resource_controller" 14 | ::ActiveAdmin::ResourceController.send :include, ActiveAdminRole::ActiveAdmin::ResourceController 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/active_admin_role/role_based_authorizable.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdminRole 2 | module RoleBasedAuthorizable 3 | extend ActiveSupport::Concern 4 | 5 | included do 6 | enum role: config.roles 7 | delegate :super_user_roles, :guest_user_roles, to: :class 8 | validates :role, presence: true 9 | end 10 | 11 | def super_user? 12 | role.in?(super_user_roles) 13 | end 14 | 15 | def guest_user? 16 | role.in?(guest_user_roles) 17 | end 18 | 19 | class_methods do 20 | def manageable_roles 21 | @manageable_roles ||= roles.except(*manageless_roles) 22 | end 23 | 24 | def super_user_roles 25 | @super_user_roles ||= config.super_user_roles.try(:map, &:to_s) || [] 26 | end 27 | 28 | def guest_user_roles 29 | @guest_user_roles ||= config.guest_user_roles.try(:map, &:to_s) || [] 30 | end 31 | 32 | private 33 | 34 | def manageless_roles 35 | (super_user_roles + guest_user_roles).flatten.compact 36 | end 37 | 38 | def config 39 | ::ActiveAdminRole.config 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/active_admin_role/version.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdminRole 2 | VERSION = "0.2.2".freeze 3 | end 4 | -------------------------------------------------------------------------------- /lib/generators/active_admin_role/USAGE: -------------------------------------------------------------------------------- 1 | Description: 2 | Generates the necessary files to get you up and running with ActiveAdminRole gem 3 | 4 | Examples: 5 | rails generate active_admin_role:install 6 | 7 | This will generate the core migration file, the initializer file and the 'AdminUser' model class. 8 | 9 | rails generate active_admin_role:install --model User 10 | -------------------------------------------------------------------------------- /lib/generators/active_admin_role/helper.rb: -------------------------------------------------------------------------------- 1 | module ActiveAdminRole 2 | module Generators 3 | module Helper 4 | def self.included(klass) 5 | klass.send :extend, ClassMethods 6 | end 7 | 8 | private 9 | 10 | def model_class_name 11 | options[:model] ? options[:model].classify : "AdminUser" 12 | end 13 | 14 | def model_file_path 15 | model_name.underscore 16 | end 17 | 18 | def model_path 19 | @model_path ||= File.join("app", "models", "#{model_file_path}.rb") 20 | end 21 | 22 | def namespace 23 | Rails::Generators.namespace if Rails::Generators.respond_to?(:namespace) 24 | end 25 | 26 | def namespaced? 27 | !!namespace 28 | end 29 | 30 | def model_name 31 | if namespaced? 32 | [namespace.to_s] + [model_class_name] 33 | else 34 | [model_class_name] 35 | end.join("::") 36 | end 37 | 38 | def inject_into_model 39 | indents = " " * (namespaced? ? 2 : 1) 40 | inject_into_class model_path, model_class_name, "#{indents}role_based_authorizable\n" 41 | end 42 | 43 | def migration_class_name 44 | if Rails::VERSION::MAJOR >= 5 45 | "ActiveRecord::Migration[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]" 46 | else 47 | "ActiveRecord::Migration" 48 | end 49 | end 50 | 51 | module ClassMethods 52 | # Define the next_migration_number method (necessary for the migration_template method to work) 53 | def next_migration_number(dirname) 54 | if ActiveRecord::Base.timestamped_migrations 55 | sleep 1 # make sure each time we get a different timestamp 56 | Time.new.utc.strftime("%Y%m%d%H%M%S") 57 | else 58 | "%.3d" % (current_migration_number(dirname) + 1) 59 | end 60 | end 61 | end 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /lib/generators/active_admin_role/install_generator.rb: -------------------------------------------------------------------------------- 1 | require "rails/generators/migration" 2 | require "generators/active_admin_role/helper" 3 | 4 | module ActiveAdminRole 5 | module Generators 6 | class InstallGenerator < ::Rails::Generators::Base 7 | include Rails::Generators::Migration 8 | include ActiveAdminRole::Generators::Helper 9 | source_root File.expand_path("./templates", __dir__) 10 | class_option :model, optional: true, 11 | type: :string, 12 | banner: "model", 13 | desc: "Specify the model class name if you will use anything other than `AdminUser`", 14 | default: "AdminUser" 15 | 16 | def copy_initializer_file 17 | template "initializer.tt", "config/initializers/active_admin_role.rb" 18 | end 19 | 20 | def configure_model 21 | inject_into_model 22 | end 23 | 24 | def copy_migration_files 25 | migration_template "migration/add_role_to_admin_users.tt", 26 | "db/migrate/add_role_to_#{model_class_name.tableize}.rb", 27 | migration_class_name: migration_class_name 28 | migration_template "migration/create_active_admin_managed_resources.tt", 29 | "db/migrate/create_active_admin_managed_resources.rb", 30 | migration_class_name: migration_class_name 31 | migration_template "migration/create_active_admin_permissions.tt", 32 | "db/migrate/create_active_admin_permissions.rb", 33 | migration_class_name: migration_class_name 34 | end 35 | 36 | def copy_model_file 37 | template "model/ability.tt", "app/models/ability.rb" 38 | end 39 | 40 | def configure_active_admin 41 | gsub_file "config/initializers/active_admin.rb", 42 | "# config.authorization_adapter = ActiveAdmin::CanCanAdapter", 43 | "config.authorization_adapter = ActiveAdmin::CanCanAdapter" 44 | end 45 | 46 | def copy_admin_permissions_file 47 | template "admin/permissions.tt", "app/admin/permissions.rb" 48 | end 49 | 50 | def configure_admin_users_file 51 | inject_into_file "app/admin/#{model_class_name.tableize}.rb", 52 | " role_changeable\n", 53 | after: "ActiveAdmin.register #{model_class_name} do\n" 54 | end 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /lib/generators/active_admin_role/templates/admin/permissions.tt: -------------------------------------------------------------------------------- 1 | ActiveAdmin.register ::ActiveAdmin::Permission, as: "Permission" do 2 | actions :index 3 | 4 | filter :state, as: :select, collection: controller.resource_class.states 5 | 6 | filter :managed_resource_action_equals, as: :select, 7 | label: ::ActiveAdmin::ManagedResource.human_attribute_name(:action), 8 | <%- if Rails::VERSION::MAJOR >= 5 -%> 9 | collection: -> { ::ActiveAdmin::ManagedResource.distinct.order(:action).pluck(:action) } 10 | <%- else -%> 11 | collection: -> { ::ActiveAdmin::ManagedResource.uniq.order(:action).pluck(:action) } 12 | <%- end -%> 13 | 14 | filter :managed_resource_name_equals, as: :select, 15 | label: ::ActiveAdmin::ManagedResource.human_attribute_name(:name), 16 | <%- if Rails::VERSION::MAJOR >= 5 -%> 17 | collection: -> { ::ActiveAdmin::ManagedResource.distinct.pluck(:name).sort } 18 | <%- else -%> 19 | collection: -> { ::ActiveAdmin::ManagedResource.uniq.pluck(:name).sort } 20 | <%- end -%> 21 | 22 | filter :managed_resource_class_name_equals, as: :select, 23 | label: ::ActiveAdmin::ManagedResource.human_attribute_name(:class_name), 24 | <%- if Rails::VERSION::MAJOR >= 5 -%> 25 | collection: -> { ::ActiveAdmin::ManagedResource.distinct.order(:class_name).pluck(:class_name) } 26 | <%- else -%> 27 | collection: -> { ::ActiveAdmin::ManagedResource.uniq.order(:class_name).pluck(:class_name) } 28 | <%- end -%> 29 | 30 | scope :all, default: true 31 | 32 | controller.resource_class.manageable_roles.each_key(&method(:scope)) 33 | 34 | controller.resource_class.states.each_key do |state| 35 | batch_action state do |ids| 36 | resource_class.clear_cache 37 | resource_class.where(id: ids).update_all(state: resource_class.states[state]) 38 | <%- if Rails::VERSION::MAJOR >= 5 -%> 39 | redirect_back fallback_location: admin_root_url, notice: t("views.permission.notice.state_changed", state: state) 40 | <%- else -%> 41 | redirect_to :back, notice: t("views.permission.notice.state_changed", state: state) 42 | <%- end -%> 43 | end 44 | end 45 | 46 | collection_action :reload, method: :post do 47 | ::ActiveAdmin::ManagedResource.reload 48 | <%- if Rails::VERSION::MAJOR >= 5 -%> 49 | redirect_back(fallback_location: admin_root_url, notice: t("views.permission.notice.reloaded")) 50 | <%- else -%> 51 | redirect_to :back, notice: t("views.permission.notice.reloaded") 52 | <%- end -%> 53 | end 54 | 55 | action_item :reload do 56 | link_to t("views.permission.action_item.reload"), reload_admin_permissions_path, method: :post 57 | end 58 | 59 | includes :managed_resource 60 | 61 | index do 62 | selectable_column 63 | column :role 64 | column(:state) do |record| 65 | status_tag(record.state, class: record.can? ? "completed ok" : "null", label: record.state) 66 | end 67 | column :action 68 | column :name 69 | column :class_name 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /lib/generators/active_admin_role/templates/initializer.tt: -------------------------------------------------------------------------------- 1 | ActiveAdminRole.configure do |config| 2 | # [Required:Hash] 3 | # == Role | default: { guest: 0, support: 1, staff: 2, manager: 3, admin: 99 } 4 | config.roles = { guest: 0, support: 1, staff: 2, manager: 3, admin: 99 } 5 | 6 | # [Optional:Array] 7 | # == Special roles which don't need to manage on database 8 | config.super_user_roles = [:admin] 9 | config.guest_user_roles = [:guest] 10 | 11 | # [Optional:String] 12 | # == User class name | default: 'AdminUser' 13 | config.user_class_name = "<%= model_class_name %>" 14 | 15 | # [Optional:String] 16 | # == method name of #current_user in Controller 17 | config.current_user_method_name = "current_<%= model_class_name.underscore.tr('/', '_') %>" 18 | 19 | # [Optional:Symbol] 20 | # == Default permission | default: :cannot 21 | config.default_state = :cannot 22 | end 23 | -------------------------------------------------------------------------------- /lib/generators/active_admin_role/templates/migration/add_role_to_admin_users.tt: -------------------------------------------------------------------------------- 1 | class AddRoleTo<%= model_class_name.tableize.camelize %> < <%= migration_class_name %> 2 | def change 3 | add_column :<%= model_class_name.tableize %>, :role, :integer, null: false, limit: 1, default: 0 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/generators/active_admin_role/templates/migration/create_active_admin_managed_resources.tt: -------------------------------------------------------------------------------- 1 | class CreateActiveAdminManagedResources < <%= migration_class_name %> 2 | def change 3 | create_table :active_admin_managed_resources do |t| 4 | t.string :class_name, null: false 5 | t.string :action, null: false 6 | t.string :name 7 | 8 | t.timestamp null: false 9 | end 10 | 11 | add_index :active_admin_managed_resources, [:class_name, :action, :name], unique: true, name: "active_admin_managed_resources_index" 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/generators/active_admin_role/templates/migration/create_active_admin_permissions.tt: -------------------------------------------------------------------------------- 1 | class CreateActiveAdminPermissions < <%= migration_class_name %> 2 | def change 3 | create_table :active_admin_permissions do |t| 4 | t.integer :managed_resource_id, null: false 5 | t.integer :role, null: false, limit: 1, default: 0 6 | t.integer :state, null: false, limit: 1, default: 0 7 | 8 | t.timestamp null: false 9 | end 10 | 11 | add_index :active_admin_permissions, [:managed_resource_id, :role], unique: true, name: "active_admin_permissions_index" 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/generators/active_admin_role/templates/model/ability.tt: -------------------------------------------------------------------------------- 1 | class Ability 2 | include CanCan::Ability 3 | include ActiveAdminRole::CanCan::Ability 4 | 5 | def initialize(user) 6 | user ||= <%= model_class_name %>.new 7 | 8 | if user.super_user? 9 | can :manage, :all 10 | else 11 | register_role_based_abilities(user) 12 | end 13 | 14 | # NOTE: Everyone can read the page of Permission Deny 15 | can :read, ActiveAdmin::Page, name: "Dashboard" 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/active_admin_role_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe ActiveAdminRole do 4 | describe ".configure" do 5 | subject { described_class.configure {|config| config.default_state = :cannot } } 6 | it { expect { subject }.not_to raise_error } 7 | end 8 | 9 | describe ".config" do 10 | subject { described_class.config } 11 | it { is_expected.to be_a described_class::Config } 12 | end 13 | 14 | describe "version" do 15 | subject { described_class::VERSION } 16 | it { is_expected.not_to be_nil } 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/config_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe ActiveAdminRole::Config do 4 | describe "#default_state=" do 5 | let(:instance) { described_class.new } 6 | 7 | before { instance.default_state = value } 8 | 9 | subject { instance.default_state } 10 | 11 | context "when value is :can" do 12 | let(:value) { :can } 13 | it { is_expected.to be :can } 14 | end 15 | 16 | context "when value is :cannot" do 17 | let(:value) { :cannot } 18 | it { is_expected.to be :cannot } 19 | end 20 | 21 | context "when value is others" do 22 | let(:value) { 123 } 23 | it { is_expected.to be :cannot } 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /spec/features/admin_user_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe "/admin/admin_users", type: :feature do 4 | before do 5 | prepare_admin_users 6 | login_as :admin 7 | click_link "Admin Users" 8 | end 9 | 10 | describe "scope" do 11 | it "should display scopes" do 12 | within "ul.scopes.table_tools_segmented_control" do 13 | expect(page).to have_link "All", href: admin_admin_users_path(scope: :all) 14 | 15 | AdminUser.roles.each_key do |role| 16 | expect(page).to have_link role, href: admin_admin_users_path(scope: role) 17 | end 18 | end 19 | end 20 | 21 | it "should return scoped collections" do 22 | AdminUser.roles.each_key do |role| 23 | find("li.scope.#{role} a").click 24 | 25 | within "#index_table_admin_users" do 26 | expect(page).to have_css "td.col-email", text: "#{role}@example.com" 27 | end 28 | end 29 | end 30 | end 31 | 32 | describe "batch action", js: true do 33 | before do 34 | find(:css, "tr#admin_user_1 td.col-selectable input").set("1") 35 | click_link "Batch Actions" 36 | click_link "assign as admin Selected" 37 | find("li.scope.admin a").click 38 | end 39 | 40 | it "should assign role to admin" do 41 | within "#index_table_admin_users" do 42 | expect(page).to have_css "td.col-email", text: "guest@example.com" 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /spec/features/integration_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe "integration test", type: :feature do 4 | def prepare_permissions 5 | ActiveAdmin::ManagedResource.reload 6 | ActiveAdmin::Permission.support.joins(:managed_resource).merge( 7 | ActiveAdmin::ManagedResource.where(class_name: "AdminUser"), 8 | ).all.map(&:can!) 9 | ActiveAdmin::Permission.staff.joins(:managed_resource).merge( 10 | ActiveAdmin::ManagedResource.where(class_name: "ActiveAdmin::Permission", action: "read"), 11 | ).all.map(&:can!) 12 | ActiveAdmin::Permission.manager.joins(:managed_resource).merge( 13 | ActiveAdmin::ManagedResource.where(class_name: "ActiveAdmin::Permission"), 14 | ).all.map(&:can!) 15 | end 16 | 17 | before do 18 | prepare_admin_users 19 | prepare_permissions 20 | end 21 | 22 | describe "guest" do 23 | before { login_as :guest } 24 | after { logout } 25 | 26 | specify "guest can only visit Dashboard" do 27 | expect(page).to have_css "ul.header-item#tabs li#dashboard" 28 | expect(page).not_to have_css "ul.header-item#tabs li#admin_users" 29 | expect(page).not_to have_css "ul.header-item#tabs li#permissions" 30 | 31 | visit admin_admin_users_path 32 | expect(page).to have_content "You are not authorized to perform this action." 33 | 34 | visit admin_permissions_path 35 | expect(page).to have_content "You are not authorized to perform this action." 36 | end 37 | end 38 | 39 | describe "support" do 40 | before { login_as :support } 41 | after { logout } 42 | 43 | specify "support can visit Dashboard and Admin Users" do 44 | expect(page).to have_css "ul.header-item#tabs li#dashboard" 45 | expect(page).to have_css "ul.header-item#tabs li#admin_users" 46 | expect(page).not_to have_css "ul.header-item#tabs li#permissions" 47 | 48 | visit admin_admin_users_path 49 | expect(page).not_to have_content "You are not authorized to perform this action." 50 | 51 | visit admin_permissions_path 52 | expect(page).to have_content "You are not authorized to perform this action." 53 | end 54 | end 55 | 56 | describe "staff" do 57 | before { login_as :staff } 58 | after { logout } 59 | 60 | specify "staff cannot execute disallowed action" do 61 | click_link "Permissions" 62 | click_link "Reload" 63 | expect(page).to have_content "You are not authorized to perform this action." 64 | end 65 | end 66 | 67 | describe "manager" do 68 | before { login_as :manager } 69 | after { logout } 70 | 71 | specify "manager can execute allowed action (without CRUD)" do 72 | click_link "Permissions" 73 | click_link "Reload" 74 | expect(page).not_to have_content "You are not authorized to perform this action." 75 | end 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /spec/features/permission_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe "/admin/permissions", type: :feature do 4 | before do 5 | prepare_admin_users 6 | login_as :admin 7 | click_link "Permissions" 8 | end 9 | 10 | describe "reload" do 11 | before { click_link "Reload" } 12 | 13 | it "should be able to reload permissions" do 14 | expect(page).to have_css "table#index_table_permissions" 15 | end 16 | end 17 | 18 | describe "batch action", js: true do 19 | before do 20 | click_link "Reload" 21 | find(:css, "tr#active_admin_permission_35 td.col-selectable input").set("1") 22 | click_link "Batch Actions" 23 | click_link "Enable Selected" 24 | find("li.scope.staff a").click 25 | end 26 | 27 | it "should enable permission" do 28 | within "tr#active_admin_permission_35" do 29 | expect(page).to have_css "td.col-state", text: "CAN" 30 | expect(page).not_to have_css "td.col-state", text: "CANNOT" 31 | end 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /spec/models/manageable_resource_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe ActiveAdmin::ManageableResource, type: :model do 4 | let(:model) { described_class.new } 5 | 6 | describe "#call" do 7 | subject { model.call } 8 | it { is_expected.to be_a Array } 9 | it { is_expected.to all be_a Hash } 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /spec/models/managed_resource_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe ActiveAdmin::ManagedResource, type: :model do 4 | it { is_expected.to have_many(:permissions).dependent(:destroy) } 5 | it { is_expected.to validate_presence_of(:class_name) } 6 | it { is_expected.to validate_presence_of(:action) } 7 | 8 | describe "#const" do 9 | subject { described_class.new(class_name: class_name).const } 10 | 11 | context "when class_name is existing" do 12 | let(:class_name) { "AdminUser" } 13 | it("should be constant") { is_expected.to be AdminUser } 14 | end 15 | 16 | context "when class_name is not existing" do 17 | let(:class_name) { "Foobar" } 18 | it { is_expected.to be_nil } 19 | end 20 | end 21 | 22 | describe "#active?" do 23 | subject { described_class.new(class_name: class_name).active? } 24 | 25 | context "when class_name is existing" do 26 | let(:class_name) { "AdminUser" } 27 | it { is_expected.to be true } 28 | end 29 | 30 | context "when class_name is not existing" do 31 | let(:class_name) { "Foobar" } 32 | it { is_expected.to be false } 33 | end 34 | end 35 | 36 | describe "#for_active_admin_page?" do 37 | subject { described_class.new(class_name: class_name).for_active_admin_page? } 38 | 39 | context "when class_name is ActiveAdmin::Page" do 40 | let(:class_name) { "ActiveAdmin::Page" } 41 | it { is_expected.to be true } 42 | end 43 | 44 | context "when class_name is not ActiveAdmin::Page" do 45 | let(:class_name) { "AdminUser" } 46 | it { is_expected.to be false } 47 | end 48 | end 49 | 50 | describe ".reload" do 51 | subject { described_class.reload } 52 | 53 | it { expect { subject }.not_to raise_error } 54 | it("should create ActiveAdmin::ManagedResource") { expect { subject }.to change { described_class.count } } 55 | it("should create ActiveAdmin::Permission") { expect { subject }.to change { ActiveAdmin::Permission.count } } 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /spec/models/permission_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe ActiveAdmin::Permission, type: :model do 4 | describe "association" do 5 | it { is_expected.to belong_to(:managed_resource) } 6 | end 7 | 8 | describe "delegation" do 9 | it { is_expected.to delegate_method(:class_name).to(:managed_resource) } 10 | it { is_expected.to delegate_method(:action).to(:managed_resource) } 11 | it { is_expected.to delegate_method(:name).to(:managed_resource) } 12 | it { is_expected.to delegate_method(:const).to(:managed_resource) } 13 | it { is_expected.to delegate_method(:active?).to(:managed_resource) } 14 | it { is_expected.to delegate_method(:for_active_admin_page?).to(:managed_resource) } 15 | end 16 | 17 | describe "validation" do 18 | let(:managed_resource) { ActiveAdmin::ManagedResource.create!(class_name: "AdminUser", action: "create", name: "admin_user") } 19 | let!(:model) { described_class.create!(managed_resource: managed_resource) } 20 | 21 | it { is_expected.to validate_presence_of(:role) } 22 | it { is_expected.to validate_presence_of(:state) } 23 | it { is_expected.to validate_uniqueness_of(:managed_resource_id).scoped_to(:role) } 24 | end 25 | 26 | describe "enum" do 27 | it { is_expected.to define_enum_for(:state).with(cannot: 0, can: 1) } 28 | it { is_expected.to define_enum_for(:role).with(ActiveAdminRole.config.roles) } 29 | end 30 | 31 | describe "callback" do 32 | let(:managed_resource) { ActiveAdmin::ManagedResource.create!(class_name: "AdminUser", action: "create", name: "admin_user") } 33 | let(:model) { described_class.create!(managed_resource: managed_resource) } 34 | 35 | it "should run callback #clear_cache after update" do 36 | expect(model.class).to receive(:clear_cache).once 37 | model.can! 38 | end 39 | end 40 | 41 | describe "#to_condition" do 42 | let(:model) { described_class.create(managed_resource: managed_resource) } 43 | subject { model.to_condition } 44 | 45 | context "when managed_resource is for_active_admin_page" do 46 | let(:managed_resource) do 47 | ActiveAdmin::ManagedResource.create( 48 | class_name: "ActiveAdmin::Page", 49 | name: "Dashboard", 50 | action: "read", 51 | ) 52 | end 53 | let(:result) { [model.state, model.action.to_sym, model.const, { name: model.name }] } 54 | 55 | it { is_expected.to eq result } 56 | end 57 | 58 | context "when managed_resource is not for_active_admin_page" do 59 | let(:managed_resource) do 60 | ActiveAdmin::ManagedResource.create( 61 | class_name: "ActiveAdmin::Permission", 62 | name: "Permission", 63 | action: "read", 64 | ) 65 | end 66 | let(:result) { [model.state, model.action.to_sym, model.const] } 67 | 68 | it { is_expected.to eq result } 69 | end 70 | end 71 | 72 | describe "ClassMethods" do 73 | before do 74 | %i[read create update destroy].map do |action| 75 | ActiveAdmin::ManagedResource.create( 76 | class_name: "ActiveAdmin::Permission", 77 | name: "Permission", 78 | action: action, 79 | ) 80 | end 81 | end 82 | 83 | describe ".update_all_from_managed_resources" do 84 | subject { described_class.update_all_from_managed_resources } 85 | it { expect { subject }.to change { described_class.count }.to(12).from(0) } 86 | end 87 | 88 | describe ".indexed_cache" do 89 | before { described_class.update_all_from_managed_resources } 90 | subject { described_class.indexed_cache } 91 | it { is_expected.to be_a Hash } 92 | end 93 | 94 | describe ".clear_cache" do 95 | before do 96 | described_class.update_all_from_managed_resources 97 | described_class.indexed_cache 98 | end 99 | 100 | subject { described_class.clear_cache } 101 | 102 | it "should clear cache" do 103 | expect(described_class.instance_variable_get(:@indexed_cache)).to be_a Hash 104 | subject 105 | expect(described_class.instance_variable_get(:@indexed_cache)).to be_nil 106 | end 107 | end 108 | end 109 | end 110 | -------------------------------------------------------------------------------- /spec/models/role_based_authorizable_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe ActiveAdminRole::RoleBasedAuthorizable, type: :model do 4 | describe ".included" do 5 | subject { AdminUser.new } 6 | 7 | it { is_expected.to define_enum_for(:role).with(ActiveAdminRole.config.roles) } 8 | it { is_expected.to delegate_method(:super_user_roles).to(:class) } 9 | it { is_expected.to delegate_method(:guest_user_roles).to(:class) } 10 | it { is_expected.to validate_presence_of(:role) } 11 | end 12 | 13 | describe "#super_user?" do 14 | let(:model) { AdminUser.new(role: role) } 15 | subject { model.super_user? } 16 | 17 | context "when role is super user role" do 18 | let(:role) { :admin } 19 | it { is_expected.to be true } 20 | end 21 | 22 | context "when role is not super user role" do 23 | let(:role) { :guest } 24 | it { is_expected.to be false } 25 | end 26 | end 27 | 28 | describe "#guest_user?" do 29 | let(:model) { AdminUser.new(role: role) } 30 | subject { model.guest_user? } 31 | 32 | context "when role is guest user role" do 33 | let(:role) { :guest } 34 | it { is_expected.to be true } 35 | end 36 | 37 | context "when role is not guest user role" do 38 | let(:role) { :admin } 39 | it { is_expected.to be false } 40 | end 41 | end 42 | 43 | describe ".manageable_roles" do 44 | let(:result) do 45 | manageless_roles = (ActiveAdminRole.config.super_user_roles + ActiveAdminRole.config.guest_user_roles).flatten.compact.map(&:to_s) 46 | ActiveAdminRole.config.roles.stringify_keys.except(*manageless_roles) 47 | end 48 | 49 | subject { AdminUser.manageable_roles } 50 | it { is_expected.to eq result } 51 | end 52 | 53 | describe ".super_user_roles" do 54 | subject { AdminUser.super_user_roles } 55 | it { is_expected.to be_a Array } 56 | end 57 | 58 | describe ".guest_user_roles" do 59 | subject { AdminUser.guest_user_roles } 60 | it { is_expected.to be_a Array } 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 2 | $LOAD_PATH << File.expand_path("support", __dir__) 3 | 4 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 5 | 6 | require "bundler" 7 | Bundler.setup 8 | 9 | ENV["RAILS_ENV"] = "test" 10 | 11 | # == Ensure the Active Admin load path is happy 12 | require "rails" 13 | 14 | ENV["RAILS"] = Rails.version 15 | ENV["RAILS_ROOT"] = File.expand_path("../tmp/rails-#{ENV["RAILS"]}", __dir__) 16 | 17 | # == Create the test app if it doesn't exists 18 | system "rake setup" unless File.exist?(ENV["RAILS_ROOT"]) 19 | 20 | # == Require ActiveRecord to ensure that Ransack loads correctly 21 | require "active_record" 22 | require "active_admin" 23 | 24 | ActiveAdmin.application.load_paths = [ENV["RAILS_ROOT"] + "/app/admin"] 25 | 26 | # == Load test app 27 | require ENV["RAILS_ROOT"] + "/config/environment.rb" 28 | 29 | # == RSpec 30 | require "rspec/rails" 31 | 32 | RSpec.configure do |config| 33 | config.expect_with :rspec do |expectations| 34 | expectations.syntax = [:should, :expect] 35 | end 36 | 37 | config.mock_with :rspec do |mocks| 38 | mocks.syntax = [:should, :expect] 39 | end 40 | 41 | config.order = :random 42 | config.use_transactional_fixtures = false 43 | 44 | config.before(:suite) do 45 | DatabaseCleaner.strategy = :truncation 46 | DatabaseCleaner.clean_with(:truncation) 47 | end 48 | 49 | config.before(:each) do 50 | DatabaseCleaner.strategy = :truncation 51 | DatabaseCleaner.start 52 | end 53 | 54 | config.after(:each) do 55 | DatabaseCleaner.clean 56 | end 57 | end 58 | 59 | # == Capybara 60 | require "capybara/rails" 61 | require "capybara/rspec" 62 | require "capybara/poltergeist" 63 | 64 | Capybara.register_driver :poltergeist do |app| 65 | Capybara::Poltergeist::Driver.new(app, js_errors: true, 66 | timeout: 80, 67 | debug: false, 68 | phantomjs_options: ["--debug=no", "--load-images=no"]) 69 | end 70 | 71 | Capybara.javascript_driver = :poltergeist 72 | 73 | # == Shoulda Matchers 74 | require "shoulda-matchers" 75 | 76 | Shoulda::Matchers.configure do |config| 77 | config.integrate do |with| 78 | with.test_framework :rspec 79 | with.library :active_record 80 | with.library :active_model 81 | with.library :rails 82 | end 83 | end 84 | 85 | # == Support 86 | def prepare_admin_users 87 | AdminUser.roles.each_key do |role| 88 | AdminUser.find_or_create_by(email: "#{role}@example.com", role: role) do |admin_user| 89 | admin_user.password = "password" 90 | admin_user.password_confirmation = "password" 91 | end 92 | end 93 | end 94 | 95 | def login_as(role) 96 | visit root_path 97 | fill_in "admin_user_email", with: "#{role}@example.com" 98 | fill_in "admin_user_password", with: "password" 99 | click_button "Login" 100 | end 101 | 102 | def logout 103 | click_link "Logout" 104 | end 105 | -------------------------------------------------------------------------------- /spec/support/rails_template.rb: -------------------------------------------------------------------------------- 1 | run "rm Gemfile" 2 | 3 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 4 | 5 | # == Prepare Active Admin 6 | generate :"active_admin:install" 7 | 8 | # == Prepare Active Admin Role 9 | generate :"active_admin_role:install" 10 | 11 | run "rm -r spec" 12 | run "rm -r test" 13 | 14 | route "root to: 'admin/dashboard#index'" 15 | 16 | rake "db:migrate" 17 | --------------------------------------------------------------------------------