papers_please 0.0.1.beta → 0.0.2.beta

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 6a205acbfa3b9735fe92e5f28fd20d692f95d05f
4
- data.tar.gz: c4811ebdf8979ecdec29dc11b442daf1b053ba89
2
+ SHA256:
3
+ metadata.gz: d12ab3dd8981fb3a6fb74a02b0f34e917438792c2151562c9945a04c79d50d9d
4
+ data.tar.gz: 694b9b96b9057e122392a2382488bfbecc877b881cf681e07dcb066091d2bd8c
5
5
  SHA512:
6
- metadata.gz: 8ba41bdb2ac002fc19ff9166e92a9e6b7da129f9470251ed2e76db24f497fc5c51afa94d8953832527be4ef5267b547e0798bdf0e6a847b682b1fd87896f3b9f
7
- data.tar.gz: 54c2475662d529ad39c63468351f1f8a261471998119983a2249e5cebb82859b320b95f28653450943806dfcd3158adffb3c9e7e135f9bf4ad2e9679e8da34c9
6
+ metadata.gz: 80a53cd2301779d6a332c1c01d495f54035886d64a5d3d23e3434de008b5fac367e9a351eb96ef1a842b9c1ae4207045c28c739a43d0f3e78def7e7836f65680
7
+ data.tar.gz: cc6193a203effc3bff3f3351359550ac71fcf4ca83bd1d1f0cd31ed918f2a083c410638e9c6c2e2e1fd2ea21da3cb7555c57a34f3118bf8c5472a5d35e72abdb
data/.byebug_history ADDED
@@ -0,0 +1,60 @@
1
+ q
2
+ @policy
3
+ q
4
+ permission
5
+ c
6
+ permission
7
+ c
8
+ permission
9
+ c
10
+ permission
11
+ q
12
+ res.include?(obj)
13
+ posts.include?(obj)
14
+ posts.include(obj)
15
+ res
16
+ obj
17
+ c
18
+ obj
19
+ res.include?(obj)
20
+ res.include?
21
+ res
22
+ c
23
+ u.posts
24
+ u
25
+ c
26
+ u
27
+ c
28
+ role
29
+ q
30
+ role
31
+ q
32
+ applicable_roles.count
33
+ applicable_roles
34
+ c
35
+ q
36
+ @user
37
+ role.applies_to?(@user)
38
+ role
39
+ c
40
+ q
41
+ applicable_roles
42
+ c
43
+ q
44
+ klass
45
+ action
46
+ permissions
47
+ permission_exists?(action, klass)
48
+ action
49
+ q
50
+ n
51
+ s
52
+ actions
53
+ c
54
+ n
55
+ role
56
+ c
57
+ n
58
+ roles.key?(:admin)
59
+ roles
60
+ name
data/Gemfile.lock ADDED
@@ -0,0 +1,45 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ papers_please (0.0.1.beta)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ byebug (10.0.2)
10
+ diff-lcs (1.3)
11
+ docile (1.3.1)
12
+ json (2.1.0)
13
+ rake (10.5.0)
14
+ rspec (3.7.0)
15
+ rspec-core (~> 3.7.0)
16
+ rspec-expectations (~> 3.7.0)
17
+ rspec-mocks (~> 3.7.0)
18
+ rspec-core (3.7.1)
19
+ rspec-support (~> 3.7.0)
20
+ rspec-expectations (3.7.0)
21
+ diff-lcs (>= 1.2.0, < 2.0)
22
+ rspec-support (~> 3.7.0)
23
+ rspec-mocks (3.7.0)
24
+ diff-lcs (>= 1.2.0, < 2.0)
25
+ rspec-support (~> 3.7.0)
26
+ rspec-support (3.7.1)
27
+ simplecov (0.16.1)
28
+ docile (~> 1.1)
29
+ json (>= 1.8, < 3)
30
+ simplecov-html (~> 0.10.0)
31
+ simplecov-html (0.10.2)
32
+
33
+ PLATFORMS
34
+ ruby
35
+
36
+ DEPENDENCIES
37
+ bundler (~> 1.16)
38
+ byebug
39
+ papers_please!
40
+ rake (~> 10.0)
41
+ rspec (~> 3.0)
42
+ simplecov
43
+
44
+ BUNDLED WITH
45
+ 1.16.1
data/README.md CHANGED
@@ -2,7 +2,98 @@
2
2
 
