negroni 0.1.0

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.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +20 -0
  3. data/README.md +59 -0
  4. data/Rakefile +64 -0
  5. data/app/mailers/negroni/mailer.rb +45 -0
  6. data/app/views/negroni/mailer/password_change.html.erb +5 -0
  7. data/app/views/negroni/mailer/reset_password_instructions.html.erb +8 -0
  8. data/app/views/negroni/mailer/unlock_instructions.html.erb +7 -0
  9. data/config/locales/en.yml +9 -0
  10. data/config/routes.rb +4 -0
  11. data/lib/negroni.rb +209 -0
  12. data/lib/negroni/configuration.rb +231 -0
  13. data/lib/negroni/controllers/helpers.rb +29 -0
  14. data/lib/negroni/controllers/token_authenticable.rb +20 -0
  15. data/lib/negroni/encryptor.rb +35 -0
  16. data/lib/negroni/engine.rb +35 -0
  17. data/lib/negroni/mailers/helpers.rb +112 -0
  18. data/lib/negroni/models.rb +138 -0
  19. data/lib/negroni/models/authenticable.rb +197 -0
  20. data/lib/negroni/models/base.rb +318 -0
  21. data/lib/negroni/models/lockable.rb +216 -0
  22. data/lib/negroni/models/omniauthable.rb +33 -0
  23. data/lib/negroni/models/recoverable.rb +204 -0
  24. data/lib/negroni/models/registerable.rb +14 -0
  25. data/lib/negroni/models/validatable.rb +63 -0
  26. data/lib/negroni/modules.rb +12 -0
  27. data/lib/negroni/omniauth.rb +25 -0
  28. data/lib/negroni/omniauth/config.rb +81 -0
  29. data/lib/negroni/orm/active_record.rb +7 -0
  30. data/lib/negroni/orm/mongoid.rb +6 -0
  31. data/lib/negroni/param_filter.rb +53 -0
  32. data/lib/negroni/resolver.rb +17 -0
  33. data/lib/negroni/token_generator.rb +58 -0
  34. data/lib/negroni/token_not_found.rb +13 -0
  35. data/lib/negroni/version.rb +6 -0
  36. data/lib/tasks/negroni_tasks.rake +5 -0
  37. 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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'orm_adapter/adapters/active_record'
4
+
5
+ ActiveSupport.on_load(:active_record) do
6
+ extend Negroni::Models
7
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ ActiveSupport.on_load(:mongoid) do
4
+ require 'orm_adapter/adapters/mongoid'
5
+ Mongoid::Document::ClassMethods.send :include, Negroni::Models
6
+ 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
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Negroni
4
+ # Version number
5
+ VERSION = '0.1.0'
6
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+ # desc "Explaining what the task does"
3
+ # task :api_auth do
4
+ # # Task goes here
5
+ # end
metadata ADDED
@@ -0,0 +1,169 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: negroni
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-12 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: []