hydra-role-management 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.circleci/config.yml +68 -0
- data/.rubocop.yml +50 -0
- data/CONTRIBUTING.md +22 -20
- data/Gemfile +3 -3
- data/README.md +33 -1
- data/Rakefile +14 -17
- data/SUPPORT.md +6 -0
- data/app/controllers/concerns/hydra/role_management/roles_behavior.rb +15 -16
- data/app/controllers/concerns/hydra/role_management/user_roles_behavior.rb +9 -8
- data/app/controllers/roles_controller.rb +2 -1
- data/app/controllers/user_roles_controller.rb +2 -2
- data/app/models/concerns/hydra/role_management/legacy_attribute_handling.rb +2 -0
- data/app/models/concerns/hydra/role_management/user_roles.rb +5 -4
- data/app/models/role.rb +8 -5
- data/app/views/roles/edit.html.erb +1 -1
- data/config/routes.rb +2 -2
- data/hydra-role-management.gemspec +19 -14
- data/lib/generators/roles/roles_generator.rb +30 -32
- data/lib/generators/roles/templates/hydra_role_management_rails3.rb +2 -1
- data/lib/generators/roles/templates/migrations/user_roles.rb +3 -2
- data/lib/hydra-role-management.rb +9 -3
- data/lib/hydra/role_management.rb +4 -2
- data/lib/hydra/role_management/version.rb +2 -1
- data/spec/controllers/roles_controller_spec.rb +27 -30
- data/spec/controllers/user_roles_controller_spec.rb +15 -13
- data/spec/lib/user_roles_spec.rb +21 -21
- data/spec/models/role_spec.rb +22 -20
- data/spec/routing/role_management_routes_spec.rb +29 -44
- data/spec/spec_helper.rb +4 -2
- data/spec/test_app_templates/app/models/sample.rb +7 -8
- data/spec/test_app_templates/app/models/solr_document.rb +2 -2
- data/spec/test_app_templates/config/initializers/hydra_config.rb +6 -6
- data/spec/test_app_templates/lib/generators/test_app_generator.rb +6 -5
- metadata +88 -17
- data/.travis.yml +0 -11
@@ -1,30 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Hydra
|
2
3
|
module RoleManagement
|
4
|
+
# Module offering methods for user behavior managing roles and groups
|
3
5
|
module UserRoles
|
4
6
|
extend ActiveSupport::Concern
|
7
|
+
|
5
8
|
included do
|
6
9
|
has_and_belongs_to_many :roles
|
7
10
|
end
|
8
11
|
|
9
12
|
def groups
|
10
13
|
g = roles.map(&:name)
|
11
|
-
g += ['registered'] unless new_record? || guest?
|
14
|
+
g += ['registered'] unless new_record? || guest?
|
12
15
|
g
|
13
16
|
end
|
14
17
|
|
15
18
|
def guest?
|
16
19
|
if defined?(DeviseGuests)
|
17
|
-
|
20
|
+
self[:guest]
|
18
21
|
else
|
19
22
|
false
|
20
23
|
end
|
21
24
|
end
|
22
|
-
|
23
25
|
|
24
26
|
def admin?
|
25
27
|
roles.where(name: 'admin').exists?
|
26
28
|
end
|
27
|
-
|
28
29
|
end
|
29
30
|
end
|
30
31
|
end
|
data/app/models/role.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# Class modeling Roles within the application
|
1
3
|
class Role < ActiveRecord::Base
|
2
4
|
has_and_belongs_to_many :users
|
3
5
|
|
4
|
-
validates :name,
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
validates :name,
|
7
|
+
uniqueness: true,
|
8
|
+
format: {
|
9
|
+
with: /\A[a-zA-Z0-9._-]+\z/,
|
10
|
+
message: 'Only letters, numbers, hyphens, underscores and periods are allowed'
|
11
|
+
}
|
9
12
|
end
|
@@ -13,7 +13,7 @@
|
|
13
13
|
<% @role.users.each do |user| %>
|
14
14
|
<li><%= user.user_key %>
|
15
15
|
<% if can? :remove_user, Role %>
|
16
|
-
<%= button_to t('role-management.edit.remove'), role_management.role_user_path(@role, user), :method=>:delete, :class=>'btn btn-danger' %>
|
16
|
+
<%= button_to t('role-management.edit.remove'), role_management.role_user_path(@role, user.id), :method=>:delete, :class=>'btn btn-danger' %>
|
17
17
|
<% end %>
|
18
18
|
</li>
|
19
19
|
<% end %>
|
data/config/routes.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
Hydra::RoleManagement::Engine.routes.draw do
|
2
3
|
# Generic file routes
|
3
4
|
resources :roles, Hydra::RoleManagement.route_options do
|
4
|
-
resources :users, :
|
5
|
+
resources :users, only: [:create, :destroy], controller: "user_roles"
|
5
6
|
end
|
6
7
|
end
|
7
|
-
|
@@ -1,27 +1,32 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
require File.expand_path('../lib/hydra/role_management/version', __FILE__)
|
3
3
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
|
-
gem.authors = [
|
6
|
-
gem.email = [
|
7
|
-
gem.description =
|
8
|
-
gem.summary =
|
9
|
-
gem.homepage =
|
5
|
+
gem.authors = ['Justin Coyne']
|
6
|
+
gem.email = ['justin@curationexperts.com']
|
7
|
+
gem.description = 'Rails engine to do user roles in an RDBMS for hydra-head'
|
8
|
+
gem.summary = 'Rails engine to do user roles in an RDBMS for hydra-head'
|
9
|
+
gem.homepage = 'https://github.com/samvera/hydra-role-management'
|
10
10
|
|
11
|
-
gem.files = `git ls-files`.split(
|
12
|
-
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
11
|
+
gem.files = `git ls-files`.split($OUTPUT_RECORD_SEPARATOR)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
13
13
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
-
gem.name =
|
15
|
-
gem.require_paths = [
|
14
|
+
gem.name = 'hydra-role-management'
|
15
|
+
gem.require_paths = ['lib']
|
16
16
|
gem.version = Hydra::RoleManagement::VERSION
|
17
17
|
gem.license = 'Apache 2.0'
|
18
18
|
|
19
|
-
gem.add_dependency 'bootstrap_form'
|
20
19
|
gem.add_dependency 'blacklight'
|
20
|
+
gem.add_dependency 'bootstrap_form'
|
21
|
+
gem.add_dependency 'bundler', '>= 1.5'
|
21
22
|
gem.add_dependency 'cancancan'
|
23
|
+
gem.add_dependency 'json', '~> 1.8'
|
24
|
+
gem.add_development_dependency 'bixby', '~> 1.0.0'
|
25
|
+
gem.add_development_dependency 'engine_cart', '~> 2.1'
|
26
|
+
gem.add_development_dependency 'pry-byebug'
|
27
|
+
gem.add_development_dependency 'rails-controller-testing', '~> 0'
|
22
28
|
gem.add_development_dependency 'rake'
|
23
|
-
gem.add_development_dependency 'rspec-rails'
|
24
29
|
gem.add_development_dependency 'rspec-its'
|
25
|
-
gem.add_development_dependency 'rails
|
26
|
-
gem.add_development_dependency '
|
30
|
+
gem.add_development_dependency 'rspec-rails'
|
31
|
+
gem.add_development_dependency 'rspec_junit_formatter'
|
27
32
|
end
|
@@ -1,27 +1,29 @@
|
|
1
|
-
|
1
|
+
|
2
|
+
# frozen_string_literal: true
|
2
3
|
require 'rails/generators'
|
3
4
|
require 'rails/generators/migration'
|
4
5
|
|
6
|
+
# Class definition for the Rails Generator integrating Roles
|
5
7
|
class RolesGenerator < Rails::Generators::Base
|
6
8
|
include Rails::Generators::Migration
|
7
9
|
|
8
10
|
source_root File.expand_path('../templates', __FILE__)
|
9
11
|
|
10
|
-
argument
|
11
|
-
desc
|
12
|
+
argument :model_name, type: :string, default: 'user'
|
13
|
+
desc '
|
12
14
|
This generator makes the following changes to your application:
|
13
15
|
1. Creates several database migrations if they do not exist in /db/migrate
|
14
16
|
2. Adds user behavior to the user model
|
15
17
|
2. Adds routes
|
16
|
-
|
18
|
+
'
|
17
19
|
|
18
20
|
# Implement the required interface for Rails::Generators::Migration.
|
19
21
|
# taken from http://github.com/rails/rails/blob/master/activerecord/lib/generators/active_record.rb
|
20
|
-
def self.next_migration_number(
|
21
|
-
|
22
|
-
@prev_migration_nr = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i
|
23
|
-
else
|
22
|
+
def self.next_migration_number(_path)
|
23
|
+
if @prev_migration_nr
|
24
24
|
@prev_migration_nr += 1
|
25
|
+
else
|
26
|
+
@prev_migration_nr = Time.now.utc.strftime('%Y%m%d%H%M%S').to_i
|
25
27
|
end
|
26
28
|
@prev_migration_nr.to_s
|
27
29
|
end
|
@@ -29,7 +31,7 @@ This generator makes the following changes to your application:
|
|
29
31
|
# Setup the database migrations
|
30
32
|
def copy_migrations
|
31
33
|
# Can't get this any more DRY, because we need this order.
|
32
|
-
%w
|
34
|
+
%w[user_roles.rb].each do |f|
|
33
35
|
better_migration_template f
|
34
36
|
end
|
35
37
|
end
|
@@ -37,49 +39,45 @@ This generator makes the following changes to your application:
|
|
37
39
|
# Add behaviors to the user model
|
38
40
|
def inject_user_roles_behavior
|
39
41
|
file_path = "app/models/#{model_name.underscore}.rb"
|
40
|
-
if File.
|
41
|
-
place_marker = if File.read(file_path).match(/include Hydra::User/)
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
42
|
+
if File.exist?(file_path)
|
43
|
+
place_marker = if File.read(file_path).match?(/include Hydra::User/)
|
44
|
+
/include Hydra::User/
|
45
|
+
elsif File.read(file_path).match?(/include Blacklight::User/)
|
46
|
+
/include Blacklight::User/
|
47
|
+
end
|
46
48
|
if place_marker
|
47
|
-
code = "\n # Connects this user object to Role-management behaviors.\n"
|
48
|
-
|
49
|
-
inject_into_file file_path, code,
|
49
|
+
code = "\n # Connects this user object to Role-management behaviors.\n" \
|
50
|
+
" include Hydra::RoleManagement::UserRoles\n\n"
|
51
|
+
inject_into_file file_path, code, after: place_marker
|
50
52
|
else
|
51
|
-
|
53
|
+
Rails.logger.error " \e[31mFailure\e[0m Hydra::User is not included in #{file_path}. Add 'include Hydra::User' and rerun."
|
52
54
|
end
|
53
55
|
else
|
54
|
-
|
56
|
+
Rails.logger.error " \e[31mFailure\e[0m hydra-role-management requires a user object. This generators assumes that the model is defined in the file #{file_path}, which does not exist. If you used a different name, please re-run the generator and provide that name as an argument. Such as \b rails -g roles client"
|
55
57
|
end
|
56
58
|
end
|
57
59
|
|
58
60
|
# The engine routes have to come after the devise routes so that /users/sign_in will work
|
59
61
|
def inject_routes
|
60
62
|
routing_code = "mount Hydra::RoleManagement::Engine => '/'"
|
61
|
-
sentinel = /devise_for :users
|
62
|
-
inject_into_file 'config/routes.rb', "\n #{routing_code}\n",
|
63
|
+
sentinel = /devise_for :users(.*)$/
|
64
|
+
inject_into_file 'config/routes.rb', "\n #{routing_code}\n", after: sentinel, verbose: false
|
63
65
|
end
|
64
66
|
|
65
67
|
# If this gem is installed under Rails 3, an attr_accessible method is required for the Role model. This
|
66
68
|
# file will be added to config/initializers and the correct code will be added to the model at runtime.
|
67
69
|
def rails3_attr_accessible
|
68
|
-
if
|
69
|
-
|
70
|
-
|
71
|
-
end
|
70
|
+
return if ActionController.const_defined? :StrongParameters
|
71
|
+
Rails.logger.info 'Role model will include attr_accessible :name because you are installing this gem in a Rails 3 app.'
|
72
|
+
copy_file 'hydra_role_management_rails3.rb', 'config/initializers/hydra_role_management_rails3.rb'
|
72
73
|
end
|
73
74
|
|
74
75
|
private
|
75
76
|
|
76
|
-
|
77
|
-
begin
|
77
|
+
def better_migration_template(file)
|
78
78
|
sleep 1 # ensure scripts have different time stamps
|
79
79
|
migration_template "migrations/#{file}", "db/migrate/#{file}"
|
80
|
-
rescue
|
81
|
-
|
80
|
+
rescue StandardError
|
81
|
+
Rails.logger.error " \e[1m\e[34mMigrations\e[0m " + $ERROR_INFO.message
|
82
82
|
end
|
83
|
-
end
|
84
|
-
|
85
83
|
end
|
@@ -1 +1,2 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
Role.send :include, Hydra::RoleManagement::LegacyAttributeHandling
|
@@ -1,9 +1,10 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
class UserRoles < ActiveRecord::Migration[5.0]
|
2
3
|
def up
|
3
4
|
create_table :roles do |t|
|
4
5
|
t.string :name
|
5
6
|
end
|
6
|
-
create_table :roles_users, :
|
7
|
+
create_table :roles_users, id: false do |t|
|
7
8
|
t.references :role
|
8
9
|
t.references :user
|
9
10
|
end
|
@@ -1,12 +1,18 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'hydra/role_management'
|
2
3
|
require 'bootstrap_form'
|
3
4
|
|
4
5
|
module Hydra
|
5
6
|
module RoleManagement
|
6
7
|
mattr_accessor :route_options
|
7
8
|
self.route_options = {}
|
8
|
-
|
9
|
-
|
9
|
+
|
10
|
+
# Draws the routes with custom arguments passed to the #mount invocation
|
11
|
+
# @param router [ActionDispatch::Routing::Mapper] the Rails routing mapper
|
12
|
+
# @param opts [Hash] the argument passed to ActionDispatch::Routing::Mapper#mount
|
13
|
+
# @see http://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Base.html
|
14
|
+
# (see ActionDispatch::Routing::Mapper::Base)
|
15
|
+
def self.draw_routes(router, opts = {})
|
10
16
|
self.route_options = opts
|
11
17
|
router.instance_exec do
|
12
18
|
mount Hydra::RoleManagement::Engine => '/'
|
@@ -1,12 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'cancan'
|
2
3
|
module Hydra
|
3
4
|
module RoleManagement
|
5
|
+
# Class definition for the Rails Engine
|
4
6
|
class Engine < ::Rails::Engine
|
5
7
|
engine_name 'role_management'
|
6
8
|
|
7
9
|
# Rails 4 should do this automatically:
|
8
|
-
config.paths.add
|
9
|
-
config.paths.add
|
10
|
+
config.paths.add 'app/controllers/concerns', eager_load: true
|
11
|
+
config.paths.add 'app/models/concerns', eager_load: true
|
10
12
|
end
|
11
13
|
end
|
12
14
|
end
|
@@ -1,51 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
describe RolesController do
|
3
|
+
routes { Hydra::RoleManagement::Engine.routes }
|
2
4
|
let(:ability) do
|
3
5
|
ability = Object.new
|
4
6
|
ability.extend(CanCan::Ability)
|
5
7
|
allow(controller).to receive(:current_ability).and_return(ability)
|
6
8
|
ability
|
7
9
|
end
|
8
|
-
|
9
10
|
let(:role) do
|
10
11
|
Role.create(name: 'foo')
|
11
12
|
end
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
end
|
16
|
-
|
17
|
-
describe "with a user who cannot edit roles" do
|
18
|
-
it "should not be able to view role index" do
|
14
|
+
describe 'with a user who cannot edit roles' do
|
15
|
+
it 'is not able to view role index' do
|
19
16
|
expect { get :index, params: {} }.to raise_error CanCan::AccessDenied
|
20
17
|
end
|
21
|
-
it
|
18
|
+
it 'is not able to view role' do
|
22
19
|
expect { get :show, params: { id: role } }.to raise_error CanCan::AccessDenied
|
23
20
|
end
|
24
|
-
it
|
21
|
+
it 'is not able to view new role form' do
|
25
22
|
expect { get :new }.to raise_error CanCan::AccessDenied
|
26
23
|
end
|
27
|
-
it
|
24
|
+
it 'is not able to create a role' do
|
28
25
|
expect { post :create, params: { role: { name: 'my_role' } } }.to raise_error CanCan::AccessDenied
|
29
26
|
end
|
30
|
-
it
|
27
|
+
it 'is not able to update a role' do
|
31
28
|
expect { put :update, params: { id: role } }.to raise_error CanCan::AccessDenied
|
32
29
|
end
|
33
|
-
it
|
30
|
+
it 'is not able to remove a role' do
|
34
31
|
expect { delete :destroy, params: { id: role } }.to raise_error CanCan::AccessDenied
|
35
32
|
end
|
36
33
|
end
|
37
34
|
|
38
|
-
describe
|
35
|
+
describe 'with a user who can read roles' do
|
39
36
|
before do
|
40
37
|
ability.can :read, Role
|
41
38
|
end
|
42
|
-
it
|
39
|
+
it 'is able to see the list of roles' do
|
43
40
|
get :index
|
44
41
|
expect(response).to be_successful
|
45
42
|
expect(assigns[:roles]).to eq [role]
|
46
43
|
end
|
47
44
|
|
48
|
-
it
|
45
|
+
it 'is able to see a single role' do
|
49
46
|
get :show, params: { id: role }
|
50
47
|
expect(response).to be_successful
|
51
48
|
expect(assigns[:role]).to eq role
|
@@ -53,31 +50,31 @@ describe RolesController do
|
|
53
50
|
end
|
54
51
|
|
55
52
|
describe "with a user who can only update role 'foo'" do
|
56
|
-
it
|
53
|
+
it 'is redirected to edit' do
|
57
54
|
ability.can :read, Role
|
58
55
|
ability.can :update, Role, id: role.id
|
59
56
|
get :show, params: { id: role }
|
60
|
-
expect(response).to redirect_to
|
57
|
+
expect(response).to redirect_to routes.url_helpers.edit_role_path(assigns[:role])
|
61
58
|
end
|
62
59
|
end
|
63
60
|
|
64
|
-
describe
|
61
|
+
describe 'with a user who can create roles' do
|
65
62
|
before do
|
66
63
|
ability.can :create, Role
|
67
64
|
end
|
68
|
-
it
|
65
|
+
it 'is able to make a new role' do
|
69
66
|
get :new
|
70
67
|
expect(response).to be_successful
|
71
68
|
expect(assigns[:role]).to be_kind_of Role
|
72
69
|
end
|
73
70
|
|
74
|
-
it
|
71
|
+
it 'is able to create a new role' do
|
75
72
|
post :create, params: { role: { name: 'my_role' } }
|
76
|
-
expect(response).to redirect_to
|
73
|
+
expect(response).to redirect_to routes.url_helpers.edit_role_path(assigns[:role])
|
77
74
|
expect(assigns[:role]).not_to be_new_record
|
78
75
|
expect(assigns[:role].name).to eq 'my_role'
|
79
76
|
end
|
80
|
-
it
|
77
|
+
it 'does not create role with an error' do
|
81
78
|
post :create, params: { role: { name: 'my role' } }
|
82
79
|
expect(assigns[:role].name).to eq 'my role'
|
83
80
|
expect(assigns[:role].errors[:name]).to eq ['Only letters, numbers, hyphens, underscores and periods are allowed']
|
@@ -85,33 +82,33 @@ describe RolesController do
|
|
85
82
|
end
|
86
83
|
end
|
87
84
|
|
88
|
-
describe
|
85
|
+
describe 'with a user who can update roles' do
|
89
86
|
before do
|
90
87
|
ability.can :update, Role
|
91
88
|
end
|
92
89
|
|
93
|
-
it
|
90
|
+
it 'is able to update a role' do
|
94
91
|
put :update, params: { id: role, role: { name: 'my_role' } }
|
95
|
-
expect(response).to redirect_to
|
92
|
+
expect(response).to redirect_to routes.url_helpers.edit_role_path(assigns[:role])
|
96
93
|
expect(assigns[:role]).not_to be_new_record
|
97
94
|
expect(assigns[:role].name).to eq 'my_role'
|
98
95
|
end
|
99
|
-
it
|
100
|
-
put :update,
|
96
|
+
it 'does not update role with an error' do
|
97
|
+
put :update, params: { id: role, role: { name: 'my role' } }
|
101
98
|
expect(assigns[:role].name).to eq 'my role'
|
102
99
|
expect(assigns[:role].errors[:name]).to eq ['Only letters, numbers, hyphens, underscores and periods are allowed']
|
103
100
|
expect(response).to be_successful
|
104
101
|
end
|
105
102
|
end
|
106
103
|
|
107
|
-
describe
|
104
|
+
describe 'with a user who can remove roles' do
|
108
105
|
before do
|
109
106
|
ability.can :destroy, Role
|
110
107
|
end
|
111
108
|
|
112
|
-
it
|
109
|
+
it 'is able to destroy a role' do
|
113
110
|
delete :destroy, params: { id: role }
|
114
|
-
expect(response).to redirect_to
|
111
|
+
expect(response).to redirect_to routes.url_helpers.roles_path
|
115
112
|
end
|
116
113
|
end
|
117
114
|
end
|