3
3
  A roles and permissions gem from Apsis Labs.
4
4
 
5
- **NOTE**: Still under heavy development, definitely not suitable for anything remotely resembling production usage.
5
+ **NOTE**: Still under heavy development, definitely not suitable for anything remotely resembling production usage. Very unlikely to even work.
6
+
7
+ ## Example
8
+
9
+ ```ruby
10
+ # app/policies/access_policy.rb
11
+ class AccessPolicy < PapersPlease::Policy
12
+ config do
13
+ # Define a role in a block
14
+ role :admin, (proc { |u| u.admin? }) do
15
+ grant [:manage, :archive], Post
16
+ end
17
+
18
+ # Define a role in a class
19
+ role :member, MemberRole
20
+
21
+ # Define a role with no predicate
22
+ role :guest do
23
+ grant [:read], Post, predicate: (proc { |u, post| !post.archived? })
24
+ end
25
+ end
26
+ end
27
+
28
+ # app/policies/roles/member_role.rb
29
+ class MemberRole < PapersPlease::Role
30
+ predicate { |user| user.member? }
31
+
32
+ config do
33
+ grant :create, Post
34
+ grant [:read, :update], Post, query: (proc { |u| u.posts })
35
+ grant :archive, Post, query: method(:published_posts)
36
+ end
37
+
38
+ private
39
+
40
+ def published_posts(user, klass)
41
+ user.posts.where(status: :published)
42
+ end
43
+ end
44
+
45
+ # app/controllers/posts_controller.rb
46
+ class PostsController < ApplicationController
47
+ # GET /posts
48
+ def index
49
+ @posts = policy.query(:read, Post)
50
+ render json: @posts
51
+ end
52
+
53
+ # GET /posts/:id
54
+ def show
55
+ @post = Post.find(params[:id])
56
+ policy.authorize! :read, @post
57
+
58
+ render json: @post
59
+ end
60
+
61
+ # POST /posts/:id/archive
62
+ def archive
63
+ @post = Post.find(params[:id])
64
+ policy.authorize! :archive, @post
65
+
66
+ @post.update!(archived: true)
67
+ render json: @post
68
+ end
69
+ end
70
+ ```
71
+
72
+ ## A helpful CLI
73
+
74
+ ```bash
75
+ $ rails papers_please:roles
76
+
77
+ # =>
78
+ # | role | permission | object |
79
+ # | :------ | :--------- | :----- |
80
+ # | :admin | :create | Post |
81
+ # | | :read | Post |
82
+ # | | :update | Post |
83
+ # | | :destroy | Post |
84
+ # | | :archive | Post |
85
+ # | | | |
86
+ # | :member | :create | Post |
87
+ # | | :read | Post |
88
+ # | | :update | Post |
89
+ # | | :archive | Post |
90
+ # | | | |
91
+ # | :guest | :read | Post |
92
+
93
+ $ rails papers_please:annotate [app/policies/access_policy.rb]
94
+
95
+ # => output roles table to top of AccessPolicy file
96
+ ```
6
97
 
7
98
  ## Installation
8
99
 
@@ -24,6 +115,15 @@ Or install it yourself as:
24
115
 
25
116
  TODO: Write usage instructions here
26
117
 
118
+ ## Theory
119
+
120
+ The structure of `papers_please` is very simple. At its core, it is a mechanism for storing and retrieving `Procs`. In an authorization context, these `Procs` answer two questions:
121
+
122
+ 1. Given a specific user and a specific permission, which objects am I allowed to operate on?
123
+ 2. Given a specific user and a specific object, do I have a specific permission?
124
+
125
+ The machinery of `papers_please` tries to simplify the organization and subsequent access to these questions as much as possible.
126
+
27
127
  ## Development
28
128
 
29
129
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -34,6 +134,10 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
34
134
 
35
135
  Bug reports and pull requests are welcome on GitHub at https://github.com/wkirby/papers_please.
36
136
 
