permissionable 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +74 -0
- data/Rakefile +2 -0
- data/lib/permissionable.rb +34 -0
- data/lib/permissionable/permissions.rb +81 -0
- data/lib/permissionable/version.rb +3 -0
- data/permissionable.gemspec +24 -0
- data/spec/dummy.rb +10 -0
- data/spec/permissionable_spec.rb +54 -0
- data/spec/permissions_spec.rb +76 -0
- metadata +102 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ac8b647c4c845648114c06ce701efeaf427a6c66
|
4
|
+
data.tar.gz: 435c2593102b61b7ec1c0ab917ae76584e1885cb
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4995603b3f0610a7dea21aa64d7a42041de7647f4afcfc885e7d78162454b24dce81d13e78e2cc0988b3ba506b3b12f76f719c715feef7f6be51b05553d65a96
|
7
|
+
data.tar.gz: 233663404592bc6c69ae5f9f9566f4b4dde70f6ae783c3b2e672e222be38906ff31e1d9286ba9767a21f544fc1c906c36484c7ff6989a8368fa148e3c0720b9e
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Robert Falkén
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# permissionable
|
2
|
+
## Installation
|
3
|
+
TODO
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
### Hooking up your model with permissionable super powers
|
7
|
+
Hooking up your model is as easy as including `Permissionable` and defining your permission **schema**
|
8
|
+
```
|
9
|
+
include Permissionable
|
10
|
+
permissions read: 1,
|
11
|
+
write: 2,
|
12
|
+
delete: 3
|
13
|
+
```
|
14
|
+
|
15
|
+
Your model doesn't have to be an ActiveRecord (AR) model, but Permissionable works out of the box with AR to persist the permissions. Just throw in a `permissions` column in the mix.
|
16
|
+
```
|
17
|
+
class AddPermissionsToResource < ActiveRecord::Migration
|
18
|
+
def change
|
19
|
+
add_column :resources, :permissions, :integer
|
20
|
+
end
|
21
|
+
end
|
22
|
+
```
|
23
|
+
|
24
|
+
If your're not using AR can also implement your own `#read_attribute` and `#write_attribute` methods to handle `read_attribute(:permissions)` and `write_attribute(:permissions, permissions_integer)`
|
25
|
+
|
26
|
+
### Working with permissions
|
27
|
+
#### Check for permissions
|
28
|
+
To check if your permissionable resource has a certain permission, use the [] method which will return true or false.
|
29
|
+
|
30
|
+
For instance, to check if your user has permission to :read_books,
|
31
|
+
`user.permissions[:read_books]`
|
32
|
+
|
33
|
+
You can also specify multiple permissions, which will check to see if your resource has all of the permissions provided (I.e. it will return false if any of the permissions are missing).
|
34
|
+
`user.permissions[:read_books, :delete_books]`
|
35
|
+
|
36
|
+
**Note** if you find the permissions[:read_books, :delete_books] syntax awkward, you can also use #include?
|
37
|
+
`user.permissions.include?(:read_books, :delete_books)`
|
38
|
+
|
39
|
+
#### Add permissions
|
40
|
+
You can add permissions to your resource with the familiar "append to"-syntax. To add permissions to :read_books to your user:
|
41
|
+
`user.permissions << :read_books`
|
42
|
+
|
43
|
+
You can also add an array of permissions yo your array of permissions:
|
44
|
+
`user.permissions << [:read_books, :delete_books]`
|
45
|
+
|
46
|
+
**Note** Again, if special syntax like this isn't really your cup of tea, you can also use #add
|
47
|
+
`user.permissions.add(:read_books, :delete_books)`
|
48
|
+
|
49
|
+
#### Remove permissions
|
50
|
+
Removing permissions from your resource is pretty straight forward:
|
51
|
+
`user.permissions.remove(:delete_books)`
|
52
|
+
|
53
|
+
### Examples
|
54
|
+
```
|
55
|
+
class User < ActiveRecord::Base
|
56
|
+
include Permissionable
|
57
|
+
|
58
|
+
permissions read: 1,
|
59
|
+
write: 2,
|
60
|
+
delete: 3
|
61
|
+
end
|
62
|
+
```
|
63
|
+
|
64
|
+
```
|
65
|
+
user = User.create
|
66
|
+
user.permissions[:read] # => false
|
67
|
+
user.permissions << :read
|
68
|
+
user.permissions[:read] # => true
|
69
|
+
user.permissions[:read, :write] # => false
|
70
|
+
user.permissions << :write
|
71
|
+
user.permissions[:read, :write] # => true
|
72
|
+
user.permissions.remove(:read)
|
73
|
+
user.permissions[:read, :write] # => false
|
74
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require "permissionable/version"
|
2
|
+
require "permissionable/permissions"
|
3
|
+
|
4
|
+
module Permissionable
|
5
|
+
|
6
|
+
def permissions
|
7
|
+
@permissions ||= Permissions.new(self)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Class methods from here on
|
11
|
+
module ClassMethods
|
12
|
+
def permissions(permission_definitions)
|
13
|
+
@permissions = {}
|
14
|
+
permission_definitions.each do |permission,i|
|
15
|
+
raise 'Can not assign 0' if i == 0
|
16
|
+
raise "#{i} is not an integer" unless i.is_a?(Fixnum)
|
17
|
+
if i == 1
|
18
|
+
@permissions[permission] = 1
|
19
|
+
else
|
20
|
+
@permissions[permission] = 2**(i-1)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_permissions
|
26
|
+
@permissions || {}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Automatically extend class methods when module is included
|
31
|
+
def self.included(base)
|
32
|
+
base.extend(ClassMethods)
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Permissionable
|
2
|
+
class Permissions
|
3
|
+
def initialize(owner)
|
4
|
+
@owner = owner
|
5
|
+
if @owner.respond_to?(:read_attribute)
|
6
|
+
@permission_integer = @owner.read_attribute(:permissions)
|
7
|
+
else
|
8
|
+
@permission_integer = 0
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_i
|
13
|
+
@permission_integer
|
14
|
+
end
|
15
|
+
|
16
|
+
# Add new permissions
|
17
|
+
# will safely ignore any permissions already granted
|
18
|
+
# Usage:
|
19
|
+
# MyModel.last.permissions.add(:write)
|
20
|
+
# MyModel.last.permissions.add(:write, :read)
|
21
|
+
def add(*permissions)
|
22
|
+
permissions.flatten!
|
23
|
+
control_definitions(permissions)
|
24
|
+
permissions.each do |permission|
|
25
|
+
new_permission = defined_permissions[permission]
|
26
|
+
@permission_integer += new_permission unless include?(permission)
|
27
|
+
end
|
28
|
+
sync_with_owner
|
29
|
+
end
|
30
|
+
alias_method :<<, :add
|
31
|
+
|
32
|
+
# Remove existing permissions
|
33
|
+
# will safely ignore any permissions not already granted
|
34
|
+
# Usage:
|
35
|
+
# resource.permissions.remove(:write)
|
36
|
+
# resource.permissions.remove(:write, :read)
|
37
|
+
def remove(*permissions)
|
38
|
+
permissions.flatten!
|
39
|
+
control_definitions(permissions)
|
40
|
+
permissions.each do |permission|
|
41
|
+
@permission_integer -= defined_permissions[permission] if include?(permission)
|
42
|
+
end
|
43
|
+
sync_with_owner
|
44
|
+
end
|
45
|
+
|
46
|
+
# Check if resource has permission
|
47
|
+
# Usage:
|
48
|
+
# resource.permissions.include?(:write)
|
49
|
+
# resource.permissions.include?(:write, :read)
|
50
|
+
# @param *permissions [Array]
|
51
|
+
# @return [TrueClass, FalseClass]
|
52
|
+
def include?(*permissions)
|
53
|
+
permissions.flatten!
|
54
|
+
control_definitions(permissions)
|
55
|
+
# Sum the corresponding int value of all
|
56
|
+
# permissions provided in the argument list
|
57
|
+
asserted = permissions.inject(0){ |mem, permission| mem + defined_permissions[permission] }
|
58
|
+
@permission_integer & asserted == asserted
|
59
|
+
end
|
60
|
+
alias_method :[], :include?
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def control_definitions(permissions)
|
65
|
+
permissions.each do |permission|
|
66
|
+
raise "Permission #{permission} not defined" if defined_permissions[permission].nil?
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def sync_with_owner
|
71
|
+
if @owner.respond_to?(:write_attribute)
|
72
|
+
@owner.write_attribute(:permission, @permission_integer)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def defined_permissions
|
77
|
+
@owner.class.get_permissions
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'permissionable/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "permissionable"
|
8
|
+
spec.version = Permissionable::VERSION
|
9
|
+
spec.authors = ["Robert Falkén"]
|
10
|
+
spec.email = ["f@lken.se"]
|
11
|
+
spec.summary = "Manage and check permissions for your models"
|
12
|
+
spec.description = "Integer backed permissions, powered by bitwise comparison"
|
13
|
+
spec.homepage = "https://github.com/robertfalken/permissionable"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
spec.add_development_dependency "rspec", "~> 3.3"
|
24
|
+
end
|
data/spec/dummy.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'pry'
|
2
|
+
require 'permissionable'
|
3
|
+
require 'dummy'
|
4
|
+
|
5
|
+
|
6
|
+
class FlakyDummy
|
7
|
+
include Permissionable
|
8
|
+
end
|
9
|
+
|
10
|
+
describe Permissionable do
|
11
|
+
context 'permission definitions' do
|
12
|
+
subject(:permissions) { Dummy.instance_variable_get(:@permissions) }
|
13
|
+
|
14
|
+
context 'valid' do
|
15
|
+
it { is_expected.to have_key(:run) }
|
16
|
+
it { is_expected.to have_key(:walk) }
|
17
|
+
it { is_expected.to have_key(:sit) }
|
18
|
+
it { is_expected.to have_key(:play) }
|
19
|
+
it { is_expected.to have_key(:eat) }
|
20
|
+
|
21
|
+
it 'creates a geometric sequence' do
|
22
|
+
expect(permissions[:run]).to eq(1)
|
23
|
+
expect(permissions[:walk]).to eq(2)
|
24
|
+
expect(permissions[:sit]).to eq(4)
|
25
|
+
expect(permissions[:play]).to eq(8)
|
26
|
+
expect(permissions[:eat]).to eq(16)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'invalid' do
|
31
|
+
it 'can not assign 0' do
|
32
|
+
expect{
|
33
|
+
FlakyDummy.permissions(nope: 0)
|
34
|
+
}.to raise_exception
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'can only assign integers' do
|
38
|
+
expect{
|
39
|
+
FlakyDummy.permissions(nope: '1')
|
40
|
+
}.to raise_exception
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'instance' do
|
46
|
+
it 'has permissions getter' do
|
47
|
+
dummy = Dummy.new
|
48
|
+
expect(dummy).to respond_to(:permissions)
|
49
|
+
expect(dummy.permissions).to be_an_instance_of(Permissionable::Permissions)
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'pry'
|
2
|
+
require 'permissionable'
|
3
|
+
require 'dummy'
|
4
|
+
|
5
|
+
describe Permissionable::Permissions do
|
6
|
+
before(:each) { @permissions = Permissionable::Permissions.new(Dummy.new) }
|
7
|
+
|
8
|
+
it 'implements #to_i' do
|
9
|
+
expect(@permissions.to_i).to eq(0)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'can add permission with <<' do
|
13
|
+
expect{
|
14
|
+
@permissions << :play
|
15
|
+
}.to change{@permissions.to_i}.by(8)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'can add permission with #add' do
|
19
|
+
expect{
|
20
|
+
@permissions << :walk
|
21
|
+
}.to change{@permissions.to_i}.by(2)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'can remove permission with #remove' do
|
25
|
+
@permissions << :play
|
26
|
+
expect{
|
27
|
+
@permissions.remove(:play)
|
28
|
+
}.to change{@permissions.to_i}.by(-8)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'is true with one included permission' do
|
32
|
+
@permissions << :run
|
33
|
+
expect(@permissions.include?(:run)).to be true
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'is false with one missing permission' do
|
37
|
+
@permissions << :run
|
38
|
+
expect(@permissions.include?(:walk)).to be false
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'is true for multiple existing permissions' do
|
42
|
+
@permissions << :run
|
43
|
+
@permissions << :walk
|
44
|
+
|
45
|
+
expect(@permissions.include?(:run, :walk)).to be true
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'it is false when one of asserted is missing' do
|
49
|
+
@permissions << :run
|
50
|
+
@permissions << :walk
|
51
|
+
expect(@permissions.include?(:run, :play)).to be false
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'can check with []' do
|
55
|
+
@permissions << :run
|
56
|
+
@permissions << :sit
|
57
|
+
expect(@permissions[:run]).to eq(true)
|
58
|
+
expect(@permissions[:sit, :run]).to eq(true)
|
59
|
+
expect(@permissions[:play]).to eq(false)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'writes attribute when adding permission' do
|
63
|
+
@owner = Dummy.new
|
64
|
+
@permissions = Permissionable::Permissions.new(@owner)
|
65
|
+
expect(@owner).to receive(:write_attribute).with(:permission, 2)
|
66
|
+
@permissions << :walk
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'writes attribute when removing permission' do
|
70
|
+
@owner = Dummy.new
|
71
|
+
@permissions = Permissionable::Permissions.new(@owner)
|
72
|
+
@permissions << :run
|
73
|
+
expect(@owner).to receive(:write_attribute).with(:permission, 0)
|
74
|
+
@permissions.remove :run
|
75
|
+
end
|
76
|
+
end
|
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: permissionable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Robert Falkén
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-07-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.3'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.3'
|
55
|
+
description: Integer backed permissions, powered by bitwise comparison
|
56
|
+
email:
|
57
|
+
- f@lken.se
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".gitignore"
|
63
|
+
- Gemfile
|
64
|
+
- LICENSE.txt
|
65
|
+
- README.md
|
66
|
+
- Rakefile
|
67
|
+
- lib/permissionable.rb
|
68
|
+
- lib/permissionable/permissions.rb
|
69
|
+
- lib/permissionable/version.rb
|
70
|
+
- permissionable.gemspec
|
71
|
+
- spec/dummy.rb
|
72
|
+
- spec/permissionable_spec.rb
|
73
|
+
- spec/permissions_spec.rb
|
74
|
+
homepage: https://github.com/robertfalken/permissionable
|
75
|
+
licenses:
|
76
|
+
- MIT
|
77
|
+
metadata: {}
|
78
|
+
post_install_message:
|
79
|
+
rdoc_options: []
|
80
|
+
require_paths:
|
81
|
+
- lib
|
82
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
requirements: []
|
93
|
+
rubyforge_project:
|
94
|
+
rubygems_version: 2.2.2
|
95
|
+
signing_key:
|
96
|
+
specification_version: 4
|
97
|
+
summary: Manage and check permissions for your models
|
98
|
+
test_files:
|
99
|
+
- spec/dummy.rb
|
100
|
+
- spec/permissionable_spec.rb
|
101
|
+
- spec/permissions_spec.rb
|
102
|
+
has_rdoc:
|