rhino_project_organizations 0.20.0.alpha.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +28 -0
- data/Rakefile +35 -0
- data/app/assets/config/rhino_organizations_manifest.js +0 -0
- data/app/controllers/rhino/users_role_invite_controller.rb +14 -0
- data/app/controllers/rhino_organizations/concerns/create_organization.rb +15 -0
- data/app/models/rhino/organization.rb +33 -0
- data/app/models/rhino/role.rb +15 -0
- data/app/models/rhino/users_role.rb +35 -0
- data/app/models/rhino/users_role_invite.rb +30 -0
- data/app/overrides/devise/invitations_controller_override.rb +50 -0
- data/app/overrides/models/rhino/account_override.rb +7 -0
- data/app/overrides/models/rhino/users_role_override.rb +7 -0
- data/app/policies/rhino/organization_policy.rb +17 -0
- data/app/policies/rhino/users_role_policy.rb +33 -0
- data/app/views/devise/mailer/invitation_instructions.html.erb +11 -0
- data/app/views/devise/mailer/invitation_instructions.text.erb +11 -0
- data/config/routes.rb +2 -0
- data/db/migrate/20201129221747_create_organizations.rb +23 -0
- data/db/migrate/20211202195016_add_devise_invitable_to_users.rb +12 -0
- data/db/migrate/20211207212517_create_users_role_invite.rb +11 -0
- data/lib/generators/rhino_organizations/install/install_generator.rb +68 -0
- data/lib/generators/rhino_organizations/install/templates/admin/users_roles.rb +5 -0
- data/lib/generators/rhino_organizations/install/templates/models/organization.rb +8 -0
- data/lib/generators/rhino_organizations/install/templates/models/role.rb +4 -0
- data/lib/generators/rhino_organizations/install/templates/models/users_role.rb +7 -0
- data/lib/generators/rhino_organizations/install/templates/models/users_role_invite.rb +6 -0
- data/lib/generators/rhino_organizations/install/templates/seeds/organizations.rb +7 -0
- data/lib/rhino_organizations/engine.rb +39 -0
- data/lib/rhino_organizations/test_case/organization_controller.rb +38 -0
- data/lib/rhino_organizations/test_case/organization_controller_policy.rb +84 -0
- data/lib/rhino_organizations/test_case.rb +10 -0
- data/lib/rhino_organizations/version.rb +17 -0
- data/lib/rhino_project_organizations.rb +6 -0
- data/lib/tasks/rhino_organizations.rake +15 -0
- metadata +125 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 964db7b13b183284e5d084f8e88897a6a53c6928727e94fa704fe27ae16f6695
|
4
|
+
data.tar.gz: 6193cb3b0ddfc68dd0a91d1bba54b616390e661202ba0dacd17a5c870ce2e40c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d64ea8c9c217bfda494404739374c492e256af5c1cc359474aca55d600e810111f44dd2cf84c3e15191bb37c05a629c66c06b8f1987371cbcc25580005aa6cfb
|
7
|
+
data.tar.gz: d18518059334d25a8cf40d93e58b1c7157c08bcab8a5c61950c9d04b5da43cfa57894328d3bfeb7486f5e2ace1519c575fa55e017c07b4b5541204e43234652e
|
data/README.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# RhinoOrganizations
|
2
|
+
Short description and motivation.
|
3
|
+
|
4
|
+
## Usage
|
5
|
+
How to use my plugin.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'rhino_organizations'
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
```bash
|
16
|
+
$ bundle
|
17
|
+
```
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
```bash
|
21
|
+
$ gem install rhino_organizations
|
22
|
+
```
|
23
|
+
|
24
|
+
## Contributing
|
25
|
+
Contribution directions go here.
|
26
|
+
|
27
|
+
## License
|
28
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
task :package
|
10
|
+
|
11
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
12
|
+
rdoc.rdoc_dir = 'rdoc'
|
13
|
+
rdoc.title = 'RhinoOrganizations'
|
14
|
+
rdoc.options << '--line-numbers'
|
15
|
+
rdoc.rdoc_files.include('README.md')
|
16
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
17
|
+
end
|
18
|
+
|
19
|
+
APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
|
20
|
+
load 'rails/tasks/engine.rake'
|
21
|
+
|
22
|
+
load 'rails/tasks/statistics.rake'
|
23
|
+
|
24
|
+
require 'bundler/gem_tasks'
|
25
|
+
|
26
|
+
require 'rake/testtask'
|
27
|
+
|
28
|
+
Rake::TestTask.new(:test) do |t|
|
29
|
+
t.libs << 'test'
|
30
|
+
t.pattern = 'test/**/*_test.rb'
|
31
|
+
t.verbose = true
|
32
|
+
t.warning = false
|
33
|
+
end
|
34
|
+
|
35
|
+
task default: :test
|
File without changes
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rhino
|
4
|
+
class UsersRoleInviteController < CrudController
|
5
|
+
def create
|
6
|
+
@model = authorize klass.new(permit_and_transform(klass))
|
7
|
+
user = ::User.find_by(email: params[:email])
|
8
|
+
::User.invite!(email: params[:email]) if user.nil?
|
9
|
+
@model.save!
|
10
|
+
|
11
|
+
permit_and_render
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RhinoOrganizations
|
4
|
+
module Concerns
|
5
|
+
module CreateOrganization
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
def create_organization(resource)
|
9
|
+
org_name = params.fetch(:organization, nil) || resource.name || resource.email
|
10
|
+
org = Organization.create!(name: "#{org_name} Organization")
|
11
|
+
UsersRole.create!(organization: org, user: resource, role: Role.find_or_create_by!(name: 'admin'))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rhino
|
4
|
+
class Organization < ApplicationRecord
|
5
|
+
self.abstract_class = true
|
6
|
+
|
7
|
+
rhino_policy :organization
|
8
|
+
rhino_properties_write only: :name
|
9
|
+
|
10
|
+
after_create_commit :track_account_created
|
11
|
+
after_destroy_commit :track_account_deleted
|
12
|
+
|
13
|
+
def self.roles_for_auth(auth_owner, record = nil)
|
14
|
+
return {} unless auth_owner
|
15
|
+
|
16
|
+
# FIXME: - hard code user?
|
17
|
+
users_roles = ::UsersRole.where(user: auth_owner).joins(:organization, :role).includes(:organization, :role)
|
18
|
+
users_roles = users_roles.where(organization_id: record.base_owner_ids) if record.present? && record.respond_to?(:base_owner_ids)
|
19
|
+
|
20
|
+
# A list of roles as hash keys with an array of base_owners for each
|
21
|
+
users_roles.group_by { |ur| ur.role.name }.transform_values { |ur_array| ur_array.map(&:organization) }
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
def track_account_created
|
26
|
+
Rhino::SegmentHelper.track_account("Account Created", self)
|
27
|
+
end
|
28
|
+
|
29
|
+
def track_account_deleted
|
30
|
+
Rhino::SegmentHelper.track_account("Account Deleted", self)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rhino
|
4
|
+
class Role < ApplicationRecord
|
5
|
+
self.abstract_class = true
|
6
|
+
|
7
|
+
validates :name, presence: true, uniqueness: true, format: { with: /\A[a-zA-Z_]+\z/, message: "alpha characters only" }
|
8
|
+
|
9
|
+
rhino_owner_global
|
10
|
+
|
11
|
+
def display_name
|
12
|
+
name.titleize
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rhino
|
4
|
+
class UsersRole < ApplicationRecord
|
5
|
+
self.abstract_class = true
|
6
|
+
|
7
|
+
rhino_properties_update only: [:role]
|
8
|
+
|
9
|
+
validate :ensure_at_least_one_admin, on: %i[update destroy], unless: :destroyed_by_association
|
10
|
+
|
11
|
+
after_create_commit :track_account_added_user
|
12
|
+
after_destroy_commit :track_account_removed_user
|
13
|
+
|
14
|
+
before_destroy do
|
15
|
+
raise ActiveRecord::RecordInvalid, self if invalid?(:destroy)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
def ensure_at_least_one_admin
|
20
|
+
admins = organization.users_roles.joins(:role).where({ role: { name: "admin" } })
|
21
|
+
user_role_id_being_updated = id
|
22
|
+
return unless admins.count == 1 && admins.first.id == user_role_id_being_updated
|
23
|
+
|
24
|
+
errors.add(:role, "Must have at least one user as admin")
|
25
|
+
end
|
26
|
+
|
27
|
+
def track_account_added_user
|
28
|
+
Rhino::SegmentHelper.track_account("Account Added User", organization, self)
|
29
|
+
end
|
30
|
+
|
31
|
+
def track_account_removed_user
|
32
|
+
Rhino::SegmentHelper.track_account("Account Removed User", organization, self)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rhino
|
4
|
+
class UsersRoleInvite < ApplicationRecord
|
5
|
+
self.abstract_class = true
|
6
|
+
|
7
|
+
rhino_policy :users_role
|
8
|
+
# FIXME: Only necessary because this module is in tree NUB-910
|
9
|
+
rhino_owner :organization if Rhino.resources.include?("Organization")
|
10
|
+
rhino_references %i[role organization]
|
11
|
+
rhino_controller :users_role_invite
|
12
|
+
|
13
|
+
before_save :normalize_email
|
14
|
+
|
15
|
+
after_create do
|
16
|
+
::UsersRole.create!(user: ::User.find_by(email:), organization:, role:)
|
17
|
+
end
|
18
|
+
|
19
|
+
after_create_commit :track_invite
|
20
|
+
|
21
|
+
private
|
22
|
+
def track_invite
|
23
|
+
Rhino::SegmentHelper.track_invite(self)
|
24
|
+
end
|
25
|
+
|
26
|
+
def normalize_email
|
27
|
+
self.email = email.downcase
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Devise::InvitationsController
|
4
|
+
prepend_before_action :require_no_authentication, only: %i[update]
|
5
|
+
before_action :configure_permitted_parameters
|
6
|
+
|
7
|
+
# PUT /resource/invitation
|
8
|
+
def update
|
9
|
+
self.resource = accept_resource
|
10
|
+
invitation_accepted = resource.errors.empty?
|
11
|
+
|
12
|
+
if invitation_accepted
|
13
|
+
actions_for_insecure_sign_in
|
14
|
+
render json: { data: resource.token_validation_response }
|
15
|
+
else
|
16
|
+
render status: :unprocessable_entity, json: { status: :error, errors: resource.errors }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
def actions_for_insecure_sign_in
|
22
|
+
return unless resource.class.allow_insecure_sign_in_after_accept
|
23
|
+
|
24
|
+
resource.after_database_authentication
|
25
|
+
set_token
|
26
|
+
set_cookie
|
27
|
+
end
|
28
|
+
|
29
|
+
def set_token
|
30
|
+
@token = resource.create_token
|
31
|
+
resource.skip_confirmation!
|
32
|
+
sign_in(:user, resource, store: false, bypass: false)
|
33
|
+
resource.save!
|
34
|
+
end
|
35
|
+
|
36
|
+
def set_cookie
|
37
|
+
return unless DeviseTokenAuth.cookie_enabled
|
38
|
+
|
39
|
+
auth_header = resource.build_auth_headers(@token.token, @token.client)
|
40
|
+
cookies[DeviseTokenAuth.cookie_name] = DeviseTokenAuth.cookie_attributes.merge(value: auth_header.to_json)
|
41
|
+
end
|
42
|
+
|
43
|
+
def accept_resource
|
44
|
+
resource_class.accept_invitation!(@update_resource_params)
|
45
|
+
end
|
46
|
+
|
47
|
+
def configure_permitted_parameters
|
48
|
+
@update_resource_params = params.permit(%i[invitation_token password password_confirmation])
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rhino
|
4
|
+
class OrganizationPolicy < ::Rhino::ViewerPolicy
|
5
|
+
def update?
|
6
|
+
# Must be an admin to update
|
7
|
+
is_admin = Rhino.base_owner.roles_for_auth(auth_owner).any? { |k, v| k == "admin" && v.include?(record) }
|
8
|
+
authorize_action(is_admin)
|
9
|
+
end
|
10
|
+
|
11
|
+
class Scope < ::Rhino::ViewerPolicy::Scope
|
12
|
+
def resolve
|
13
|
+
super.joins(scope.joins_for_auth_owner).where("#{Rhino.auth_owner.table_name}.#{Rhino.auth_owner.primary_key}": auth_owner&.id)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rhino
|
4
|
+
class UsersRolePolicy < ::Rhino::ViewerPolicy
|
5
|
+
def show?
|
6
|
+
authorize_action(true) if admin? || record.user == auth_owner
|
7
|
+
end
|
8
|
+
|
9
|
+
def create?
|
10
|
+
authorize_action admin?
|
11
|
+
end
|
12
|
+
|
13
|
+
def update?
|
14
|
+
authorize_action admin?
|
15
|
+
end
|
16
|
+
|
17
|
+
def destroy?
|
18
|
+
authorize_action admin?
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def admin?
|
23
|
+
Rhino.base_owner.roles_for_auth(auth_owner).any? { |k, v| k == "admin" && v.include?(record.organization) }
|
24
|
+
end
|
25
|
+
|
26
|
+
class Scope < ::Rhino::ViewerPolicy::Scope
|
27
|
+
def resolve
|
28
|
+
admin_organizations = ::Organization.joins(users_roles: [:role]).where(users_roles: { roles: { name: "admin" }, user: auth_owner })
|
29
|
+
scope.where(organization: admin_organizations).or(scope.where(user: auth_owner))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<p><%= t("devise.mailer.invitation_instructions.hello", email: @resource.email) %></p>
|
2
|
+
|
3
|
+
<p><%= t("devise.mailer.invitation_instructions.someone_invited_you", url: ENV['FRONT_END_URL']) %></p>
|
4
|
+
|
5
|
+
<p><%= link_to("Accept invitation", "#{ENV['FRONT_END_URL']}/auth/accept-invitation?invitation_token=#{@token}") %></p>
|
6
|
+
|
7
|
+
<% if @resource.invitation_due_at %>
|
8
|
+
<p><%= t("devise.mailer.invitation_instructions.accept_until", due_date: l(@resource.invitation_due_at, format: :'devise.mailer.invitation_instructions.accept_until_format')) %></p>
|
9
|
+
<% end %>
|
10
|
+
|
11
|
+
<p><%= t("devise.mailer.invitation_instructions.ignore") %></p>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<%= t("devise.mailer.invitation_instructions.hello", email: @resource.email) %>
|
2
|
+
|
3
|
+
<%= t("devise.mailer.invitation_instructions.someone_invited_you", url: ENV['FRONT_END_URL']) %>
|
4
|
+
|
5
|
+
<%= "#{ENV['FRONT_END_URL']}/auth/accept-invitation?invitation_token=#{@token}" %>
|
6
|
+
|
7
|
+
<% if @resource.invitation_due_at %>
|
8
|
+
<%= t("devise.mailer.invitation_instructions.accept_until", due_date: l(@resource.invitation_due_at, format: :'devise.mailer.invitation_instructions.accept_until_format')) %>
|
9
|
+
<% end %>
|
10
|
+
|
11
|
+
<%= t("devise.mailer.invitation_instructions.ignore") %>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
class CreateOrganizations < ActiveRecord::Migration[6.0]
|
2
|
+
def change
|
3
|
+
create_table :organizations do |t|
|
4
|
+
t.string :name
|
5
|
+
|
6
|
+
t.timestamps
|
7
|
+
end
|
8
|
+
|
9
|
+
create_table :roles do |t|
|
10
|
+
t.string :name, null: false
|
11
|
+
|
12
|
+
t.timestamps
|
13
|
+
end
|
14
|
+
|
15
|
+
create_table :users_roles do |t|
|
16
|
+
t.references :user, null: false, foreign_key: true
|
17
|
+
t.references :organization, null: false, foreign_key: true
|
18
|
+
t.references :role, null: false, foreign_key: true
|
19
|
+
|
20
|
+
t.timestamps
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class AddDeviseInvitableToUsers < ActiveRecord::Migration[6.1]
|
2
|
+
def change
|
3
|
+
add_column :users, :invitation_token, :string
|
4
|
+
add_column :users, :invitation_created_at, :datetime
|
5
|
+
add_column :users, :invitation_sent_at, :datetime
|
6
|
+
add_column :users, :invitation_accepted_at, :datetime
|
7
|
+
add_column :users, :invitation_limit, :integer
|
8
|
+
add_column :users, :invited_by_id, :integer
|
9
|
+
add_column :users, :invited_by_type, :string
|
10
|
+
add_index :users, :invitation_token, unique: true
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class CreateUsersRoleInvite < ActiveRecord::Migration[6.1]
|
2
|
+
def change
|
3
|
+
create_table :users_role_invites do |t|
|
4
|
+
t.string :email, null: false
|
5
|
+
t.references :organization, null: false, foreign_key: true
|
6
|
+
t.references :role, null: false, foreign_key: true
|
7
|
+
|
8
|
+
t.timestamps
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RhinoOrganizations
|
4
|
+
module Generators
|
5
|
+
class InstallGenerator < ::Rails::Generators::Base
|
6
|
+
source_root File.expand_path('templates', __dir__)
|
7
|
+
|
8
|
+
def install_models # rubocop:todo Metrics/MethodLength
|
9
|
+
say 'Copying rhino_organizations models to app/models'
|
10
|
+
copy_file "#{__dir__}/templates/models/organization.rb", 'app/models/organization.rb'
|
11
|
+
copy_file "#{__dir__}/templates/models/role.rb", 'app/models/role.rb'
|
12
|
+
copy_file "#{__dir__}/templates/models/users_role.rb", 'app/models/users_role.rb'
|
13
|
+
copy_file "#{__dir__}/templates/models/users_role_invite.rb", 'app/models/users_role_invite.rb'
|
14
|
+
|
15
|
+
data = <<-'RUBY'
|
16
|
+
has_many :users_roles, dependent: :destroy
|
17
|
+
has_many :organizations, through: :users_roles
|
18
|
+
has_many :roles, through: :users_roles
|
19
|
+
RUBY
|
20
|
+
|
21
|
+
inject_into_file 'app/models/user.rb', optimize_indentation(data, 2), after: "class User < Rhino::User\n"
|
22
|
+
|
23
|
+
data = <<-'RUBY'
|
24
|
+
has_many :users_roles, dependent: :destroy, foreign_key: :user_id, inverse_of: false
|
25
|
+
has_many :organizations, through: :users_roles
|
26
|
+
has_many :roles, through: :users_roles
|
27
|
+
RUBY
|
28
|
+
inject_into_file 'app/models/account.rb', optimize_indentation(data, 2), after: "class Account < Rhino::Account\n"
|
29
|
+
end
|
30
|
+
|
31
|
+
def install_devise_invitable
|
32
|
+
generate "devise_invitable:install"
|
33
|
+
|
34
|
+
# Set a sane default
|
35
|
+
gsub_file "config/initializers/devise.rb", /^ \# config.invitation_limit = 5/, " config.invitation_limit = 5"
|
36
|
+
end
|
37
|
+
|
38
|
+
def install_initializer
|
39
|
+
gsub_file 'config/initializers/rhino.rb', " # config.base_owner = 'Organization'", " config.base_owner = \"Organization\""
|
40
|
+
|
41
|
+
data = 'config.resources += ["Organization", "UsersRole", "Role", "UsersRoleInvite"]'
|
42
|
+
inject_into_file 'config/initializers/rhino.rb', optimize_indentation(data, 2), after: /^\s*config\.resources.+\n/
|
43
|
+
end
|
44
|
+
|
45
|
+
def install_seeds
|
46
|
+
say 'Copying seed files'
|
47
|
+
copy_file "#{__dir__}/templates/seeds/organizations.rb", 'db/seeds/development/organizations.rb'
|
48
|
+
copy_file "#{__dir__}/templates/seeds/organizations.rb", 'db/seeds/test/organizations.rb'
|
49
|
+
end
|
50
|
+
|
51
|
+
def install_active_admin # rubocop:disable Metrics/MethodLength
|
52
|
+
say 'Copying rhino_organization ActiveAdmin files and configurations'
|
53
|
+
copy_file "#{__dir__}/templates/admin/users_roles.rb", 'app/admin/users_roles.rb'
|
54
|
+
data = <<-'RUBY'
|
55
|
+
br
|
56
|
+
br
|
57
|
+
panel "User Roles" do
|
58
|
+
table_for user.users_roles do
|
59
|
+
column :organization
|
60
|
+
column :role
|
61
|
+
end
|
62
|
+
end
|
63
|
+
RUBY
|
64
|
+
inject_into_file 'app/admin/users.rb', optimize_indentation(data, 4), after: "default_main_content\n"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rhino/engine"
|
4
|
+
require "rhino_organizations/version"
|
5
|
+
|
6
|
+
# https://guides.rubyonrails.org/engines.html#other-gem-dependencies
|
7
|
+
require "devise_invitable"
|
8
|
+
|
9
|
+
module RhinoOrganizations
|
10
|
+
class Engine < ::Rails::Engine
|
11
|
+
config.autoload_paths << File.expand_path("../../lib", __dir__)
|
12
|
+
|
13
|
+
initializer "rhino_organizations.register_module" do
|
14
|
+
config.after_initialize do
|
15
|
+
if Rhino.resources.include?("Organization")
|
16
|
+
Rhino.registered_modules[:rhino_organizations] = {
|
17
|
+
version: RhinoOrganizations::VERSION::STRING
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# https://guides.rubyonrails.org/engines.html#overriding-models-and-controllers
|
24
|
+
# Use root instead of Rails.root to scope for this engine
|
25
|
+
initializer "rhino_organizations.overrides" do
|
26
|
+
overrides = "#{root}/app/overrides"
|
27
|
+
Rails.autoloaders.main.ignore(overrides)
|
28
|
+
|
29
|
+
config.to_prepare do
|
30
|
+
# FIXME: Only necessary because this module is in tree NUB-682
|
31
|
+
if Rhino.resources.include?("Organization")
|
32
|
+
Dir.glob("#{overrides}/**/*_override.rb").each do |override|
|
33
|
+
load override
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rhino
|
4
|
+
module TestCase
|
5
|
+
class OrganizationControllerTest < ControllerTest
|
6
|
+
protected
|
7
|
+
def sign_in_with_organization(role = "admin")
|
8
|
+
@current_user = create :user
|
9
|
+
@current_organization = create :organization
|
10
|
+
@current_role = create :role, name: role
|
11
|
+
@current_user_role = create :users_role, role: @current_role, user: @current_user, organization: @current_organization
|
12
|
+
sign_in @current_user
|
13
|
+
end
|
14
|
+
|
15
|
+
def sign_in_with_organization_and_admin_user
|
16
|
+
sign_in_with_organization
|
17
|
+
end
|
18
|
+
|
19
|
+
def sign_in_with_organization_and_non_admin_user
|
20
|
+
sign_in_with_organization("regular")
|
21
|
+
end
|
22
|
+
|
23
|
+
def index(params: {}, headers: {})
|
24
|
+
get "/api/organizations", params:, headers:
|
25
|
+
end
|
26
|
+
|
27
|
+
def prepare
|
28
|
+
sign_in
|
29
|
+
index params: @params
|
30
|
+
end
|
31
|
+
|
32
|
+
def prepare_with_organization
|
33
|
+
sign_in_with_organization_and_admin_user
|
34
|
+
index params: @params
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rhino
|
4
|
+
module TestCase
|
5
|
+
class OrganizationControllerPolicyTest < OrganizationControllerTest
|
6
|
+
protected
|
7
|
+
def sign_in_org_users_and_resource(role = "admin", resource = :blog)
|
8
|
+
sign_in_with_organization(role)
|
9
|
+
|
10
|
+
@resource_class = resource.to_s.classify.safe_constantize
|
11
|
+
@resource_collection_route = "#{@resource_class.model_name.route_key}_path"
|
12
|
+
@resource_singular_route = "#{@resource_class.model_name.singular_route_key}_path"
|
13
|
+
@resource = create resource, organization: @current_organization
|
14
|
+
@another_resource = create resource
|
15
|
+
end
|
16
|
+
|
17
|
+
def policy_index_success(expected_results = [@resource])
|
18
|
+
get_api send(@resource_collection_route)
|
19
|
+
|
20
|
+
assert_response_ok
|
21
|
+
assert_equal expected_results.count, parsed_response["total"]
|
22
|
+
expected_results.each_with_index { |result, idx| assert_equal result.title, parsed_response["results"][idx]["title"] }
|
23
|
+
end
|
24
|
+
|
25
|
+
def policy_show_success(resource = @resource)
|
26
|
+
get_api send(@resource_singular_route, resource)
|
27
|
+
|
28
|
+
assert_response_ok
|
29
|
+
assert_equal resource.id, parsed_response["id"]
|
30
|
+
assert_equal resource.title, parsed_response["title"]
|
31
|
+
end
|
32
|
+
|
33
|
+
def policy_show_fail(resource = @resource)
|
34
|
+
get_api send(@resource_singular_route, resource)
|
35
|
+
|
36
|
+
assert_response_not_found
|
37
|
+
end
|
38
|
+
|
39
|
+
def policy_create_success(create_attributes)
|
40
|
+
assert_difference "#{@resource_class}.count" do
|
41
|
+
post_api send(@resource_collection_route), params: create_attributes
|
42
|
+
end
|
43
|
+
|
44
|
+
assert_response_ok
|
45
|
+
end
|
46
|
+
|
47
|
+
def policy_create_fail(create_attributes)
|
48
|
+
assert_no_difference "#{@resource_class}.count" do
|
49
|
+
post_api send(@resource_collection_route), params: create_attributes
|
50
|
+
end
|
51
|
+
|
52
|
+
assert_response_forbidden
|
53
|
+
end
|
54
|
+
|
55
|
+
def policy_update_success(update_attributes, resource = @resource)
|
56
|
+
patch_api send(@resource_singular_route, resource), params: update_attributes
|
57
|
+
|
58
|
+
assert_response_ok
|
59
|
+
end
|
60
|
+
|
61
|
+
def policy_update_fail(update_attributes, resource = @resource)
|
62
|
+
patch_api send(@resource_singular_route, resource), params: update_attributes
|
63
|
+
|
64
|
+
assert_response_forbidden
|
65
|
+
end
|
66
|
+
|
67
|
+
def policy_destroy_success(resource = @resource)
|
68
|
+
assert_difference "#{@resource_class}.count", -1 do
|
69
|
+
delete_api send(@resource_singular_route, resource)
|
70
|
+
end
|
71
|
+
|
72
|
+
assert_response_ok
|
73
|
+
end
|
74
|
+
|
75
|
+
def policy_destroy_fail(resource = @resource)
|
76
|
+
assert_no_difference "Blog.count" do
|
77
|
+
delete_api send(@resource_singular_route, resource)
|
78
|
+
end
|
79
|
+
|
80
|
+
assert_response_forbidden
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RhinoOrganizations
|
4
|
+
# Returns the currently loaded version of Rhino core as a +Gem::Version+.
|
5
|
+
def self.gem_version
|
6
|
+
Gem::Version.new VERSION::STRING
|
7
|
+
end
|
8
|
+
|
9
|
+
module VERSION
|
10
|
+
MAJOR = 0
|
11
|
+
MINOR = 20
|
12
|
+
TINY = 0
|
13
|
+
PRE = "alpha.0"
|
14
|
+
|
15
|
+
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
namespace :rhino_organizations do
|
4
|
+
# Prevent migration installation task from showing up twice.
|
5
|
+
if Rake::Task.task_defined?('rhino_organizations_engine:install:migrations')
|
6
|
+
Rake::Task['rhino_organizations_engine:install:migrations'].clear_comments
|
7
|
+
end
|
8
|
+
|
9
|
+
desc 'Install rhino_organizations'
|
10
|
+
task install: :environment do
|
11
|
+
Rake::Task['rhino_organizations_engine:install:migrations'].invoke if Rake::Task.task_defined?('rhino_organizations_engine:install:migrations')
|
12
|
+
|
13
|
+
Rails::Command.invoke :generate, ['rhino_organizations:install']
|
14
|
+
end
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rhino_project_organizations
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.20.0.alpha.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- JP Rosevear
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-08-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 7.1.0
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 7.1.0
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 7.1.0
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 7.1.0
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: rhino_project_core
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - '='
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 0.20.0.alpha.0
|
40
|
+
type: :runtime
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - '='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 0.20.0.alpha.0
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: devise_invitable
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - '='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 2.0.9
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - '='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 2.0.9
|
61
|
+
description: ''
|
62
|
+
email:
|
63
|
+
- jp@codalio.com
|
64
|
+
executables: []
|
65
|
+
extensions: []
|
66
|
+
extra_rdoc_files: []
|
67
|
+
files:
|
68
|
+
- README.md
|
69
|
+
- Rakefile
|
70
|
+
- app/assets/config/rhino_organizations_manifest.js
|
71
|
+
- app/controllers/rhino/users_role_invite_controller.rb
|
72
|
+
- app/controllers/rhino_organizations/concerns/create_organization.rb
|
73
|
+
- app/models/rhino/organization.rb
|
74
|
+
- app/models/rhino/role.rb
|
75
|
+
- app/models/rhino/users_role.rb
|
76
|
+
- app/models/rhino/users_role_invite.rb
|
77
|
+
- app/overrides/devise/invitations_controller_override.rb
|
78
|
+
- app/overrides/models/rhino/account_override.rb
|
79
|
+
- app/overrides/models/rhino/users_role_override.rb
|
80
|
+
- app/policies/rhino/organization_policy.rb
|
81
|
+
- app/policies/rhino/users_role_policy.rb
|
82
|
+
- app/views/devise/mailer/invitation_instructions.html.erb
|
83
|
+
- app/views/devise/mailer/invitation_instructions.text.erb
|
84
|
+
- config/routes.rb
|
85
|
+
- db/migrate/20201129221747_create_organizations.rb
|
86
|
+
- db/migrate/20211202195016_add_devise_invitable_to_users.rb
|
87
|
+
- db/migrate/20211207212517_create_users_role_invite.rb
|
88
|
+
- lib/generators/rhino_organizations/install/install_generator.rb
|
89
|
+
- lib/generators/rhino_organizations/install/templates/admin/users_roles.rb
|
90
|
+
- lib/generators/rhino_organizations/install/templates/models/organization.rb
|
91
|
+
- lib/generators/rhino_organizations/install/templates/models/role.rb
|
92
|
+
- lib/generators/rhino_organizations/install/templates/models/users_role.rb
|
93
|
+
- lib/generators/rhino_organizations/install/templates/models/users_role_invite.rb
|
94
|
+
- lib/generators/rhino_organizations/install/templates/seeds/organizations.rb
|
95
|
+
- lib/rhino_organizations/engine.rb
|
96
|
+
- lib/rhino_organizations/test_case.rb
|
97
|
+
- lib/rhino_organizations/test_case/organization_controller.rb
|
98
|
+
- lib/rhino_organizations/test_case/organization_controller_policy.rb
|
99
|
+
- lib/rhino_organizations/version.rb
|
100
|
+
- lib/rhino_project_organizations.rb
|
101
|
+
- lib/tasks/rhino_organizations.rake
|
102
|
+
homepage: ''
|
103
|
+
licenses:
|
104
|
+
- MIT
|
105
|
+
metadata: {}
|
106
|
+
post_install_message:
|
107
|
+
rdoc_options: []
|
108
|
+
require_paths:
|
109
|
+
- lib
|
110
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
requirements: []
|
121
|
+
rubygems_version: 3.5.11
|
122
|
+
signing_key:
|
123
|
+
specification_version: 4
|
124
|
+
summary: ''
|
125
|
+
test_files: []
|