137
+ ## Special Thanks
138
+
139
+ This owes its existence to [`AccessGranted`](https://github.com/chaps-io/access-granted). Thanks!
140
+
37
141
  ## License
38
142
 
39
143
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,11 @@
1
+ module PapersPlease
2
+ class Error < StandardError; end
3
+
4
+ class AccessDenied < Error; end
5
+
6
+ class DuplicatePermission < Error; end
7
+ class InvalidPermission < Error; end
8
+
9
+ class DuplicateScope < Error; end
10
+ class InvalidScope < Error; end
11
+ end
@@ -0,0 +1,36 @@
1
+ module PapersPlease
2
+ class Permission
3
+ attr_accessor :key, :subject, :query, :predicate
4
+
5
+ def initialize(key, subject, query: nil, predicate: nil)
6
+ @key = key
7
+ @subject = subject
8
+ @query = query
9
+ @predicate = predicate
10
+ end
11
+
12
+ def matches?(key, subject)
13
+ key_matches?(key) && subject_matches?(subject)
14
+ end
15
+
16
+ def granted?(*args)
17
+ return predicate.call(*args) if predicate.is_a? Proc
18
+ false
19
+ end
20
+
21
+ def fetch(*args)
22
+ return query.call(*args) if query.is_a? Proc
23
+ nil
24
+ end
25
+
26
+ private
27
+
28
+ def key_matches?(key)
29
+ key == @key
30
+ end
31
+
32
+ def subject_matches?(subject)
33
+ subject == @subject || subject.class <= @subject
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,68 @@
1
+ module PapersPlease
2
+ class Policy
3
+ attr_accessor :roles
4
+ attr_reader :user
5
+
6
+ def initialize(user)
7
+ @user = user
8
+ @roles = {}
9
+ @cache = {}
10
+
11
+ configure
12
+ end
13
+
14
+ def configure
15
+ raise NotImplementedError
16
+ end
17
+
18
+ # Add a role to the Policy
19
+ def add_role(name, predicate = nil, &block)
20
+ name = name.to_sym
21
+ raise DuplicateRole if roles.key?(name)
22
+
23
+ role = Role.new(name, predicate: predicate, definition: block)
24
+ roles[name] = role
25
+
26
+ role
27
+ end
28
+ alias role add_role
29
+
30
+ # Look up a stored permission block and call with
31
+ # the current user and subject
32
+ def can?(action, subject = nil)
33
+ applicable_roles.each do |_, role|
34
+ permission = role.find_permission(action, subject)
35
+ return permission.granted?(user, subject, action) unless permission.nil?
36
+ end
37
+
38
+ false
39
+ end
40
+
41
+ def cannot?(*args)
42
+ !can?(*args)
43
+ end
44
+
45
+ def authorize!(action, subject)
46
+ raise AccessDenied.new(action, subject) if cannot?(action, subject)
47
+ subject
48
+ end
49
+
50
+ # Look up a stored scope block and call with the
51
+ # current user and class
52
+ def scope_for(action, klass)
53
+ applicable_roles.each do |_, role|
54
+ permission = role.find_permission(action, klass)
55
+ return permission.fetch(user, klass, action) unless permission.nil?
56
+ end
57
+
58
+ nil
59
+ end
60
+
61
+ # Fetch roles that apply to the current user
62
+ def applicable_roles
63
+ @applicable_roles ||= roles.select do |_, role|
64
+ role.applies_to?(user)
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,82 @@
1
+ module PapersPlease
2
+ class Role
3
+ attr_reader :name, :predicate, :permissions
4
+
5
+ def initialize(name, predicate: nil, definition: nil)
6
+ @name = name
7
+ @predicate = predicate
8
+ @permissions = []
9
+
10
+ instance_eval(&definition) unless definition.nil?
11
+ end
12
+
13
+ def applies_to?(user)
14
+ return @predicate.call(user) if @predicate.is_a? Proc
15
+ true
16
+ end
17
+
18
+ def add_permission(actions, klass, query: nil, predicate: nil)
19
+ prepare_actions(actions).each do |action|
20
+ raise DuplicatePermission if permission_exists?(action, klass)
21
+
22
+ has_query = query.is_a?(Proc)
23
+ has_predicate = predicate.is_a?(Proc)
24
+ permission = Permission.new(action, klass)
25
+
26
+ if has_query && has_predicate
27
+ # Both query & predicate provided
28
+
29
+ permission.query = query
30
+ permission.predicate = predicate
31
+ elsif has_query && !has_predicate
32
+ # Only query provided
33
+
34
+ permission.query = query
35
+
36
+ if action == :create && actions == :manage
37
+ # If the action is :create, expanded from :manage
38
+ # then we set the default all predicate
39
+ permission.predicate = (proc { true })
40
+ else
41
+ # Otherwise the default predicate is to check
42
+ # for inclusion in the returned relationship
43
+ permission.predicate = (proc { |user, obj|
44
+ res = query.call(user, klass, action)
45
+ res.respond_to?(:include?) && res.include?(obj)
46
+ })
47
+ end
48
+ elsif !has_query && has_predicate
49
+ # Only predicate provided
50
+
51
+ permission.predicate = predicate
52
+ else
53
+ # Neither provided
54
+ permission.query = (proc { klass.all })
55
+ permission.predicate = (proc { true })
56
+ end
57
+
58
+ permissions << permission
59
+ end
60
+ end
61
+ alias grant add_permission
62
+
63
+ def find_permission(action, subject)
64
+ permissions.detect do |permission|
65
+ permission.matches? action, subject
66
+ end
67
+ end
68
+
69
+ def permission_exists?(action, subject)
70
+ !find_permission(action, subject).nil?
71
+ end
72
+
73
+ private
74
+
75
+ # Wrap actions, translating :manage into :crud
76
+ def prepare_actions(action)
77
+ Array(*[action]).flat_map do |a|
78
+ a == :manage ? [:create, :read, :update, :destroy] : [a]
79
+ end
80
+ end
81
+ end
82
+ end
@@ -1,3 +1,3 @@
1
1
  module PapersPlease
2
- VERSION = "0.0.1.beta"
2
+ VERSION = "0.0.2.beta"
3
3
  end
data/lib/papers_please.rb CHANGED
@@ -1,4 +1,8 @@
1
- require "papers_please/version"
1
+ require 'papers_please/version'
2
+ require 'papers_please/errors'
3
+ require 'papers_please/policy'
4
+ require 'papers_please/role'
5
+ require 'papers_please/permission'
2
6
 
3
7
  module PapersPlease
4
8
  # Your code goes here...
@@ -24,4 +24,6 @@ Gem::Specification.new do |spec|
24
24
  spec.add_development_dependency "bundler", "~> 1.16"
25
25
  spec.add_development_dependency "rake", "~> 10.0"
26
26
  spec.add_development_dependency "rspec", "~> 3.0"
27
+ spec.add_development_dependency "byebug"
28
+ spec.add_development_dependency 'simplecov'
27
29
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: papers_please
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1.beta
4
+ version: 0.0.2.beta
5
5
  platform: ruby
6
6
  authors:
7
7
  - Apsis Labs
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-07-20 00:00:00.000000000 Z
11
+ date: 2018-10-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,6 +52,34 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: byebug
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
55
83
  description:
56
84
  email:
57
85
  - wyatt@apsis.io
@@ -59,16 +87,22 @@ executables: []
59
87
  extensions: []
60
88
  extra_rdoc_files: []
61
89
  files:
90
+ - ".byebug_history"
62
91
  - ".gitignore"
63
92
  - ".rspec"
64
93
  - ".travis.yml"
65
94
  - Gemfile
95
+ - Gemfile.lock
66
96
  - LICENSE.txt
67
97
  - README.md
68
98
  - Rakefile
69
99
  - bin/console
70
100
  - bin/setup
71
101
  - lib/papers_please.rb
102
+ - lib/papers_please/errors.rb
103
+ - lib/papers_please/permission.rb
104
+ - lib/papers_please/policy.rb
105
+ - lib/papers_please/role.rb
72
106
  - lib/papers_please/version.rb
73
107
  - papers_please.gemspec
74
108
  homepage: http://apsis.io
@@ -91,7 +125,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
91
125
  version: 1.3.1
92
126
  requirements: []
93
127
  rubyforge_project:
94
- rubygems_version: 2.6.13
128
+ rubygems_version: 2.7.6
95
129
  signing_key:
96
130
  specification_version: 4
97
131
  summary: A roles & permissions gem for ruby applications.