negroni-lite 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +20 -0
- data/README.md +63 -0
- data/Rakefile +64 -0
- data/app/mailers/negroni/mailer.rb +45 -0
- data/app/views/negroni/mailer/password_change.html.erb +5 -0
- data/app/views/negroni/mailer/reset_password_instructions.html.erb +8 -0
- data/app/views/negroni/mailer/unlock_instructions.html.erb +7 -0
- data/config/locales/en.yml +9 -0
- data/config/routes.rb +4 -0
- data/lib/negroni.rb +209 -0
- data/lib/negroni/configuration.rb +231 -0
- data/lib/negroni/controllers/helpers.rb +29 -0
- data/lib/negroni/controllers/token_authenticable.rb +20 -0
- data/lib/negroni/encryptor.rb +35 -0
- data/lib/negroni/engine.rb +35 -0
- data/lib/negroni/mailers/helpers.rb +112 -0
- data/lib/negroni/models.rb +138 -0
- data/lib/negroni/models/authenticable.rb +197 -0
- data/lib/negroni/models/base.rb +318 -0
- data/lib/negroni/models/lockable.rb +216 -0
- data/lib/negroni/models/omniauthable.rb +33 -0
- data/lib/negroni/models/recoverable.rb +204 -0
- data/lib/negroni/models/registerable.rb +14 -0
- data/lib/negroni/models/validatable.rb +63 -0
- data/lib/negroni/modules.rb +12 -0
- data/lib/negroni/omniauth.rb +25 -0
- data/lib/negroni/omniauth/config.rb +81 -0
- data/lib/negroni/orm/active_record.rb +7 -0
- data/lib/negroni/orm/mongoid.rb +6 -0
- data/lib/negroni/param_filter.rb +53 -0
- data/lib/negroni/resolver.rb +17 -0
- data/lib/negroni/token_generator.rb +58 -0
- data/lib/negroni/token_not_found.rb +13 -0
- data/lib/negroni/version.rb +6 -0
- data/lib/tasks/negroni_tasks.rake +5 -0
- metadata +169 -0
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Negroni
|
4
|
+
module Models
|
5
|
+
# Registerable is responsible for everything related to registering a new
|
6
|
+
# resource (ie user sign up).
|
7
|
+
module Registerable
|
8
|
+
# Required fields for this module (_none_)
|
9
|
+
def self.required_fields(_klass = nil)
|
10
|
+
[]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Negroni
|
4
|
+
module Models
|
5
|
+
# Validatable creates all needed validations for a user email and password.
|
6
|
+
# It's optional, given you may want to create the validations by yourself.
|
7
|
+
# Automatically validate if the email is present, unique and its format is
|
8
|
+
# valid. Also tests presence of password, confirmation and length.
|
9
|
+
#
|
10
|
+
# ## Options
|
11
|
+
#
|
12
|
+
# Validatable adds the following options to devise_for:
|
13
|
+
#
|
14
|
+
# * `email_regexp`: the regular expression used to validate e-mails
|
15
|
+
# * `password_length`: a range expressing password length. Default: 8..72
|
16
|
+
#
|
17
|
+
module Validatable
|
18
|
+
extend ActiveSupport::Concern
|
19
|
+
|
20
|
+
# Required fields for :validatable
|
21
|
+
def self.required_fields(_klass = nil)
|
22
|
+
[]
|
23
|
+
end
|
24
|
+
|
25
|
+
included do
|
26
|
+
raise RuntimeError unless respond_to?(:validates)
|
27
|
+
|
28
|
+
validates :email, presence: true, if: :email_required?
|
29
|
+
|
30
|
+
with_options allow_blank: true, if: :email_changed? do |val|
|
31
|
+
val.validates :email, uniqueness: true
|
32
|
+
val.validates :email, format: { with: email_regexp }
|
33
|
+
end
|
34
|
+
|
35
|
+
validates :password, presence: true, if: :password_required?
|
36
|
+
|
37
|
+
validates :password, confirmation: true, if: :password_required?
|
38
|
+
|
39
|
+
validates :password, length: { in: password_length }, allow_blank: true
|
40
|
+
|
41
|
+
validates :password_confirmation, presence: true,
|
42
|
+
on: [:create, :update],
|
43
|
+
if: :password_required?
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
|
48
|
+
def password_required?
|
49
|
+
!persisted? || !password.nil? || !password_confirmation.nil?
|
50
|
+
end
|
51
|
+
|
52
|
+
# Override this method if an email is not required for your model
|
53
|
+
def email_required?
|
54
|
+
true
|
55
|
+
end
|
56
|
+
|
57
|
+
# Class methods for Validations
|
58
|
+
module ClassMethods
|
59
|
+
Negroni::Models.config(self, :email_regexp, :password_length)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/core_ext/object/with_options'
|
4
|
+
|
5
|
+
Negroni.with_options model: true do |negroni|
|
6
|
+
negroni.register_module :authenticable
|
7
|
+
negroni.register_module :registerable
|
8
|
+
negroni.register_module :lockable
|
9
|
+
negroni.register_module :omniauthable
|
10
|
+
negroni.register_module :recoverable
|
11
|
+
negroni.register_module :validatable
|
12
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'omniauth'
|
5
|
+
require 'omniauth/version'
|
6
|
+
rescue LoadError
|
7
|
+
warn 'Could not load `omniauth`. ' \
|
8
|
+
'Please ensure you have the omniauth gem in your gemfile (>= 1.0.0).'
|
9
|
+
raise
|
10
|
+
end
|
11
|
+
|
12
|
+
unless OmniAuth::VERSION =~ /^1\./
|
13
|
+
raise 'You are using an old OmniAuth version, please make sure it is >= 1.0.0'
|
14
|
+
end
|
15
|
+
|
16
|
+
OmniAuth.config.path_prefix = nil
|
17
|
+
OmniAuth.config.logger = Rails.logger
|
18
|
+
|
19
|
+
module Negroni
|
20
|
+
# The `OmniAuth` module encloses configuration and providers for OmniAuth
|
21
|
+
# integration.
|
22
|
+
module OmniAuth
|
23
|
+
autoload :Config, 'negroni/omniauth/config'
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Negroni
|
4
|
+
module OmniAuth
|
5
|
+
# Raised if a strategy is unknown or not found.
|
6
|
+
class UnknownStrategy < NameError
|
7
|
+
# Raise a new error for `strategy`.
|
8
|
+
def initialize(strategy)
|
9
|
+
@strategy = strategy
|
10
|
+
super("Could not find a strategy named `#{strategy}`. " \
|
11
|
+
"Please ensure it is `require`d or explicitly set using " \
|
12
|
+
"the :strategy_class option.")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Config encapsulates OmniAuth configuration for Negroni
|
17
|
+
class Config
|
18
|
+
# @!attribute [rw] strategy
|
19
|
+
# @return [Symbol] the strategy for OmniAuth
|
20
|
+
attr_accessor :strategy
|
21
|
+
|
22
|
+
# @!attribute [r] args
|
23
|
+
# @return [Array<Object>] arguments needed to configure the provider
|
24
|
+
attr_reader :args
|
25
|
+
|
26
|
+
# @!attribute [r] options
|
27
|
+
# @return [Hash] options needed to configure the provider
|
28
|
+
attr_reader :options
|
29
|
+
|
30
|
+
# @!attribute [r] provider
|
31
|
+
# @return [Symbol] the name of the OmniAuth provider
|
32
|
+
attr_reader :provider
|
33
|
+
|
34
|
+
# @!attribute [r] strategy_name
|
35
|
+
# @return [String, Symbol] the name of the strategy, if different from
|
36
|
+
# `provider`
|
37
|
+
attr_reader :strategy_name
|
38
|
+
|
39
|
+
# Create a new OmniAuth::Config
|
40
|
+
#
|
41
|
+
# @param provider [Symbol] the name of the provider to use
|
42
|
+
# @param args [Array<Object>] any arguments to pass to the provider
|
43
|
+
#
|
44
|
+
def initialize(provider, args)
|
45
|
+
@provider = provider
|
46
|
+
@args = args
|
47
|
+
@options = @args.last.is_a?(Hash) ? @args.last : {}
|
48
|
+
@strategy = nil
|
49
|
+
@strategy_name = options[:name] || @provider
|
50
|
+
@strategy_class = options.delete(:strategy_class)
|
51
|
+
end
|
52
|
+
|
53
|
+
# @!attribute [r] strategy_class
|
54
|
+
# @return [Class] the class for the OmniAuth strategy
|
55
|
+
|
56
|
+
# @return [Class] the class for the OmniAuth strategy
|
57
|
+
def strategy_class
|
58
|
+
@strategy_class ||= find_strategy || autoload_strategy
|
59
|
+
end
|
60
|
+
|
61
|
+
# Find a the strategy, using the name provided
|
62
|
+
def find_strategy
|
63
|
+
::OmniAuth.strategies.find do |klass|
|
64
|
+
klass.to_s =~ /#{::OmniAuth::Utils.camelize(strategy_name)}$/ ||
|
65
|
+
klass.default_options[:name] == strategy_name
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Autoload a strategy
|
70
|
+
def autoload_strategy
|
71
|
+
name = ::OmniAuth::Utils.camelize(provider.to_s)
|
72
|
+
|
73
|
+
if ::OmniAuth::Strategies.const_defined?(name)
|
74
|
+
return ::OmniAuth::Strategies.const_get(name)
|
75
|
+
end
|
76
|
+
|
77
|
+
raise UnknownStrategy, name
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Negroni
|
4
|
+
# ParamFilter is responsible for filtering blacklisted parameters from a
|
5
|
+
# request for validation and authentication.
|
6
|
+
class ParamFilter
|
7
|
+
# Creates a new instance of `ParamFilter`.
|
8
|
+
#
|
9
|
+
# @param case_insensitive_keys [Array<Symbol>] keys which are not case
|
10
|
+
# sensitive
|
11
|
+
# @param strip_whitespace_keys [Array<Symbol>] keys which should have
|
12
|
+
# whitespace stripped
|
13
|
+
#
|
14
|
+
def initialize(case_insensitive_keys, strip_whitespace_keys)
|
15
|
+
@case_insensitive_keys = case_insensitive_keys || []
|
16
|
+
@strip_whitespace_keys = strip_whitespace_keys || []
|
17
|
+
end
|
18
|
+
|
19
|
+
# Filter the `conditions` based on \@case_insensitive_keys and
|
20
|
+
# \@strip_whitespace_keys.
|
21
|
+
#
|
22
|
+
# @param conditions [Hash] the conditions hash to filter
|
23
|
+
#
|
24
|
+
# @return [Hash] the filtered `conditions` hash
|
25
|
+
def filter(conditions)
|
26
|
+
conditions = stringify_params(conditions.dup.to_h)
|
27
|
+
|
28
|
+
conditions.merge! filtered_hash_by_meth_for_keys(conditions.dup,
|
29
|
+
:downcase,
|
30
|
+
@case_insensitive_keys)
|
31
|
+
|
32
|
+
conditions.merge! filtered_hash_by_meth_for_keys(conditions.dup,
|
33
|
+
:strip,
|
34
|
+
@strip_whitespace_keys)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def filtered_hash_by_meth_for_keys(conditions, method, condition_keys)
|
40
|
+
condition_keys.each do |key|
|
41
|
+
value = conditions[key]
|
42
|
+
conditions[key] = value.send(method) if value.respond_to?(method)
|
43
|
+
end
|
44
|
+
|
45
|
+
conditions
|
46
|
+
end
|
47
|
+
|
48
|
+
def stringify_params(conditions)
|
49
|
+
return conditions unless conditions.is_a? Hash
|
50
|
+
conditions.each { |k, v| conditions[k] = v.to_s }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Negroni
|
4
|
+
# @api private
|
5
|
+
# Resolver is responsible for getting and `constantizing` a string.
|
6
|
+
class Resolver
|
7
|
+
# Create a Resolver
|
8
|
+
def initialize(reference)
|
9
|
+
@reference = reference
|
10
|
+
end
|
11
|
+
|
12
|
+
# Resolve a string to a constant
|
13
|
+
def resolve
|
14
|
+
ActiveSupport::Dependencies.constantize(@reference)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'openssl'
|
4
|
+
|
5
|
+
module Negroni
|
6
|
+
# Generates a secure token.
|
7
|
+
class TokenGenerator
|
8
|
+
# Creates a new token generator.
|
9
|
+
#
|
10
|
+
# @param key_generator [ActiveSupport::CachingKeyGenerator] the generator
|
11
|
+
# @param digest [String, Symbol] The digest to use. Must be a stringified
|
12
|
+
# version of an OpenSSL::Digest subclass. Default: `SHA256`.
|
13
|
+
def initialize(key_generator = nil, digest = 'SHA256', secret_key: nil)
|
14
|
+
@key_generator = if secret_key
|
15
|
+
ActiveSupport::CachingKeyGenerator.new(
|
16
|
+
ActiveSupport::KeyGenerator.new(secret_key)
|
17
|
+
)
|
18
|
+
elsif key_generator
|
19
|
+
key_generator
|
20
|
+
else
|
21
|
+
raise ArgumentError, 'Unexpected arguments!'
|
22
|
+
end
|
23
|
+
@digest = digest
|
24
|
+
end
|
25
|
+
|
26
|
+
# Digest a key.
|
27
|
+
#
|
28
|
+
# @param _klass [Class] the class for which to digest the value.
|
29
|
+
# @param column [String] the column name to digest.
|
30
|
+
# @param value [#to_s] the value to digest.
|
31
|
+
#
|
32
|
+
# @return [String] the digested value.
|
33
|
+
def digest(_klass, column, value)
|
34
|
+
return unless value.present?
|
35
|
+
OpenSSL::HMAC.hexdigest(@digest, key_for(column), value.to_s)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Generate a key.
|
39
|
+
#
|
40
|
+
# @param klass [Class] the class for which to generate the key.
|
41
|
+
# @param column [String] the column name to generate a key for.
|
42
|
+
def generate(klass, column)
|
43
|
+
key = key_for(column)
|
44
|
+
|
45
|
+
loop do
|
46
|
+
raw = Negroni.friendly_token
|
47
|
+
enc = OpenSSL::HMAC.hexdigest(@digest, key, raw)
|
48
|
+
break [raw, enc] unless klass.to_adapter.find_first(column => enc)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def key_for(column)
|
55
|
+
@key_generator.generate_key "Negroni #{column}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Negroni
|
4
|
+
# Raised when a token is bad
|
5
|
+
class TokenNotFound < StandardError
|
6
|
+
# Override for better `raise` calls
|
7
|
+
#
|
8
|
+
# @param object [Object] whatever you want
|
9
|
+
def self.exception(object)
|
10
|
+
super "The record #{object} could not be found."
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
metadata
ADDED
@@ -0,0 +1,169 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: negroni-lite
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- J. Morgan Lieberthal
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-12-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 5.0.0
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 5.0.0.1
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 5.0.0
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 5.0.0.1
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: knock
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '2.0'
|
40
|
+
type: :runtime
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '2.0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: orm_adapter
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0.5'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0.5'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: bcrypt
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '3.0'
|
68
|
+
type: :runtime
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '3.0'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: sqlite3
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: rspec-rails
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: 3.5.0
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 3.5.0
|
103
|
+
description: Very light user management plugin for rails api apps
|
104
|
+
email:
|
105
|
+
- j.morgan.lieberthal@gmail.com
|
106
|
+
executables: []
|
107
|
+
extensions: []
|
108
|
+
extra_rdoc_files: []
|
109
|
+
files:
|
110
|
+
- LICENSE
|
111
|
+
- README.md
|
112
|
+
- Rakefile
|
113
|
+
- app/mailers/negroni/mailer.rb
|
114
|
+
- app/views/negroni/mailer/password_change.html.erb
|
115
|
+
- app/views/negroni/mailer/reset_password_instructions.html.erb
|
116
|
+
- app/views/negroni/mailer/unlock_instructions.html.erb
|
117
|
+
- config/locales/en.yml
|
118
|
+
- config/routes.rb
|
119
|
+
- lib/negroni.rb
|
120
|
+
- lib/negroni/configuration.rb
|
121
|
+
- lib/negroni/controllers/helpers.rb
|
122
|
+
- lib/negroni/controllers/token_authenticable.rb
|
123
|
+
- lib/negroni/encryptor.rb
|
124
|
+
- lib/negroni/engine.rb
|
125
|
+
- lib/negroni/mailers/helpers.rb
|
126
|
+
- lib/negroni/models.rb
|
127
|
+
- lib/negroni/models/authenticable.rb
|
128
|
+
- lib/negroni/models/base.rb
|
129
|
+
- lib/negroni/models/lockable.rb
|
130
|
+
- lib/negroni/models/omniauthable.rb
|
131
|
+
- lib/negroni/models/recoverable.rb
|
132
|
+
- lib/negroni/models/registerable.rb
|
133
|
+
- lib/negroni/models/validatable.rb
|
134
|
+
- lib/negroni/modules.rb
|
135
|
+
- lib/negroni/omniauth.rb
|
136
|
+
- lib/negroni/omniauth/config.rb
|
137
|
+
- lib/negroni/orm/active_record.rb
|
138
|
+
- lib/negroni/orm/mongoid.rb
|
139
|
+
- lib/negroni/param_filter.rb
|
140
|
+
- lib/negroni/resolver.rb
|
141
|
+
- lib/negroni/token_generator.rb
|
142
|
+
- lib/negroni/token_not_found.rb
|
143
|
+
- lib/negroni/version.rb
|
144
|
+
- lib/tasks/negroni_tasks.rake
|
145
|
+
homepage: https://github.com/baberthal/negroni
|
146
|
+
licenses:
|
147
|
+
- MIT
|
148
|
+
metadata: {}
|
149
|
+
post_install_message:
|
150
|
+
rdoc_options: []
|
151
|
+
require_paths:
|
152
|
+
- lib
|
153
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
154
|
+
requirements:
|
155
|
+
- - ">="
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: 2.2.2
|
158
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
159
|
+
requirements:
|
160
|
+
- - ">="
|
161
|
+
- !ruby/object:Gem::Version
|
162
|
+
version: '0'
|
163
|
+
requirements: []
|
164
|
+
rubyforge_project:
|
165
|
+
rubygems_version: 2.5.2
|
166
|
+
signing_key:
|
167
|
+
specification_version: 4
|
168
|
+
summary: Very light user management plugin for rails api apps
|
169
|
+
test_files: []
|