action_policy 0.3.2 → 0.3.3

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: 998666ad64277112c6ddc44e6db9e0aa81b6bbf32994fb675797bcd8e1f9ca70
4
- data.tar.gz: 5a35c722589841c6b41d6efd1735abe63833db03b9a7aec23e798648a97c5470
3
+ metadata.gz: 782d6e62c1723d8aa9b128cf054d90ace4a52a65467db205a079f898a9e068a8
4
+ data.tar.gz: e581bec4f5a7aaa782229f85aa34d09d7ed27eb19c870dcc9d3982c3271205d8
5
5
  SHA512:
6
- metadata.gz: 03a5594069a5947566708f3b3ad419d79be8931d38c25195f754d1474fa9b2f3bcf5e0f9e520a828d56c1fcfac762929ae4fdcf852c657f4b6bec973c0559e0b
7
- data.tar.gz: e41e88d916fdf0832e7b03cdadcf7f6c1a4814c45e6ca76e12094bbcb03a6ea54146a0e676d079a0813caff31f54c3dfe90d0e917ade8c4261e17ec4d55a4221
6
+ metadata.gz: 8178fe440e7b7b8f1b60a858e503ad65e37eaa3157dca0f48b772a626704f66763392d60a2f18758d9e903d19e687f292c0b261f5c531d81693ee5e1df38472c
7
+ data.tar.gz: 8126f417658773d0deeca4d927881d8a9bf504853d89d58d1d43dd07d6f1328bc355b81bd9f12e7742ce753d20d6c17caf07a0ce2f4fd6df50e2fb0bb70ca034
data/.gitignore CHANGED
@@ -8,3 +8,8 @@
8
8
  /spec/reports/
9
9
  /tmp/
10
10
  Gemfile.local
11
+
12
+ spec/dummy/log/*
13
+ spec/dummy/db/*.sqlite3
14
+ spec/dummy/db/*.sqlite3-journal
15
+ spec/dummy/tmp/
@@ -12,6 +12,7 @@ AllCops:
12
12
  - 'Gemfile'
13
13
  - 'vendor/**/*'
14
14
  - 'gemfiles/**/*'
15
+ - 'lib/generators/**/templates/**/*'
15
16
  DisplayCopNames: true
16
17
  TargetRubyVersion: 2.4
17
18
 
@@ -1,5 +1,29 @@
1
1
  ## master
2
2
 
3
+ ## 0.3.3 (2019-11-27)
4
+
5
+ - Improve pretty print functionality. ([@palkan][])
6
+
7
+ Colorize true/false values.
8
+ Handle multiline expressions and debug statements (i.e., `binding.pry`).
9
+
10
+ - Add Rails generators. ([@nicolas-brousse][])
11
+
12
+ Adds `action_policy:install` and `action_policy:policy MODEL` Rails generators.
13
+
14
+ - Optional authorization target. ([@somenugget][])
15
+
16
+ Allows making authorization context optional:
17
+
18
+ ```ruby
19
+ class OptionalRolePolicy < ActionPolicy::Base
20
+ authorize :role, optional: true
21
+ end
22
+
23
+ policy = OptionalRolePolicy.new
24
+ policy.role #=> nil
25
+ ```
26
+
3
27
  ## 0.3.2 (2019-05-26) 👶
4
28
 
5
29
  - Fixed thread-safety issues with scoping configs. ([@palkan][])
@@ -313,3 +337,5 @@
313
337
  [@brendon]: https://github.com/brendon
314
338
  [@DmitryTsepelev]: https://github.com/DmitryTsepelev
315
339
  [@korolvs]: https://github.com/korolvs
340
+ [@nicolas-brousse]: https://github.com/nicolas-brousse
341
+ [@somenugget]: https://github.com/somenugget
data/README.md CHANGED
@@ -47,6 +47,8 @@ class ApplicationPolicy < ActionPolicy::Base
47
47
  end
48
48
  ```
49
49
 
50
+ This may be done with `rails generate action_policy:install` generator.
51
+
50
52
  Then write a policy for a resource. For example:
51
53
 
52
54
  ```ruby
@@ -64,6 +66,8 @@ class PostPolicy < ApplicationPolicy
64
66
  end
65
67
  ```
66
68
 
69
+ This may be done with `rails generate action_policy:policy Post` generator.
70
+
67
71
  Now you can easily add authorization to your Rails\* controller:
68
72
 
69
73
  ```ruby
@@ -31,6 +31,7 @@ Gem::Specification.new do |spec|
31
31
 
32
32
  spec.required_ruby_version = ">= 2.4.0"
33
33
 
