accessly 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,122 @@
1
+ require "accessly/base"
2
+
3
+ module Accessly
4
+ # Accessly::Query is the interface that hides the implementation
5
+ # of the data layer. Ask Accessly::Query whether an actor
6
+ # has permission on a record, ask it for a list of permitted records for the record
7
+ # type, and ask it whether an actor has a general permission not
8
+ # related to any certain record or record type.
9
+ class Query < Base
10
+
11
+ # Create an instance of Accessly::Query.
12
+ # Lookups are cached in inherited object(s) to prevent redundant calls to the database.
13
+ # Pass in a Hash or ActiveRecord::Base for actors if the actor(s)
14
+ # inherit some permissions from other actors in the system. This may happen
15
+ # when you have a user in one or more groups or organizations with their own
16
+ # access control permissions.
17
+ #
18
+ # @param actors [Hash, ActiveRecord::Base] The actor(s) we're checking permission(s)
19
+ #
20
+ # @example
21
+ # # Create a new object with a single actor
22
+ # Accessly::Query.new(user)
23
+ # @example
24
+ # # Create a new object with multiple actors
25
+ # Accessly::Query.new(User => user.id, Group => [1,2], Organization => Organization.where(user_id: user.id).pluck(:id))
26
+ def initialize(actors)
27
+ super(actors)
28
+ end
29
+
30
+ # Check whether an actor has a given permission.
31
+ # @return [Boolean]
32
+ # @overload can?(action_id, namespace)
33
+ # Ask whether the actor has permission to perform action_id
34
+ # in the given namespace. Multiple actions can have the same id
35
+ # as long as their namespace is different. The namespace can be
36
+ # any String. We recommend using namespace to group a class of
37
+ # permissions, such as to group parts of a particular feature
38
+ # in your application.
39
+ #
40
+ # @param action_id [Integer, Array<Integer>] The action or actions we're checking whether the actor has. If this is an array, then the check is ORed.
41
+ # @param namespace [String] The namespace of the given action_id.
42
+ # @return [Boolean] Returns true if actor has been granted the permission, false otherwise.
43
+ #
44
+ # @example
45
+ # # Can the user perform the action with id 3 for posts?
46
+ # Accessly.can?(user, 3, "posts")
47
+ # @example
48
+ # # Can the user perform the action with id 5 for Posts?
49
+ # Accessly::Query.new(user).can?(5, Post)
50
+ # @example
51
+ # # Can the sets of actors perform the action with id 5 for Posts?
52
+ # Accessly::Query.new(User => user.id, Group => [1,2]).can?(5, Post)
53
+ # @example
54
+ # # Can the user on segment 1 perform the action with id 5 for Posts
55
+ # Accessly::Query.new(user).on_segment(1).can?(5, Post)
56
+ # @example
57
+ # # Can the sets of actors on segment 1 perform the action with id 5 for Posts
58
+ # Accessly::Query.new(User => user.id, Group => [1,2]).on_segment(1).can?(5, Post)
59
+ #
60
+ # @overload can?(action_id, object_type, object_id)
61
+ # Ask whether the actor has permission to perform action_id
62
+ # on a given record.
63
+ #
64
+ # @param action_id [Integer, Array<Integer>] The action or actions we're checking whether the actor has. If this is an array, then the check is ORed.
65
+ # @param object_type [ActiveRecord::Base] The ActiveRecord model which we're checking for permission on.
66
+ # @param object_id [Integer] The id of the ActiveRecord object which we're checking for permission on.
67
+ # @return [Boolean] Returns true if actor has been granted the permission on the specified record, false otherwise.
68
+ #
69
+ # @example
70
+ # # Can the user perform the action with id 5 for the Post with id 7?
71
+ # Accessly::Query.new(user).can?(5, Post, 7)
72
+ # @example
73
+ # # Can the sets of actors perform the action with id 5 for the Post with id 7?
74
+ # Accessly::Query.new(User => user.id, Group => [1,2]).can?(5, Post, 7)
75
+ # @example
76
+ # # Can the user on segment 1 perform the action with id 5 for the Post with id 7?
77
+ # Accessly::Query.new(user).on_segment(1).can?(5, Post, 7)
78
+ # @example
79
+ # # Can the sets of actors on segment 1 perform the action with id 5 for the Post with id 7?
80
+ # Accessly::Query.new(User => user.id, Group => [1,2]).on_segment(1).can?(5, Post, 7)
81
+ def can?(action_id, object_type, object_id = nil)
82
+ if object_id.nil?
83
+ permitted_action_query.can?(action_id, object_type)
84
+ else
85
+ permitted_action_on_object_query.can?(action_id, object_type, object_id)
86
+ end
87
+ end
88
+
89
+ # Returns an ActiveRecord::Relation of ids in the namespace for
90
+ # which the actor has permission to perform action_id.
91
+ #
92
+ # @param action_id [Integer] The action we're checking on the actor in the namespace.
93
+ # @param namespace [String] The namespace to check actor permissions.
94
+ # @return [ActiveRecord::Relation]
95
+ #
96
+ # @example
97
+ # # Give me the list of Post ids on which the user has permission to perform action_id 3
98
+ # Accessly::Query.new(user).list(3, Post)
99
+ # @example
100
+ # # Give me the list of Post ids on which the user has permission to perform action_id 3 on segment 1
101
+ # Accessly::Query.new(user).on_segment(1).list(3, Post)
102
+ # @example
103
+ # # Give me the list of Post ids on which the user and its groups has permission to perform action_id 3
104
+ # Accessly::Query.new(User => user.id, Group => [1,2]).list(3, Post)
105
+ # @example
106
+ # # Give me the list of Post ids on which the user and its groups has permission to perform action_id 3 on segment 1
107
+ # Accessly::Query.new(User => user.id, Group => [1,2]).on_segment(1).list(3, Post)
108
+ def list(action_id, namespace)
109
+ permitted_action_on_object_query.list(action_id, namespace)
110
+ end
111
+
112
+ private
113
+
114
+ def permitted_action_query
115
+ @_permitted_action_query ||= Accessly::PermittedActions::Query.new(@actors, @segment_id)
116
+ end
117
+
118
+ def permitted_action_on_object_query
119
+ @_permitted_action_on_object_query ||= Accessly::PermittedActions::OnObjectQuery.new(@actors, @segment_id)
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,24 @@
1
+ module Accessly
2
+ class QueryBuilder
3
+
4
+ # Builds a query with a series of actors 'OR' together
5
+ #
6
+ # Use like this:
7
+ # `Accessly::QueryBuilder.with_actors(PermittedActionOnObject, {User => 1, Group => [2,3]})`
8
+ #
9
+ # @param query [ActiveRecord::Relation] The relation on which to append the where clause
10
+ # @param actors [Hash] A hash of actors where the key is the object/classname and the value is an Integer or array of Integers
11
+ # @return [ActiveRecord::Relation]
12
+ def self.with_actors(query, actors)
13
+ result_query = nil
14
+ actors.each do |key, value|
15
+ result_query = if result_query.nil?
16
+ query.where(actor_type: String(key), actor_id: value)
17
+ else
18
+ result_query.or(query.where(actor_type: String(key), actor_id: value))
19
+ end
20
+ end
21
+ result_query
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,3 @@
1
+ module Accessly
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,5 @@
1
+ Description:
2
+ Install Accessly components inside a rails project
3
+
4
+ Example:
5
+ rails generate accessly:install
@@ -0,0 +1,51 @@
1
+ require "rails/generators"
2
+ require "rails/generators/active_record"
3
+
4
+ module Accessly
5
+ module Generators
6
+ class InstallGenerator < Rails::Generators::Base
7
+ include Rails::Generators::Migration
8
+ source_root File.expand_path("../templates", __FILE__)
9
+
10
+ def create_accessly_migration
11
+ copy_migration "create_permitted_actions.rb"
12
+ copy_migration "create_permitted_action_on_objects.rb"
13
+ end
14
+
15
+ private
16
+
17
+ def copy_migration(migration_name, config = {})
18
+ unless migration_exists?(migration_name)
19
+ migration_template(
20
+ "db/migrate/#{migration_name}",
21
+ "db/migrate/#{migration_name}",
22
+ config.merge(migration_version: migration_version),
23
+ )
24
+ end
25
+ end
26
+
27
+ def migration_exists?(name)
28
+ existing_migrations.include?(name)
29
+ end
30
+
31
+ def existing_migrations
32
+ @existing_migrations ||= Dir.glob("db/migrate/*.rb").map do |file|
33
+ migration_name_without_timestamp(file)
34
+ end
35
+ end
36
+
37
+ def migration_name_without_timestamp(file)
38
+ file.sub(%r{^.*(db/migrate/)(?:\d+_)?}, '')
39
+ end
40
+
41
+ # necessary to generate timestamps when using `create_migration`
42
+ def self.next_migration_number(dir)
43
+ ActiveRecord::Generators::Base.next_migration_number(dir)
44
+ end
45
+
46
+ def migration_version
47
+ "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,15 @@
1
+ class CreatePermittedActionOnObjects < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ create_table :accessly_permitted_action_on_objects, force: true, id: :uuid do |t|
4
+ t.column :segment_id, :integer, default: -1
5
+ t.column :action, :integer, null: false
6
+ t.column :actor_id, :integer, null: false
7
+ t.column :actor_type, :string, null: false
8
+ t.column :object_type, :string, null: false
9
+ t.column :object_id, :integer, null: false
10
+ end
11
+
12
+ add_index(:accessly_permitted_action_on_objects, [:segment_id, :actor_type, :actor_id, :object_type, :object_id, :action], unique: true, name: "acessly_paoo_uniq_table_idx")
13
+ add_index(:accessly_permitted_action_on_objects, [:segment_id, :object_type, :object_id, :action], name: "acessly_paoo_on_object_idx")
14
+ end
15
+ end
@@ -0,0 +1,13 @@
1
+ class CreatePermittedActions < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ create_table :accessly_permitted_actions, force: true, id: :uuid do |t|
4
+ t.column :segment_id, :integer, default: -1
5
+ t.column :action, :integer, null: false
6
+ t.column :actor_id, :integer, null: false
7
+ t.column :actor_type, :string, null: false
8
+ t.column :object_type, :string, null: false
9
+ end
10
+
11
+ add_index(:accessly_permitted_actions, [:segment_id, :actor_type, :actor_id, :object_type, :action], unique: true, name: "acessly_pa_uniq_table_idx")
12
+ end
13
+ end
metadata ADDED
@@ -0,0 +1,176 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: accessly
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Aaron Milam
8
+ - Eddie Hourigan
9
+ - Ross Reinhardt
10
+ autorequire:
11
+ bindir: exe
12
+ cert_chain: []
13
+ date: 2018-03-28 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activerecord
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - "~>"
20
+ - !ruby/object:Gem::Version
21
+ version: '5.0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - "~>"
27
+ - !ruby/object:Gem::Version
28
+ version: '5.0'
29
+ - !ruby/object:Gem::Dependency
30
+ name: bundler
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - "~>"
34
+ - !ruby/object:Gem::Version
35
+ version: '1.16'
36
+ type: :development
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - "~>"
41
+ - !ruby/object:Gem::Version
42
+ version: '1.16'
43
+ - !ruby/object:Gem::Dependency
44
+ name: rake
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '10.0'
50
+ type: :development
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - "~>"
55
+ - !ruby/object:Gem::Version
56
+ version: '10.0'
57
+ - !ruby/object:Gem::Dependency
58
+ name: minitest
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '5.0'
64
+ type: :development
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - "~>"
69
+ - !ruby/object:Gem::Version
70
+ version: '5.0'
71
+ - !ruby/object:Gem::Dependency
72
+ name: database_cleaner
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - "~>"
76
+ - !ruby/object:Gem::Version
77
+ version: '1.5'
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - "~>"
83
+ - !ruby/object:Gem::Version
84
+ version: '1.5'
85
+ - !ruby/object:Gem::Dependency
86
+ name: sqlite3
87
+ requirement: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - "~>"
90
+ - !ruby/object:Gem::Version
91
+ version: '1.3'
92
+ type: :development
93
+ prerelease: false
94
+ version_requirements: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - "~>"
97
+ - !ruby/object:Gem::Version
98
+ version: '1.3'
99
+ - !ruby/object:Gem::Dependency
100
+ name: rails
101
+ requirement: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - "~>"
104
+ - !ruby/object:Gem::Version
105
+ version: '5.0'
106
+ type: :development
107
+ prerelease: false
108
+ version_requirements: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - "~>"
111
+ - !ruby/object:Gem::Version
112
+ version: '5.0'
113
+ description: Use the policy pattern to define access control mechanisms in Rails.
114
+ Store user-level, group-level, or org-level permission on any given record or concept
115
+ in the database with ultra-fast lookups.
116
+ email:
117
+ - devops@lessonly.com
118
+ executables: []
119
+ extensions: []
120
+ extra_rdoc_files: []
121
+ files:
122
+ - ".editorconfig"
123
+ - ".gitignore"
124
+ - ".ruby-version"
125
+ - ".tool-versions"
126
+ - Gemfile
127
+ - Gemfile.lock
128
+ - LICENSE
129
+ - LICENSE.txt
130
+ - README.md
131
+ - Rakefile
132
+ - accessly.gemspec
133
+ - bin/console
134
+ - bin/setup
135
+ - lib/accessly.rb
136
+ - lib/accessly/base.rb
137
+ - lib/accessly/models/permitted_action.rb
138
+ - lib/accessly/models/permitted_action_on_object.rb
139
+ - lib/accessly/permission/grant.rb
140
+ - lib/accessly/permission/revoke.rb
141
+ - lib/accessly/permitted_actions/base.rb
142
+ - lib/accessly/permitted_actions/on_object_query.rb
143
+ - lib/accessly/permitted_actions/query.rb
144
+ - lib/accessly/policy/base.rb
145
+ - lib/accessly/query.rb
146
+ - lib/accessly/query_builder.rb
147
+ - lib/accessly/version.rb
148
+ - lib/generators/accessly/install/USAGE
149
+ - lib/generators/accessly/install/install_generator.rb
150
+ - lib/generators/accessly/install/templates/db/migrate/create_permitted_action_on_objects.rb
151
+ - lib/generators/accessly/install/templates/db/migrate/create_permitted_actions.rb
152
+ homepage: https://github.com/lessonly/accessly
153
+ licenses:
154
+ - MIT
155
+ metadata: {}
156
+ post_install_message:
157
+ rdoc_options: []
158
+ require_paths:
159
+ - lib
160
+ required_ruby_version: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ required_rubygems_version: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - ">="
168
+ - !ruby/object:Gem::Version
169
+ version: '0'
170
+ requirements: []
171
+ rubyforge_project:
172
+ rubygems_version: 2.6.13
173
+ signing_key:
174
+ specification_version: 4
175
+ summary: Simplified access control in Rails
176
+ test_files: []