permisi 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f4321f19a7a5c2ecbfa99d51c00a82d7fb1861007f52e6eab462c49df2c5b390
4
- data.tar.gz: ed73932d3a013e89a3d6c49c79c3328fe84ecfb566f66cd53ac1a9d355de383c
3
+ metadata.gz: 47559ea904f406010a7979cd77de853bf8c3628c7e4c1020e45742ae439a7e06
4
+ data.tar.gz: b8c814331a90cbc7e4d2d3560cfb939419b001f1e88e731a3e67e9b180ead167
5
5
  SHA512:
6
- metadata.gz: a9ada7b5c95dcfa0a9627a7606bf4909e516f2e291aac518f61d1cf1a422e904027bf6c95910fc7ddde9b47c61e88d2027e0e377261af06e0d2dac16dd78e43d
7
- data.tar.gz: 421d311e27eb1f2663c6515c62623c3804ee77ee76bec16162b199370eb1619a4283acf5a79ae8eaca86da6daf7f32f5324c872ce9acc92a07b120f29cebe00c
6
+ metadata.gz: bdfbfbadcc257c15ded64521607e062d2d51ba3c9fc5209f4af273f69473df981e2bbbc74457e0bbd3cc0576e9c51e67895f66685bba7dada2e88d3b4513199c
7
+ data.tar.gz: a095961d001eb1495f7d084434f8bef182add89e86f9a147013a34f0b7c171cff4da65373798442e45e962264f03c6d0c07eb23f58ca520e742c61adbd94d8e6
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
+ /spec/support/db/*.db
1
2
  /.bundle/
2
3
  /.yardoc
3
4
  /_yardoc/
@@ -9,3 +10,6 @@
9
10
 
10
11
  # rspec failure tracking
11
12
  .rspec_status
13
+
14
+ *.sublime-*
15
+ .byebug_history
data/CHANGELOG.md CHANGED
@@ -1,4 +1,13 @@
1
- 0.0.0
2
- =====
1
+ # Changelog
3
2
 
4
- - Reserve gem name.
3
+ # 0.1.0
4
+
5
+ Finished extraction work from my past projects.
6
+
7
+ - Implemented ActiveRecord backend
8
+ - Implemented `Actable` mixin
9
+ - Implemented permissions hash sanitization and checking
10
+
11
+ # 0.0.1
12
+
13
+ Reserved the gem name: https://en.wiktionary.org/wiki/permisi
data/Gemfile CHANGED
@@ -5,8 +5,9 @@ source "https://rubygems.org"
5
5
  # Specify your gem's dependencies in permisi.gemspec
6
6
  gemspec
7
7
 
8
+ gem "byebug", "~> 11.1"
8
9
  gem "rake", "~> 13.0"
9
-
10
10
  gem "rspec", "~> 3.0"
11
-
12
- gem "rubocop", "~> 1.7"
11
+ gem "rubocop", "~> 1.9"
12
+ gem "simplecov", "~> 0.21.2"
13
+ gem "sqlite3", "~> 1.4"
data/Gemfile.lock CHANGED
@@ -1,13 +1,34 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- permisi (0.0.0)
4
+ permisi (0.1.0)
5
+ activemodel (>= 3.2.0)
6
+ activerecord (>= 3.2.0)
7
+ activesupport (>= 3.2.0)
8
+ zeitwerk (~> 2.4, >= 2.4.2)
5
9
 
6
10
  GEM
7
11
  remote: https://rubygems.org/
8
12
  specs:
13
+ activemodel (6.1.2.1)
14
+ activesupport (= 6.1.2.1)
15
+ activerecord (6.1.2.1)
16
+ activemodel (= 6.1.2.1)
17
+ activesupport (= 6.1.2.1)
18
+ activesupport (6.1.2.1)
19
+ concurrent-ruby (~> 1.0, >= 1.0.2)
20
+ i18n (>= 1.6, < 2)
21
+ minitest (>= 5.1)
22
+ tzinfo (~> 2.0)
23
+ zeitwerk (~> 2.3)
9
24
  ast (2.4.2)
25
+ byebug (11.1.3)
26
+ concurrent-ruby (1.1.8)
10
27
  diff-lcs (1.4.4)
28
+ docile (1.3.5)
29
+ i18n (1.8.9)
30
+ concurrent-ruby (~> 1.0)
31
+ minitest (5.14.3)
11
32
  parallel (1.20.1)
12
33
  parser (3.0.0.0)
13
34
  ast (~> 2.4.1)
@@ -40,16 +61,29 @@ GEM
40
61
  rubocop-ast (1.4.1)
41
62
  parser (>= 2.7.1.5)
42
63
  ruby-progressbar (1.11.0)
64
+ simplecov (0.21.2)
65
+ docile (~> 1.1)
66
+ simplecov-html (~> 0.11)
67
+ simplecov_json_formatter (~> 0.1)
68
+ simplecov-html (0.12.3)
69
+ simplecov_json_formatter (0.1.2)
70
+ sqlite3 (1.4.2)
71
+ tzinfo (2.0.4)
72
+ concurrent-ruby (~> 1.0)
43
73
  unicode-display_width (2.0.0)
74
+ zeitwerk (2.4.2)
44
75
 
45
76
  PLATFORMS
46
77
  x86_64-linux
47
78
 
48
79
  DEPENDENCIES
80
+ byebug (~> 11.1)
49
81
  permisi!
50
82
  rake (~> 13.0)
51
83
  rspec (~> 3.0)
52
- rubocop (~> 1.7)
84
+ rubocop (~> 1.9)
85
+ simplecov (~> 0.21.2)
86
+ sqlite3 (~> 1.4)
53
87
 
54
88
  BUNDLED WITH
55
89
  2.2.5
data/README.md CHANGED
@@ -1,10 +1,37 @@
1
- # Permisi
2
-
3
- [![Gem Version](https://badge.fury.io/rb/permisi.svg)](https://badge.fury.io/rb/permisi)
4
-
5
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/permisi`. To experiment with that code, run `bin/console` for an interactive prompt.
6
-
7
- TODO: Delete this and the text above, and describe your gem
1
+ <table>
2
+ <tr>
3
+ <th>
4
+ <a href="https://commons.wikimedia.org/wiki/File:Female_Chinese_Lion_Statue.jpg">
5
+ <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/1/17/Female_Chinese_Lion_Statue.jpg/102px-Female_Chinese_Lion_Statue.jpg">
6
+ </a>
7
+ </th>
8
+ <th>
9
+ <h1>Permisi</h1>
10
+ <p><em>Simple and dynamic role-based access control for Rails</em></p>
11
+ <p>
12
+ <a href="https://badge.fury.io/rb/permisi"><img src="https://badge.fury.io/rb/permisi.svg" alt="Gem Version"></a>
13
+ <a href="https://codeclimate.com/github/ukazap/permisi/maintainability"><img src="https://api.codeclimate.com/v1/badges/0b1238302f2012b20740/maintainability" /></a>
14
+ </p>
15
+ </th>
16
+ <th>
17
+ <a href="https://commons.wikimedia.org/wiki/File:Male_Chinese_Lion_Statue.jpg">
18
+ <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/1/14/Male_Chinese_Lion_Statue.jpg/98px-Male_Chinese_Lion_Statue.jpg">
19
+ </a>
20
+ </th>
21
+ </tr>
22
+ </table>
23
+
24
+ ## Concept
25
+
26
+ Permisi provides a way of dynamically declaring user rights (a.k.a. permissions) using a simple role-based access control scheme.
27
+
28
+ This is not an alternative to CanCanCan/Pundit, instead it complement them with dynamic role definition and role membership.
29
+
30
+ Permisi has three basic concepts:
31
+
32
+ - Actor: a person, group of people, or an automated agent who interacts with the app
33
+ - Role: a job function, job title, or rank which determines an actor's authority
34
+ - Permission: the ability to perform an action
8
35
 
9
36
  ## Installation
10
37
 
@@ -17,14 +44,133 @@ gem 'permisi'
17
44
  And then execute:
18
45
 
19
46
  $ bundle install
47
+ $ rails g permisi:install
48
+
49
+ ## Configuring backend
50
+
51
+ Set `config.backend` in the initializer to the backend of choice for storing and retrieving roles:
52
+
53
+ ```ruby
54
+ # config/initializers/permisi.rb
55
+
56
+ Permisi.init do |config|
57
+ #...
58
+ config.backend = :active_record
59
+ #...
60
+ end
61
+ ```
62
+
63
+ To use `:active_record`, run the generated migration from the installation step:
64
+
65
+ $ rails db:migrate
66
+
67
+ Permisi only support `:active_record` backend at the moment. In the future, it will be possible to use `:mongoid`.
68
+
69
+ ## Configuring permissions
70
+
71
+ First you have to predefine the permissions, which is basically a set of possible actions according to the app's use cases. The actions can be grouped in any way possible. For example, you might want to define actions around resource types.
72
+
73
+ To define the available actions in the system, assign a hash to the `config.permissions` with the following format:
74
+
75
+ ```ruby
76
+ # config/initializers/permisi.rb
77
+ Permisi.init do |config|
78
+ # ...
79
+ config.permissions = {
80
+ # A symbol-array pair denotes a namespace.
81
+ # A common use of namespacing is for grouping
82
+ # available actions by resources.
83
+ authors: [
84
+ # Enclosed in the array are symbols
85
+ # denoting available actions in the namespace:
86
+ :list,
87
+ :view,
88
+ :create,
89
+ :edit,
90
+ :delete
91
+ ],
92
+ # You can also use the simplified %i[] notation:
93
+ publishers: %i[list view create edit delete],
94
+ # Besides actions, you can also have nested
95
+ # namespaces:
96
+ books: [
97
+ :list,
98
+ :view,
99
+ :create,
100
+ :edit,
101
+ :delete,
102
+ {
103
+ editions: [
104
+ :list, :view, :create, :edit, :delete, :archive
105
+ ]
106
+ }
107
+ ]
108
+ }
109
+ # ...
110
+ end
111
+ ```
20
112
 
21
- Or install it yourself as:
113
+ ## Defining and managing roles
22
114
 
23
- $ gem install permisi
115
+ Once you have the predefined permissions, you can then define different roles with different level of access within the boundary of the predefined permissions. You can delete or create new roles according to organizational changes. You can also modify existing roles without a change in your code.
24
116
 
25
- ## Usage
117
+ You can create, edit, and destroy roles at runtime. You might also want to define preset roles via `db/seeds.rb`.
26
118
 
27
- TODO: Write usage instructions here
119
+ ```ruby
120
+ # Interact with Permisi.roles as you would with ActiveRecord query interfaces:
121
+
122
+ # List all roles
123
+ Permisi.roles.all
124
+
125
+ # Create a new role
126
+ admin_role = Permisi.roles.create(slug: :admin, name: "Administrator", permissions: {
127
+ books: {
128
+ list: true,
129
+ view: true,
130
+ create: true,
131
+ edit: true
132
+ }
133
+ })
134
+
135
+ # Ask specific role permission
136
+ admin_role.allows? "books.delete" # == false
137
+
138
+ # Update existing role
139
+ admin.permissions[:books].merge!({ delete: true })
140
+ admin.save
141
+ admin_role.allows? "books.delete" # == true
142
+ ```
143
+
144
+ ## Configuring actors
145
+
146
+ You can then give or take multiple roles to an actor which will allow or prevent them to perform certain actions in a flexible manner. But before you can do that, you have to wire up your user model with Permisi.
147
+
148
+ Permisi does not hold an assumption that a specific model is present (e.g. User model). Instead, it keeps track of "actors" internally. The goal is to support multiple use cases such as actor polymorphism, user _groups_, etc.
149
+
150
+ For example, you can map your user model to Permisi's actor model by including the `Permisi::Actable` mixin like so:
151
+
152
+ ```ruby
153
+ # app/models/user.rb
154
+
155
+ class User < ApplicationRecord
156
+ include Permisi::Actable
157
+ end
158
+ ```
159
+
160
+ You can then interact with the new `#permisi` method:
161
+
162
+ ```ruby
163
+ user = User.find_by_email "esther@example.com"
164
+ user.permisi # => instance of Actor
165
+
166
+ user.permisi.has_role? :admin # == false
167
+ user.permisi.may? "books.delete" # == false
168
+
169
+ user.permisi.roles << Permisi.roles.find_by_slug(:admin)
170
+
171
+ user.permisi.has_role? :admin # == true
172
+ user.permisi.may? "books.delete" # == true
173
+ ```
28
174
 
29
175
  ## Development
30
176
 
@@ -34,7 +180,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
34
180
 
35
181
  ## Contributing
36
182
 
37
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/permisi. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/permisi/blob/master/CODE_OF_CONDUCT.md).
183
+ Bug reports and pull requests are welcome on GitHub at https://github.com/ukazap/permisi. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/ukazap/permisi/blob/master/CODE_OF_CONDUCT.md).
38
184
 
39
185
  ## License
40
186
 
@@ -42,4 +188,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
42
188
 
43
189
  ## Code of Conduct
44
190
 
45
- Everyone interacting in the Permisi project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/permisi/blob/master/CODE_OF_CONDUCT.md).
191
+ Everyone interacting in the Permisi project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/ukazap/permisi/blob/master/CODE_OF_CONDUCT.md).
@@ -0,0 +1,33 @@
1
+ require "rails/generators"
2
+ require "rails/generators/migration"
3
+ require "rails/generators/active_record"
4
+
5
+ module Permisi
6
+ module Generators
7
+ class InstallGenerator < Rails::Generators::Base
8
+ include Rails::Generators::Migration
9
+
10
+ source_root File.expand_path('../templates', __FILE__)
11
+
12
+ def self.next_migration_number(path)
13
+ ActiveRecord::Generators::Base.next_migration_number(path)
14
+ end
15
+
16
+ def create_initializer
17
+ template 'initializer.rb', 'config/initializers/permisi.rb'
18
+ end
19
+
20
+ def create_migrations
21
+ migration_template 'migration.rb', 'db/migrate/create_permisi_tables.rb', migration_version: migration_version
22
+ end
23
+
24
+ private
25
+
26
+ def migration_version
27
+ if ActiveRecord.version.version > '5'
28
+ "[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,11 @@
1
+ require "permisi"
2
+
3
+ Permisi.init do |config|
4
+ # Define which backend to use
5
+ # See https://github.com/ukazap/permisi#configuring-backend
6
+ config.backend = :active_record
7
+
8
+ # Define all permissions available in the system
9
+ # See https://github.com/ukazap/permisi#configuring-permissions
10
+ config.permissions = {}
11
+ end
@@ -0,0 +1,28 @@
1
+ class CreatePermisiTables < ActiveRecord::Migration<%= migration_version %>
2
+ def up
3
+ create_table :permisi_actors do |t|
4
+ t.references :aka, polymorphic: true
5
+ t.timestamps
6
+ end
7
+
8
+ add_index :permisi_actors, [:aka_type, :aka_id]
9
+
10
+ create_table :permisi_roles do |t|
11
+ t.string :slug, null: false, unique: true
12
+ t.string :name, null: false, unique: true
13
+ t.json :permissions
14
+ t.timestamps
15
+ end
16
+
17
+ create_table :permisi_actor_roles do |t|
18
+ t.belongs_to :actor
19
+ t.belongs_to :role
20
+ end
21
+ end
22
+
23
+ def down
24
+ drop_table :permisi_actor_roles
25
+ drop_table :permisi_roles
26
+ drop_table :permisi_actors
27
+ end
28
+ end
data/lib/permisi.rb CHANGED
@@ -1,8 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "permisi/version"
3
+ require "zeitwerk"
4
+ $permisi_loader = Zeitwerk::Loader.for_gem
5
+ $permisi_loader.ignore("#{__dir__}/generators")
6
+ $permisi_loader.ignore("#{__dir__}/permisi/backend/mongoid.rb") # todo
7
+ $permisi_loader.setup
4
8
 
5
9
  module Permisi
6
- class Error < StandardError; end
7
- # Your code goes here...
10
+ class << self
11
+ def init(&block)
12
+ yield config if block_given?
13
+ end
14
+
15
+ def config
16
+ @@config ||= Config.new
17
+ end
18
+
19
+ def actors
20
+ __backend.actors
21
+ end
22
+
23
+ def actor(aka)
24
+ __backend.findsert_actor(aka)
25
+ end
26
+
27
+ def roles
28
+ __backend.roles
29
+ end
30
+
31
+ private
32
+
33
+ def __backend
34
+ if config.backend.nil? || !(config.backend <= Backend::Base)
35
+ raise Backend::InvalidBackend
36
+ end
37
+
38
+ config.backend
39
+ end
40
+ end
8
41
  end
@@ -0,0 +1,9 @@
1
+ module Permisi
2
+ module Actable
3
+ def permisi_actor
4
+ @permisi_actor ||= Permisi.actor(self)
5
+ end
6
+
7
+ alias permisi permisi_actor
8
+ end
9
+ end
@@ -0,0 +1,23 @@
1
+ require "active_record"
2
+
3
+ module Permisi
4
+ class Backend::ActiveRecord < Backend::Base
5
+ class << self
6
+ def table_name_prefix
7
+ "permisi_"
8
+ end
9
+
10
+ def findsert_actor(aka)
11
+ Actor.find_or_create_by(aka: aka)
12
+ end
13
+
14
+ def actors
15
+ Actor.all
16
+ end
17
+
18
+ def roles
19
+ Role.all
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,15 @@
1
+ module Permisi
2
+ class Backend::ActiveRecord::Actor < ::ActiveRecord::Base
3
+ belongs_to :aka, polymorphic: true
4
+ has_many :actor_roles
5
+ has_many :roles, through: :actor_roles
6
+
7
+ def has_role?(role_slug)
8
+ roles.load.any? { |role| role.slug == role_slug.to_s }
9
+ end
10
+
11
+ def may?(action_path)
12
+ roles.load.any? { |role| role.allows?(action_path) }
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,6 @@
1
+ module Permisi
2
+ class Backend::ActiveRecord::ActorRole < ::ActiveRecord::Base
3
+ belongs_to :actor
4
+ belongs_to :role
5
+ end
6
+ end
@@ -0,0 +1,27 @@
1
+ module Permisi
2
+ class Backend::ActiveRecord::Role < ::ActiveRecord::Base
3
+ has_many :actor_roles
4
+ has_many :actors, through: :actor_roles
5
+ has_many :akas, through: :actors
6
+
7
+ validates_presence_of :name, :slug
8
+ validates_uniqueness_of :name, :slug
9
+
10
+ after_initialize :set_default_permissions
11
+ before_validation :sanitize_permissions
12
+
13
+ serialize :permissions, Permisi::PermissionUtil::Serializer
14
+
15
+ def allows?(action_path)
16
+ Permisi::PermissionUtil.allows?(self.permissions, action_path)
17
+ end
18
+
19
+ def set_default_permissions
20
+ self.permissions ||= Permisi.config.default_permissions if self.new_record?
21
+ end
22
+
23
+ def sanitize_permissions
24
+ self.permissions = Permisi::PermissionUtil.sanitize_permissions(self.permissions)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,6 @@
1
+ module Permisi
2
+ module Backend
3
+ class Base
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,9 @@
1
+ module Permisi
2
+ module Backend
3
+ class InvalidBackend < StandardError
4
+ def initialize(message = "Please specify a backend. For details, check https://github.com/ukazap/permisi#configuring-backend")
5
+ super
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ module Permisi
2
+ class Backend::Mongoid < Backend::Base
3
+ raise "under construction"
4
+ end
5
+ end
@@ -0,0 +1,29 @@
1
+ require "active_support/core_ext/class/attribute_accessors"
2
+
3
+ module Permisi
4
+ class Config
5
+ attr_accessor :backend, :permissions
6
+ attr_reader :default_permissions
7
+
8
+ def initialize
9
+ @permissions = ::HashWithIndifferentAccess.new
10
+ @default_permissions = ::HashWithIndifferentAccess.new
11
+ end
12
+
13
+ def backend=(chosen_backend)
14
+ if chosen_backend.is_a? Symbol
15
+ chosen_backend = "::Permisi::Backend::#{chosen_backend.to_s.classify}".constantize
16
+ end
17
+
18
+ @backend = chosen_backend
19
+ rescue NameError
20
+ raise Backend::InvalidBackend
21
+ end
22
+
23
+ def permissions=(permissions_hash)
24
+ permissions_hash = HashWithIndifferentAccess.new(permissions_hash)
25
+ @default_permissions = PermissionUtil.transform_namespace(permissions_hash)
26
+ @permissions = permissions_hash
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,86 @@
1
+ require "active_model/type"
2
+ require "active_support/hash_with_indifferent_access"
3
+
4
+ module Permisi
5
+ module PermissionUtil
6
+ class InvalidNamespace < StandardError; end
7
+
8
+ class << self
9
+ def allows?(hash, action_path)
10
+ return false unless hash.kind_of?(Hash)
11
+
12
+ action_path_arr = action_path.split(".")
13
+ (!Permisi.config.default_permissions.dig(*action_path_arr).nil? rescue false) &&
14
+ hash.dig(*action_path_arr) == true
15
+ end
16
+
17
+ def transform_namespace(namespace, current_path: nil)
18
+ HashWithIndifferentAccess.new.tap do |transformed|
19
+ namespace.each_pair do |key, value|
20
+ raise InvalidNamespace,
21
+ "`#{[current_path, key].compact.join(".")}` should be an array" unless value.is_a? Array
22
+
23
+ value.each.with_index do |arr_v, arr_i|
24
+ if arr_v.is_a?(Symbol)
25
+ transformed[key] ||= ::HashWithIndifferentAccess.new
26
+ if transformed[key].has_key? arr_v
27
+ raise InvalidNamespace, "duplicate entry: `#{[current_path, key, arr_v].compact.join(".")}`"
28
+ end
29
+
30
+ transformed[key][arr_v] = false
31
+ elsif arr_v.is_a?(Hash)
32
+ transform_namespace(arr_v,
33
+ current_path: [current_path, key].compact.join(".")).each_pair do |ts_k, ts_v|
34
+ transformed[key] ||= ::HashWithIndifferentAccess.new
35
+ if transformed[key].has_key? ts_k
36
+ raise InvalidNamespace, "duplicate entry: `#{[current_path, key, ts_k].compact.join(".")}`"
37
+ end
38
+
39
+ transformed[key][ts_k] = ts_v
40
+ end
41
+ else
42
+ raise InvalidNamespace,
43
+ "`#{[current_path, key].compact.join(".")}[#{arr_i}]` should be a symbol or a hash"
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ def sanitize_permissions(permission_hash)
51
+ __deeply_sanitize_permissions(permission_hash, template: Permisi.config.default_permissions)
52
+ end
53
+
54
+ private
55
+
56
+ def __deeply_sanitize_permissions(permission_hash, template: {})
57
+ HashWithIndifferentAccess.new.tap do |sanitized|
58
+ permission_hash.each_pair do |key, value|
59
+ next unless template.has_key?(key)
60
+
61
+ if value.is_a?(Hash)
62
+ sanitized[key] = __deeply_sanitize_permissions(value, template: template[key])
63
+ else
64
+ sanitized[key] = __cast_value_to_boolean(value)
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ def __cast_value_to_boolean(value)
71
+ bool = ActiveModel::Type::Boolean.new.cast(value)
72
+ bool ||= false
73
+ end
74
+ end
75
+
76
+ class Serializer
77
+ def self.dump(hash)
78
+ hash
79
+ end
80
+
81
+ def self.load(hash)
82
+ (hash.is_a?(Hash) ? hash : {}).with_indifferent_access
83
+ end
84
+ end
85
+ end
86
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Permisi
4
- VERSION = "0.0.1"
4
+ VERSION = "0.1.0"
5
5
  end
data/permisi.gemspec CHANGED
@@ -8,15 +8,21 @@ Gem::Specification.new do |spec|
8
8
  spec.authors = ["Ukaza Perdana"]
9
9
  spec.email = ["ukaza@hey.com"]
10
10
 
11
- spec.summary = "Simple and dynamic user roles and permissions scheme for Rails"
12
- spec.description = "Permisi provides a way of dynamically declaring user rights (a.k.a. permissions) using a simple role-based access control scheme. A user may be associated to multiple roles with a different set of rights in each role."
11
+ spec.summary = "Simple and dynamic role-based access control for Rails"
12
+
13
+ spec.description = <<~DESCRIPTION
14
+ Permisi provides a way of dynamically declaring user rights (a.k.a. permissions) using a simple role-based access control scheme.
15
+ A user may be associated to multiple roles with a different set of permissions in each role.
16
+ The roles and user-roles association can be dynamically defined and changed on runtime.
17
+ DESCRIPTION
18
+
13
19
  spec.homepage = "https://github.com/ukazap/permisi"
14
20
  spec.license = "MIT"
15
- spec.required_ruby_version = Gem::Requirement.new(">= 2.4.0")
21
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.4.4")
16
22
 
17
23
  spec.metadata["homepage_uri"] = spec.homepage
18
24
  spec.metadata["source_code_uri"] = spec.homepage
19
- spec.metadata["changelog_uri"] = "https://github.com/ukazap/permisi/blob.main/CHANGELOG.md"
25
+ spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
20
26
 
21
27
  # Specify which files should be added to the gem when it is released.
22
28
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
@@ -27,8 +33,10 @@ Gem::Specification.new do |spec|
27
33
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
28
34
  spec.require_paths = ["lib"]
29
35
 
30
- # Uncomment to register a new dependency of your gem
31
- # spec.add_dependency "example-gem", "~> 1.0"
36
+ spec.add_dependency "activesupport", ">= 3.2.0"
37
+ spec.add_dependency "activerecord", ">= 3.2.0"
38
+ spec.add_dependency "activemodel", ">= 3.2.0"
39
+ spec.add_dependency "zeitwerk", ["~> 2.4", ">= 2.4.2"]
32
40
 
33
41
  # For more information and examples about making a new gem, checkout our
34
42
  # guide at: https://bundler.io/guides/creating_gem.html
metadata CHANGED
@@ -1,18 +1,81 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: permisi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ukaza Perdana
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-02-04 00:00:00.000000000 Z
12
- dependencies: []
13
- description: Permisi provides a way of dynamically declaring user rights (a.k.a. permissions)
14
- using a simple role-based access control scheme. A user may be associated to multiple
15
- roles with a different set of rights in each role.
11
+ date: 2021-02-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 3.2.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 3.2.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: activerecord
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 3.2.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 3.2.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: activemodel
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 3.2.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 3.2.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: zeitwerk
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.4'
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: 2.4.2
65
+ type: :runtime
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - "~>"
70
+ - !ruby/object:Gem::Version
71
+ version: '2.4'
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: 2.4.2
75
+ description: |
76
+ Permisi provides a way of dynamically declaring user rights (a.k.a. permissions) using a simple role-based access control scheme.
77
+ A user may be associated to multiple roles with a different set of permissions in each role.
78
+ The roles and user-roles association can be dynamically defined and changed on runtime.
16
79
  email:
17
80
  - ukaza@hey.com
18
81
  executables: []
@@ -32,7 +95,20 @@ files:
32
95
  - Rakefile
33
96
  - bin/console
34
97
  - bin/setup
98
+ - lib/generators/permisi/install_generator.rb
99
+ - lib/generators/permisi/templates/initializer.rb
100
+ - lib/generators/permisi/templates/migration.rb
35
101
  - lib/permisi.rb
102
+ - lib/permisi/actable.rb
103
+ - lib/permisi/backend/active_record.rb
104
+ - lib/permisi/backend/active_record/actor.rb
105
+ - lib/permisi/backend/active_record/actor_role.rb
106
+ - lib/permisi/backend/active_record/role.rb
107
+ - lib/permisi/backend/base.rb
108
+ - lib/permisi/backend/invalid_backend.rb
109
+ - lib/permisi/backend/mongoid.rb
110
+ - lib/permisi/config.rb
111
+ - lib/permisi/permission_util.rb
36
112
  - lib/permisi/version.rb
37
113
  - permisi.gemspec
38
114
  homepage: https://github.com/ukazap/permisi
@@ -41,7 +117,7 @@ licenses:
41
117
  metadata:
42
118
  homepage_uri: https://github.com/ukazap/permisi
43
119
  source_code_uri: https://github.com/ukazap/permisi
44
- changelog_uri: https://github.com/ukazap/permisi/blob.main/CHANGELOG.md
120
+ changelog_uri: https://github.com/ukazap/permisi/blob/main/CHANGELOG.md
45
121
  post_install_message:
46
122
  rdoc_options: []
47
123
  require_paths:
@@ -50,7 +126,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
50
126
  requirements:
51
127
  - - ">="
52
128
  - !ruby/object:Gem::Version
53
- version: 2.4.0
129
+ version: 2.4.4
54
130
  required_rubygems_version: !ruby/object:Gem::Requirement
55
131
  requirements:
56
132
  - - ">="
@@ -60,5 +136,5 @@ requirements: []
60
136
  rubygems_version: 3.2.3
61
137
  signing_key:
62
138
  specification_version: 4
63
- summary: Simple and dynamic user roles and permissions scheme for Rails
139
+ summary: Simple and dynamic role-based access control for Rails
64
140
  test_files: []