crows 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (7) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +149 -0
  4. data/crows.gemspec +20 -0
  5. data/lib/crows.rb +76 -0
  6. data/test/test_crows.rb +121 -0
  7. metadata +68 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 45a31cd6e8c73b9a5ea6b587986da193074bc2d6
4
+ data.tar.gz: e51b2f94197a437e4dec56e166c99eec206bfa88
5
+ SHA512:
6
+ metadata.gz: 9cac3c422c581712a967212b6a3516bd7fa915766fe62c4737ae38c69dfcd3bd6721eac17a993a48d72d1be5dede1172e424a826379c10e9ab4681660bb887fb
7
+ data.tar.gz: 1d24ab84eaa80027896b71cf58d54e3d65577b4b38602eb0c03c858efabe7d5189149375ca1b13d33bb47f8c2d1f98fcc9a7e7a23099a58368debdc7e404c70f
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Julio Lopez
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,149 @@
1
+ # Crows
2
+
3
+ Inspired by [Pundit](https://github.com/elabs/pundit), Crows is a micro framework-agnostic library for authorization in Ruby classes.
4
+
5
+ A set of crows to authorized your users, because the night is dark and full of terrors...
6
+
7
+ Crows provide you with a few helpers to check if `current_user` can make operations into some records. This gives you the freedom to build your own plain Ruby classes to make authorization works easily, without the painful of bigs DSLs or something like that.
8
+
9
+ ## Installation
10
+
11
+ Installing Crows is as simple as running:
12
+
13
+ ```
14
+ $ gem install crows
15
+ ```
16
+
17
+ Include Crows in your Gemfile with gem 'crows' or require it with require 'crows'.
18
+
19
+ Usage
20
+ -----
21
+
22
+ Include Crows in your main class, can be your controller, your API class, etc.
23
+
24
+ ``` ruby
25
+ class ApplicationController < ActionController::Base
26
+ include Crows
27
+ end
28
+ ```
29
+
30
+ Crows exist around the notion of Crow classes.
31
+
32
+ ``` ruby
33
+ class PostCrow
34
+ attr_reader :user, :post
35
+
36
+ def initialize(user, post)
37
+ @user = user
38
+ @post = post
39
+ end
40
+
41
+ def destroy?
42
+ user.admin? and post.draft?
43
+ end
44
+ end
45
+ ```
46
+
47
+ In the above example:
48
+
49
+ - We define a Crow class, its name is composed by a name of a class whose authorization you want to check, plus the suffix "Crow".
50
+ - In its `initialize` the class receive as first argument the user(captured by your method `current_user`) and as second argument your instance of the class for which was created the current crow.
51
+ - Finally the Crow class implements methods to check if user can be authorized to do a particular action.
52
+
53
+ Lastly, you have the `authorize` method for use your Crow class.
54
+
55
+ ``` ruby
56
+ authorize @post, :destroy?
57
+ ```
58
+
59
+ The above line will check your PostCrow class to authorize if current user can destroy the @post
60
+
61
+ Raising an exception if is not true the result of `destroy?`
62
+
63
+ ``` ruby
64
+ Crows::NotAuthorizedError: not allowed to destroy? this #<Post:0x007fd1831adc48>
65
+ ```
66
+
67
+ Also, you have a method `crow` to receive the instance of your Crow class for handle the checks manually, useful in the views:
68
+
69
+ ``` erb
70
+ <% if crow(@post).destroy? %>
71
+ <%= link_to "Delete post", post_path(@post), method: :delete %>
72
+ <% end %>
73
+ ```
74
+
75
+ ## Scopes
76
+
77
+ You can also declare a Scope in your Crow class, use it when you need to check if the user can access to a list of records, looks like this:
78
+
79
+ ``` ruby
80
+ class PostCrow
81
+ class Scope
82
+ attr_reader :user, :scope
83
+
84
+ def initialize(user, scope)
85
+ @user = user
86
+ @scope = scope
87
+ end
88
+
89
+ def resolve
90
+ if user.admin?
91
+ scope.all
92
+ else
93
+ scope.select{|post| post.published }
94
+ end
95
+ end
96
+ end
97
+ end
98
+ ```
99
+
100
+ To make the Scope class works:
101
+ - You need to create it nested in your Crow class.
102
+ - In its `initialize` the class Scope receive as first argument the `current_user`(similar as works the Crow classes) and as second argument the scope(a class name) that you'll pass later to the `authorize` method
103
+ - And you need to declare the `resolve` method, which should check the authorization and return something that can be iterated(that's why is called scope, isn't it?)
104
+
105
+ Now, you can use your scopes like this:
106
+
107
+ ``` erb
108
+ <% crow_scope(Post).each do |post| %>
109
+ <p><%= link_to post.title, post_path(post) %></p>
110
+ <% end %>
111
+ ```
112
+
113
+ This example will return all the posts if the user is admin, if not it will return just the published posts, as we indicated in the `resolve` method.
114
+
115
+ ## Customize your current user
116
+
117
+ Sometimes, you'll need to authorize users outside your main class(your controller, API class, etc), in that case you simply need to Crows call other method than `current_user` and we give you for that, the option of define the `crow_user`
118
+
119
+ ```ruby
120
+ def crow_user
121
+ #grab here the user that you want to check his authorization
122
+ User.find(params[:user_id])
123
+ end
124
+ ```
125
+
126
+ ## Get and Set Crows
127
+
128
+ For other purposes you can get the hash of all crows and scopes defined with `crows` and `crows_scope` methods (use them in the class where you include Crows)
129
+
130
+ Following the previous examples:
131
+
132
+ ```ruby
133
+ class ApplicationController
134
+ puts crows
135
+ #=>{<Post:instance> => <PostCrow:instance>}
136
+
137
+ puts crows_scope
138
+ #=>{Post => #result of PostCrow::Scope.resolve}
139
+ end
140
+ ```
141
+
142
+ And you can set manually the crows with a hash syntax:
143
+
144
+ ```ruby
145
+ class ApplicationController
146
+ crows[@post] = crow(@post)
147
+ crows_scope[Post] = crow_scope(Post)
148
+ end
149
+ ```
@@ -0,0 +1,20 @@
1
+ require "./lib/crows"
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "crows"
5
+ s.version = Crows::VERSION
6
+ s.summary = "Micro library for authorization in Ruby classes."
7
+ s.description = "Crows is a micro framework-agnostic library for authorization in Ruby classes. A set of crows to authorized your users, because the night is dark and full of terrors... Crows provide you with a few helpers to check if `current_user` can make operations into some records. This gives you the freedom to build your own plain Ruby classes to make authorization works easily, without the painful of bigs DSLs or something like that."
8
+ s.authors = ["Julio Lopez"]
9
+ s.email = ["ljuliom@gmail.com"]
10
+ s.homepage = "http://github.com/TheBlasfem/crows"
11
+ s.files = Dir[
12
+ "LICENSE",
13
+ "README.md",
14
+ "lib/**/*.rb",
15
+ "*.gemspec",
16
+ "test/**/*.rb"
17
+ ]
18
+ s.license = "MIT"
19
+ s.add_development_dependency "cutest", "1.1.3"
20
+ end
@@ -0,0 +1,76 @@
1
+ # Copyright (c) 2015 Julio Lopez
2
+
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+
10
+ # The above copyright notice and this permission notice shall be included in all
11
+ # copies or substantial portions of the Software.
12
+
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ # SOFTWARE.
20
+ module Crows
21
+ VERSION = "0.1.0"
22
+ SUFFIX = "Crow"
23
+
24
+ class NotDefinedError < StandardError; end
25
+ class NotAuthorizedError < StandardError
26
+ def initialize(options = {})
27
+ query = options[:query]
28
+ record = options[:record]
29
+ message = "not allowed to #{query} this #{record.inspect}"
30
+ super(message)
31
+ end
32
+ end
33
+
34
+ def crow(record)
35
+ instance = find_crowclass(record).new(crow_user, record)
36
+ crows[record] = instance unless record.is_a? Class
37
+ instance
38
+ end
39
+
40
+ def crow_scope(klass)
41
+ crows_scope[klass] = crow(klass)
42
+ crows_scope[klass].resolve
43
+ end
44
+
45
+ def authorize(record, query)
46
+ crow = crow(record)
47
+ unless crow.public_send(query)
48
+ raise NotAuthorizedError.new(query: query, record: record)
49
+ end
50
+ true
51
+ end
52
+
53
+ def crow_user
54
+ current_user
55
+ end
56
+
57
+ def crows
58
+ @crows ||= {}
59
+ end
60
+
61
+ def crows_scope
62
+ @crows_scope ||= {}
63
+ end
64
+
65
+ private
66
+
67
+ def find_crowclass(record)
68
+ klass = if record.is_a? Class
69
+ Object.const_get(record.to_s + SUFFIX)::Scope
70
+ else
71
+ Object.const_get(record.class.to_s + SUFFIX)
72
+ end
73
+ rescue NameError
74
+ raise NotDefinedError, "unable to find crow #{klass} for #{record.inspect}"
75
+ end
76
+ end
@@ -0,0 +1,121 @@
1
+ require File.expand_path("../lib/crows", File.dirname(__FILE__))
2
+
3
+ scope do
4
+ class User; end
5
+
6
+ class Post
7
+ def self.all
8
+ ['post 1', 'post 2']
9
+ end
10
+ end
11
+
12
+ class PostCrow
13
+ attr_reader :user, :post
14
+
15
+ def initialize(user, post)
16
+ @user = user
17
+ @post = post
18
+ end
19
+
20
+ def update?
21
+ true
22
+ end
23
+
24
+ def destroy?
25
+ false
26
+ end
27
+
28
+ class Scope
29
+ attr_reader :user, :scope
30
+
31
+ def initialize(user, scope)
32
+ @user = user
33
+ @scope = scope
34
+ end
35
+
36
+ def resolve
37
+ if true
38
+ scope.all
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ class Controller
45
+ include Crows
46
+ attr_reader :current_user
47
+ def initialize(user)
48
+ @current_user = user
49
+ end
50
+ end
51
+
52
+ @user = User.new
53
+ @controller = Controller.new @user
54
+ @post = Post.new
55
+
56
+ test "return true if authorization passes" do
57
+ action = @controller.authorize(@post, :update?)
58
+ assert_equal action, true
59
+ end
60
+
61
+ test "throw an exception if authorization fails" do
62
+ assert_raise(Crows::NotAuthorizedError) do
63
+ @controller.authorize(@post, :destroy?)
64
+ end
65
+ end
66
+
67
+ test "throws an exception when a crow class cannot be found" do
68
+ assert_raise(Crows::NotDefinedError) do
69
+ @controller.authorize(@user, :update?)
70
+ end
71
+ end
72
+
73
+ test "returns an instantiated crow" do
74
+ crow = @controller.crow(@post)
75
+ assert_equal crow.post, @post
76
+ end
77
+
78
+ test "throws an exception when a crow class cannot be found in #crow" do
79
+ assert_raise(Crows::NotDefinedError) do
80
+ @controller.crow(@user)
81
+ end
82
+ end
83
+
84
+ test "save the crow" do
85
+ assert_equal @controller.crows[Post.new], nil
86
+ crow = @controller.crow(@post)
87
+ assert_equal @controller.crows[@post], crow
88
+ end
89
+
90
+ test "allow crow to be injected" do
91
+ crow = @controller.crow(@post)
92
+ @controller.crows[@post] = crow
93
+ assert_equal @controller.crows[@post], crow
94
+ end
95
+
96
+ test "returns an instantiated crow scope" do
97
+ scope = @controller.crow_scope(Post)
98
+ assert_equal scope, ['post 1', 'post 2']
99
+ end
100
+
101
+ test "throws an exception when a crow scope class cannot be found" do
102
+ assert_raise(Crows::NotDefinedError) do
103
+ @controller.crow_scope(User)
104
+ end
105
+ end
106
+
107
+ test "save the crow scope" do
108
+ scope = @controller.crow_scope(Post)
109
+ assert_equal @controller.crows_scope[Post].resolve, scope
110
+ end
111
+
112
+ test "allow crow scope to be injected" do
113
+ scope = @controller.crow_scope(Post)
114
+ @controller.crows_scope[Post] = scope
115
+ assert_equal @controller.crows_scope[Post], scope
116
+ end
117
+
118
+ test "crow_user return the same current_user of controller" do
119
+ assert_equal @controller.crow_user, @controller.current_user
120
+ end
121
+ end
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: crows
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Julio Lopez
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-06-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: cutest
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 1.1.3
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 1.1.3
27
+ description: Crows is a micro framework-agnostic library for authorization in Ruby
28
+ classes. A set of crows to authorized your users, because the night is dark and
29
+ full of terrors... Crows provide you with a few helpers to check if `current_user`
30
+ can make operations into some records. This gives you the freedom to build your
31
+ own plain Ruby classes to make authorization works easily, without the painful of
32
+ bigs DSLs or something like that.
33
+ email:
34
+ - ljuliom@gmail.com
35
+ executables: []
36
+ extensions: []
37
+ extra_rdoc_files: []
38
+ files:
39
+ - LICENSE
40
+ - README.md
41
+ - crows.gemspec
42
+ - lib/crows.rb
43
+ - test/test_crows.rb
44
+ homepage: http://github.com/TheBlasfem/crows
45
+ licenses:
46
+ - MIT
47
+ metadata: {}
48
+ post_install_message:
49
+ rdoc_options: []
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ requirements: []
63
+ rubyforge_project:
64
+ rubygems_version: 2.4.6
65
+ signing_key:
66
+ specification_version: 4
67
+ summary: Micro library for authorization in Ruby classes.
68
+ test_files: []