34
+ spec.add_development_dependency "ammeter", "~> 1.1.3"
34
35
  spec.add_development_dependency "bundler", ">= 1.15"
35
36
  spec.add_development_dependency "minitest", "~> 5.0"
36
37
  spec.add_development_dependency "rake", "~> 10.0"
@@ -19,6 +19,7 @@ end
19
19
  Now you must provide `account` during policy initialization. When authorization key is missing or equals to `nil`, `ActionPolicy::AuthorizationContextMissing` error is raised.
20
20
 
21
21
  **NOTE:** if you want to allow passing `nil` as `account` value, you must add `allow_nil: true` option to `authorize`.
22
+ If you want to be able not to pass `account` at all, you must add `optional: true`
22
23
 
23
24
  To do that automatically in your `authorize!` and `allowed_to?` calls, you must also configure authorization context. For example, in your controller:
24
25
 
@@ -1,15 +1,15 @@
1
1
  # GraphQL integration
2
2
 
3
- You can use Action Policy as an authorization library for you [GraphQL Ruby](https://graphql-ruby.org/) application via the [`action_policy-graphql` gem](https://github.com/palkan/action_policy-graphql).
3
+ You can use Action Policy as an authorization library for your [GraphQL Ruby](https://graphql-ruby.org/) application via the [`action_policy-graphql` gem](https://github.com/palkan/action_policy-graphql).
4
4
 
5
5
  This integration provides the following features:
6
6
  - Fields & mutations authorization
7
7
  - List and connections scoping
8
- - [**Exposing permissions/authorization rules in the API**](https://dev.to/evilmartians/exposing-permissions-in-graphql-apis-with-action-policy-1mfh).
8
+ - [**Exposing permissions/authorization rules in the API**](https://evilmartians.com/chronicles/exposing-permissions-in-graphql-apis-with-action-policy).
9
9
 
10
10
  ## Getting Started
11
11
 
12
- First, add `action_policy-graphql` gem to your Gemfile (see [installation instructions](https://github.com/palkan/action_policy-graphql#installation)).
12
+ First, add the `action_policy-graphql` gem to your Gemfile (see [installation instructions](https://github.com/palkan/action_policy-graphql#installation)).
13
13
 
14
14
  Then, include `ActionPolicy::GraphQL::Behaviour` to your base type (or any other type/mutation where you want to use authorization features):
15
15
 
@@ -23,11 +23,16 @@ end
23
23
  class Types::BaseMutation < GraphQL::Schema::Mutation
24
24
  include ActionPolicy::GraphQL::Behaviour
25
25
  end
26
+
27
+ # For using authorization helpers in resolvers
28
+ class Types::BaseResolver < GraphQL::Schema::Resolver
29
+ include ActionPolicy::GraphQL::Behaviour
30
+ end
26
31
  ```
27
32
 
28
33
  ## Authorization Context
29
34
 
30
- By default, Action Policy use `context[:current_user]` as the `user` [authorization context](./authoriation_context.md).
35
+ By default, Action Policy uses `context[:current_user]` as the `user` [authorization context](./authorization_context.md).
31
36
 
32
37
  **NOTE:** see below for more information on what's included into `ActionPolicy::GraphQL::Behaviour`.
33
38
 
@@ -75,6 +80,38 @@ You can also change the default `show?` rule globally:
75
80
  ActionPolicy::GraphQL.default_authorize_rule = :show_graphql_field?
76
81
  ```
77
82
 
83
+ If you want to perform authorization before resolving the field value, you can use `preauthorize: *` option:
84
+
85
+ ```ruby
86
+ field :homes, [Home], null: false, preauthorize: {with: HomePolicy}
87
+
88
+ def homes
89
+ Home.all
90
+ end
91
+ ```
92
+
93
+ The code above is equal to:
94
+
95
+ ```ruby
96
+ field :homes, [Home], null: false
97
+
98
+ def homes
99
+ authorize! "homes", to: :index?, with: HomePolicy
100
+ Home.all
101
+ end
102
+ ```
103
+
104
+ **NOTE:** we pass the field's name as the `record` to the policy rule. We assume that preauthorization rules do not depend on
105
+ the record itself and pass the field's name for debugging purposes only.
106
+
107
+ You can customize the authorization options, e.g. `authorize: {to: :preview?, with: CustomPolicy}`.
108
+
109
+ **NOTE:** unlike `authorize: *` you MUST specify the `with: SomePolicy` option.
110
+ The default authorization rule depends on the type of the field:
111
+
112
+ - for lists we use `index?` (configured by `ActionPolicy::GraphQL.default_preauthorize_list_rule` parameter)
113
+ - for _singleton_ fields we use `show?` (configured by `ActionPolicy::GraphQL.default_preauthorize_node_rule` parameter)
114
+
78
115
  ### Class-level authorization
79
116
 
80
117
  You can use Action Policy in the class-level [authorization hooks](https://graphql-ruby.org/authorization/authorization.html) (`self.authorized?`) like this:
@@ -83,9 +120,10 @@ You can use Action Policy in the class-level [authorization hooks](https://graph
83
120
  class Types::Friendship < Types::BaseObject
84
121
  def self.authorized?(object, context)
85
122
  super &&
86
- object.allowed_to?(
123
+ allowed_to?(
87
124
  :show?,
88
125
  object,
126
+ # NOTE: you must provide context explicitly
89
127
  context: {user: context[:current_user]}
90
128
  )
91
129
  end
@@ -94,7 +132,7 @@ end
94
132
 
95
133
  ## Authorizing Mutations
96
134
 
97
- Mutation is just a Ruby class with a single API method. There is nothing specific in authorizing mutations: from the Action Policy point of view, they are just [_behaviours_](./behaviour.md).
135
+ A mutation is just a Ruby class with a single API method. There is nothing specific in authorizing mutations: from the Action Policy point of view, they are just [_behaviours_](./behaviour.md).
98
136
 
99
137
  If you want to authorize the mutation, you call `authorize!` method. For example:
100
138
 
@@ -166,6 +204,8 @@ class CityType < ::Common::Graphql::Type
166
204
  end
167
205
  ```
168
206
 
207
+ **NOTE:** you cannot use `authorize: *` and `authorized_scope: *` at the same time but you can combine `preauthorize: *` with `authorized_scope: *`.
208
+
169
209
  See the documenation on [scoping](./scoping.md).
170
210
 
171
211
  ## Exposing Authorization Rules
@@ -221,6 +261,16 @@ You can override a custom authorization field prefix (`can_`):
221
261
  ActionPolicy::GraphQL.default_authorization_field_prefix = "allowed_to_"
222
262
  ```
223
263
 
264
+ You can specify a custom field name as well (only for a single rule):
265
+
266
+ ```ruby
267
+ class ProfileType < ::Common::Graphql::Type
268
+ # Adds can_create_post field.
269
+
270
+ expose_authorization_rules :create?, with: PostPolicy, field_name: "can_create_post"
271
+ end
272
+ ```
273
+
224
274
  ## Custom Behaviour
225
275
 
226
276
  Including the default `ActionPolicy::GraphQL::Behaviour` is equal to adding the following to your base class:
@@ -77,4 +77,4 @@ When everything is green, it's time to fully migrate to ActionPolicy:
77
77
  - migrate view helpers (from `policy(..)` to `allowed_to?`, from `policy_scope` to `authorized`)
78
78
  - re-write specs using simple non-DSL syntax (or [Action Policy RSpec syntax](testing#rspec-dsl))
79
79
  - add [authorization tests](testing#testing-authorization) (add `require 'action_policy/rspec'`)
80
- - use [Reasons](), [I18n integration](i18n), [cache](caching) and other Action Policy features!
80
+ - use [Reasons](reasons), [I18n integration](i18n), [cache](caching) and other Action Policy features!
@@ -34,6 +34,10 @@ class ApplicationPolicy < ActionPolicy::Base
34
34
  end
35
35
  ```
36
36
 
37
+ You could use the following command to generate it.
38
+
39
+ $ rails generate action_policy:install
40
+
37
41
  **NOTE:** it is not necessary to inherit from `ActionPolicy::Base`; instead, you can [construct basic policy](custom_policy.md) choosing only the components you need.
38
42
 
39
43
  Rules must be public methods on the class. Using private methods as rules will raise an error.
@@ -87,9 +87,10 @@ The additional details are especially helpful when combined with localization, '
87
87
 
88
88
  ```yml
89
89
  en:
90
- policy:
91
- stage:
92
- show?: "The %{title} stage is not accessible"
90
+ action_policy:
91
+ policy:
92
+ stage:
93
+ show?: "The %{title} stage is not accessible"
93
94
  ```
94
95
 
95
96
  And then when you call `full_messages`:
@@ -85,7 +85,7 @@ end
85
85
  If test failed the exception message includes the result and [failure reasons](reasons) (if any):
86
86
 
87
87
  ```
88
- 1) PostPolucy#show? when post is draft
88
+ 1) PostPolicy#show? when post is draft
89
89
  Failure/Error: ...
90
90
 
91
91
  Expected to fail but succeed:
@@ -96,7 +96,7 @@ If you have [debugging utils](debugging) installed the message also includes the
96
96
  source code of the policy rule:
97
97
 
98
98
  ```
99
- 1) UserPolucy#manage? when post is draft
99
+ 1) UserPolicy#manage? when post is draft
100
100
  Failure/Error: ...
101
101
 
102
102
  Expected to fail but succeed:
@@ -49,11 +49,15 @@ module ActionPolicy
49
49
  @authorization_context = {}
50
50
 
51
51
  self.class.authorization_targets.each do |id, opts|
52
- raise AuthorizationContextMissing, id unless params.key?(id)
52
+ if opts[:optional] == true
53
+ val = params.fetch(id, nil)
54
+ else
55
+ raise AuthorizationContextMissing, id unless params.key?(id)
53
56
 
54
- val = params.fetch(id)
57
+ val = params.fetch(id)
55
58
 
56
- raise AuthorizationContextMissing, id if val.nil? && opts[:allow_nil] != true
59
+ raise AuthorizationContextMissing, id if val.nil? && opts[:allow_nil] != true
60
+ end
57
61
 
58
62
  authorization_context[id] = instance_variable_set("@#{id}", val)
59
63
  end
@@ -90,8 +90,9 @@ module ActionPolicy
90
90
  def with_clean_result # :nodoc:
91
91
  was_result = @result
92
92
  yield
93
- res, @result = @result, was_result
94
- res
93
+ @result
94
+ ensure
95
+ @result = was_result
95
96
  end
96
97
 
97
98
  # Returns a result of applying the specified rule to the specified record.
@@ -43,6 +43,6 @@ RSpec.configure do |config|
43
43
  config.include(
44
44
  ActionPolicy::RSpec::PunditSyntax::PolicyExampleGroup,
45
45
  type: :policy,
46
- example_group: {file_path: %r{spec/policies}}
46
+ file_path: %r{spec/policies}
47
47
  )
48
48
  end
@@ -41,6 +41,9 @@ module ActionPolicy
41
41
  # #=> AND
42
42
  # #=> access_feed? #=> true
43
43
  module PrettyPrint
44
+ TRUE = "\e[32mtrue\e[0m"
45
+ FALSE = "\e[31mfalse\e[0m"
46
+
44
47
  class Visitor
45
48
  attr_reader :lines, :object
46
49
  attr_accessor :indent
@@ -68,10 +71,11 @@ module ActionPolicy
68
71
 
69
72
  def expression_with_result(sexp)
70
73
  expression = Unparser.unparse(sexp)
71
- "#{expression} #=> #{eval_exp(expression)}"
74
+ "#{expression} #=> #{colorize(eval_exp(expression))}"
72
75
  end
73
76
 
74
77
  def eval_exp(exp)
78
+ return "<skipped>" if ignore_exp?(exp)
75
79
  object.instance_eval(exp)
76
80
  rescue => e
77
81
  "Failed: #{e.message}"
@@ -89,13 +93,22 @@ module ActionPolicy
89
93
  visit_node(ast.children[1])
90
94
  end
91
95
 
92
- # Parens
93
96
  def visit_begin(ast)
94
- lines << indented("(")
95
- self.indent += 2
96
- visit_node(ast.children[0])
97
- self.indent -= 2
98
- lines << indented(")")
97
+ # Parens
98
+ if ast.children.size == 1
99
+ lines << indented("(")
100
+ self.indent += 2
101
+ visit_node(ast.children[0])
102
+ self.indent -= 2
103
+ lines << indented(")")
104
+ else
105
+ # Multiple expressions
106
+ ast.children.each do |node|
107
+ visit_node(node)
108
+ # restore indent after each expression
109
+ self.indent -= 2
110
+ end
111
+ end
99
112
  end
100
113
 
101
114
  def visit_missing(ast)
@@ -108,6 +121,18 @@ module ActionPolicy
108
121
  self.indent += 2 if indent.zero?
109
122
  end
110
123
  end
124
+
125
+ # Some lines should not be evaled
126
+ def ignore_exp?(exp)
127
+ exp.match?(/^\s*binding\.(pry|irb)\s*$/)
128
+ end
129
+
130
+ def colorize(val)
131
+ return val unless $stdout.isatty
132
+ return TRUE if val.eql?(true)
133
+ return FALSE if val.eql?(false)
134
+ val
135
+ end
111
136
  end
112
137
 
113
138
  class << self
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActionPolicy
4
- VERSION = "0.3.2"
4
+ VERSION = "0.3.3"
5
5
  end
@@ -0,0 +1,2 @@
1
+ Description:
2
+ Generates an application policy for your application.
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module ActionPolicy
6
+ module Generators
7
+ class InstallGenerator < ::Rails::Generators::Base
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+ def copy_application_policy
11
+ template "application_policy.rb", "app/policies/application_policy.rb"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,2 @@
1
+ class ApplicationPolicy < ActionPolicy::Base
2
+ end
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Generates a policy for a model with the given name.
3
+
4
+ Example:
5
+ rails generate action_policy:policy user
6
+
7
+ This will create:
8
+ app/policies/user_policy.rb
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module ActionPolicy
6
+ module Generators
7
+ class PolicyGenerator < ::Rails::Generators::NamedBase
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+ invoke "action_policy:install"
11
+
12
+ def create_policy
13
+ template "policy.rb", File.join("app/policies", class_path, "#{file_name}_policy.rb")
14
+ end
15
+
16
+ hook_for :test_framework
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,22 @@
1
+ <% module_namespacing do -%>
2
+ class <%= class_name %>Policy < ApplicationPolicy
3
+ # See https://actionpolicy.evilmartians.io/#/writing_policies
4
+ #
5
+ # def index?
6
+ # true
7
+ # end
8
+ #
9
+ # def update?
10
+ # # here we can access our context and record
11
+ # user.admin? || (user.id == record.user_id)
12
+ # end
13
+
14
+ # Scoping
15
+ # See https://actionpolicy.evilmartians.io/#/scoping
16
+ #
17
+ # relation_scope do |relation|
18
+ # next relation if user.admin?
19
+ # relation.where(user: user)
20
+ # end
21
+ end
22
+ <% end -%>
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module Rspec
6
+ module Generators
7
+ class PolicyGenerator < ::Rails::Generators::NamedBase
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+ def create_policy_spec
11
+ template "policy_spec.rb", File.join("spec/policies", class_path, "#{file_name}_policy_spec.rb")
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,19 @@
1
+ require '<%= File.exists?("spec/rails_helper.rb") ? "rails_helper" : "spec_helper" %>'
2
+
3
+ RSpec.describe <%= class_name %>Policy, type: :policy do
4
+ # let(:user) { build_stubbed :user }
5
+ # let(:record) { build_stubbed :post, draft: false }
6
+ # let(:context) { {user: user} }
7
+
8
+ describe_rule :index? do
9
+ pending "add some examples to (or delete) #{__FILE__}"
10
+ end
11
+
12
+ describe_rule :create? do
13
+ pending "add some examples to (or delete) #{__FILE__}"
14
+ end
15
+
16
+ describe_rule :manage? do
17
+ pending "add some examples to (or delete) #{__FILE__}"
18
+ end
19
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module TestUnit
6
+ module Generators
7
+ class PolicyGenerator < ::Rails::Generators::NamedBase
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+ def create_policy_test
11
+ template "policy_test.rb", File.join("test/policies", class_path, "#{file_name}_policy_test.rb")
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,12 @@
1
+ require 'test_helper'
2
+
3
+ class <%= class_name %>PolicyTest < ActiveSupport::TestCase
4
+ def test_index
5
+ end
6
+
7
+ def test_create
8
+ end
9
+
10
+ def test_manage
11
+ end
12
+ end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: action_policy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Dementyev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-06-27 00:00:00.000000000 Z
11
+ date: 2019-11-27 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ammeter
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
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bundler
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -253,6 +267,16 @@ files:
253
267
  - lib/action_policy/utils/pretty_print.rb
254
268
  - lib/action_policy/utils/suggest_message.rb
255
269
  - lib/action_policy/version.rb
270
+ - lib/generators/action_policy/install/USAGE
271
+ - lib/generators/action_policy/install/install_generator.rb
272
+ - lib/generators/action_policy/install/templates/application_policy.rb
273
+ - lib/generators/action_policy/policy/USAGE
274
+ - lib/generators/action_policy/policy/policy_generator.rb
275
+ - lib/generators/action_policy/policy/templates/policy.rb
276
+ - lib/generators/action_policy/rspec/policy_generator.rb
277
+ - lib/generators/action_policy/rspec/templates/policy_spec.rb
278
+ - lib/generators/action_policy/test_unit/policy_generator.rb
279
+ - lib/generators/action_policy/test_unit/templates/policy_test.rb
256
280
  homepage: https://github.com/palkan/action_policy
257
281
  licenses:
258
282
  - MIT
@@ -277,7 +301,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
277
301
  - !ruby/object:Gem::Version
278
302
  version: '0'
279
303
  requirements: []
280
- rubygems_version: 3.0.3
304
+ rubygems_version: 3.0.6
281
305
  signing_key:
282
306
  specification_version: 4
283
307
  summary: Authorization framework for Ruby/Rails application