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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 862ef7ec915b261c58ccae478762ddfaa3d8e786
4
+ data.tar.gz: b4dd5712588c466dcca659bba213cc350cc78fa3
5
+ SHA512:
6
+ metadata.gz: b8e97dfc7fb1c0defc7033859205fb5d6a2619f7cb38f795a4bd042ebcd0d275d331ce1f37bf4b6e13e7a2939f2839758f93b449e550a2928ab08a023329433e
7
+ data.tar.gz: cee2afbb8b7e955fac0fce75f153538fa1993c5a06e0761afa9bd274e33e00a16857473461c77dbbe19faa180819d18e1a14b4ea7c7fbdbce33b16e74a8a9b8b
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2016 J. Morgan Lieberthal
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,59 @@
1
+ # Negroni
2
+ Negroni is a user-management solution for Rails. It is indended for api-only
3
+ applications.
4
+
5
+ Born out of [devise][devise-gh], it removes all the session-related code,
6
+ focusing on JSON web token authentication, using [knock][knock-gh].
7
+
8
+ As with [devise][devise-gh], both ActiveRecord and Mongoid are supported.
9
+
10
+ ## Rails Version
11
+ This gem was developed with Rails version 5.0.0.1, but should work with anything
12
+ greater that Rails 5.
13
+
14
+ ## Usage
15
+ First, install the gem (see [installation](#Installation) for details.)
16
+
17
+ To install the initializers, run:
18
+
19
+ ```bash
20
+ $ bin/rails generate negroni:install
21
+ ```
22
+
23
+ ## Installation
24
+ Add this line to your application's Gemfile:
25
+
26
+ ```ruby
27
+ gem 'negroni'
28
+ ```
29
+
30
+ And then execute:
31
+ ```bash
32
+ $ bundle
33
+ ```
34
+
35
+ Or install it yourself as:
36
+ ```bash
37
+ $ gem install negroni
38
+ ```
39
+
40
+ ## Documentation
41
+
42
+ Documentation is avaliable by running:
43
+
44
+ ```bash
45
+ $ rake docs
46
+ ```
47
+
48
+ in the root folder of the project. Docs will be placed in doc/.
49
+
50
+
51
+ ## Contributing
52
+ Contribution directions go here.
53
+
54
+ ## License
55
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
56
+
57
+
58
+ [devise-gh]: https://github.com/plataformatec/devise
59
+ [knock-gh]: https://github.com/nsarno/knock
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require 'bundler/setup'
5
+ rescue LoadError
6
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
+ end
8
+
9
+ APP_RAKEFILE = File.expand_path('../spec/test_app/Rakefile', __FILE__)
10
+ load 'rails/tasks/engine.rake'
11
+
12
+ load 'rails/tasks/statistics.rake'
13
+
14
+ require 'bundler/gem_tasks'
15
+
16
+ ENV['COVERAGE'] ||= 'true'
17
+
18
+ require 'rspec/core/rake_task'
19
+ RSpec::Core::RakeTask.new(:spec) do |t|
20
+ t.pattern = Dir['spec/**/*_spec.rb']
21
+ t.rspec_opts = '-f doc'
22
+ end
23
+
24
+ task default: :spec
25
+
26
+ Dir["#{File.dirname(__FILE__)}/spec/support/orm/*.rb"].each do |file|
27
+ orm = File.basename(file).split('.').first
28
+ # There __must__ be a better way to do this.
29
+ namespace :spec do
30
+ desc "Run RSpec examples with AA_ORM=#{orm}"
31
+ task "orm:#{orm}" do
32
+ exit 1 unless system "AA_ORM=#{orm} rake spec"
33
+ end
34
+
35
+ desc 'Run RSpec examples for all supported orms'
36
+ task 'orm:all' => "spec:orm:#{orm}"
37
+ end
38
+ end
39
+
40
+ task ci: :default
41
+ task ci: 'spec:orm:all'
42
+
43
+ require 'yard/rake/yardoc_task'
44
+
45
+ YARD::Rake::YardocTask.new(:docs) do |t|
46
+ require 'yard'
47
+ t.options = ['--yardopts', '.yardopts']
48
+ t.stats_options = ['--list-undoc']
49
+ end
50
+
51
+ task default: :docs
52
+
53
+ require 'rubocop/rake_task'
54
+
55
+ desc 'Run RuboCop on the app, config, lib, and spec directories'
56
+ RuboCop::RakeTask.new do |t|
57
+ t.patterns = ['{app,config,lib,spec/^test_app}/**/*.rb']
58
+ t.formatters = %w(clang)
59
+ t.fail_on_error = false
60
+ t.requires = ['rubocop-rspec']
61
+ t.options = %w(--config .rubocop.yml)
62
+ end
63
+
64
+ task default: :rubocop
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined?(ActionMailer)
4
+ module Negroni
5
+ # @!parse
6
+ # # Mailer for all auth-related mail
7
+ # class Mailer < ActionMailer::Base; end
8
+ #
9
+ # The base mailer for all emails
10
+ class Mailer < Negroni.parent_mailer.constantize
11
+ include Negroni::Mailers::Helpers
12
+
13
+ # Sends `record` instructions to reset their password using `token`
14
+ #
15
+ # @param record [Object, #email] the record to send instructions
16
+ # @param token [String] the token needed to reset the password
17
+ # @param options [Hash] a hash of options for the mail
18
+ #
19
+ def reset_password_instructions(record, token, options = {})
20
+ @token = token
21
+ negroni_mail(record, :reset_password_instructions, options)
22
+ end
23
+
24
+ # Sends `record` instructions to unlock their account using `token`
25
+ #
26
+ # @param record [Object, #email] the record to send instructions
27
+ # @param token [String] the token needed to reset the password
28
+ # @param options [Hash] a hash of options for the mail
29
+ #
30
+ def unlock_instructions(record, token, options = {})
31
+ @token = token
32
+ negroni_mail(record, :unlock_instructions, options)
33
+ end
34
+
35
+ # Sends `record` a notification of password changes
36
+ #
37
+ # @param record [Object, #email] the record to send instructions
38
+ # @param options [Hash] a hash of options for the mail
39
+ #
40
+ def password_change(record, options = {})
41
+ negroni_mail(record, :password_change, options)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,5 @@
1
+ <p>Hello <%= @resource.email %></p>
2
+
3
+ <p>We're contacting you to notify you that your password has changed.</p>
4
+
5
+ <p>If you wanted this to happen, you can safely ignore this email. Otherwise, please contact us.</p>
@@ -0,0 +1,8 @@
1
+ <p>Hello <%= @resource.email %></p>
2
+
3
+ <p>Someone has requested a link to change your password. You can do this via the below link.</p>
4
+
5
+ <%# <p><%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %1></p> %>
6
+
7
+ <p>If you didn't request this, please ignore this email.</p>
8
+ <p>Your password won't change until you access the above link and create a new one.</p>
@@ -0,0 +1,7 @@
1
+ <p>Hello <%= @resource.email %>!</p>
2
+
3
+ <p>Your account has been locked due to an excessive number of unsuccessful sign in attempts.</p>
4
+
5
+ <p>Click the link below to unlock your account:</p>
6
+
7
+ <%# <p><%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %1></p> %>
@@ -0,0 +1,9 @@
1
+ en:
2
+ errors:
3
+ messages:
4
+ expired: "has expired, please request a new one"
5
+ not_found: "not found"
6
+ not_locked: "was not locked"
7
+ not_saved:
8
+ one: "one (1) error prohibited this %{resource} from being saved:"
9
+ other: "%{count} errors prohibited this %{resource} from being saved:"
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ Negroni::Engine.routes.draw do
4
+ end
@@ -0,0 +1,209 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails'
4
+ require 'knock'
5
+ require 'active_support/core_ext/numeric/time'
6
+ require 'active_support/dependencies'
7
+ require 'orm_adapter'
8
+ require 'securerandom'
9
+
10
+ require 'negroni/version'
11
+
12
+ # Negroni extracts common authentication configuration to be used across the
13
+ # application.
14
+ module Negroni
15
+ extend ActiveSupport::Autoload
16
+
17
+ autoload :Configuration, 'negroni/configuration'
18
+ autoload :Encryptor, 'negroni/encryptor'
19
+ autoload :OmniAuth, 'negroni/omniauth'
20
+ autoload :ParamFilter, 'negroni/param_filter'
21
+ autoload :Resolver, 'negroni/resolver'
22
+ autoload :TokenGenerator, 'negroni/token_generator'
23
+ autoload :TokenNotFound, 'negroni/token_not_found'
24
+
25
+ # Namespace for controllers
26
+ module Controllers
27
+ autoload :Helpers, 'negroni/controllers/helpers'
28
+ autoload :TokenAuthenticable, 'negroni/controllers/token_authenticable'
29
+ end
30
+
31
+ # Namespace for mailer-related code
32
+ module Mailers
33
+ autoload :Helpers, 'negroni/mailers/helpers'
34
+ end
35
+
36
+ # @api private
37
+ # To hold all modules
38
+ ALL = [] # rubocop:disable Style/MutableConstant
39
+
40
+ extend Configuration::Delegation
41
+
42
+ class << self
43
+ # @!group Private Configuration
44
+
45
+ # Stores OmniAuth configurations
46
+ # @return [Hash<Symbol,OmniAuth::Config>]
47
+ attr_accessor :omniauth_configs
48
+
49
+ # When true, enter in paranoid mode to avoid user enumeration.
50
+ # @return [Boolean]
51
+ attr_accessor :paranoid
52
+
53
+ # Stores the token generator
54
+ # @return [TokenGenerator]
55
+ attr_accessor :token_generator
56
+
57
+ # Stores the secret key
58
+ # @return [String]
59
+ attr_accessor :secret_key
60
+
61
+ # @!attribute [rw] mailer
62
+ # @return [Mailer]
63
+
64
+ # Returns the mailer
65
+ # @return [Mailer]
66
+ def mailer
67
+ @mailer_ref.resolve
68
+ end
69
+
70
+ # Set the mailer class
71
+ def mailer=(new_mailer)
72
+ @mailer_ref = ref(new_mailer)
73
+ end
74
+
75
+ # @!endgroup
76
+
77
+ # Yields the module for configuration. This is the standard way to configure
78
+ # Negroni.
79
+ #
80
+ # @yield [Negroni]
81
+ # @return [Void]
82
+ def configure
83
+ yield self
84
+ end
85
+
86
+ # List of OmniAuth provider that are registered
87
+ # @return [Array<Symbol>]
88
+ def omniauth_providers
89
+ omniauth_configs.keys
90
+ end
91
+
92
+ # Register an OmniAuth provider.
93
+ #
94
+ # @param provider [Symbol] the OmniAuth provider to register
95
+ # @param args [Object*] any additional arguments needed to configure and
96
+ # initialize the provider
97
+ #
98
+ # @example
99
+ # config.omniauth :github, APP_ID, APP_SECRET
100
+ #
101
+ # @return [Void]
102
+ def omniauth(provider, *args)
103
+ config = Negroni::OmniAuth::Config.new(provider, args)
104
+ omniauth_configs[config.strategy_name.to_sym] = config
105
+ end
106
+
107
+ # Generate a friendly string randomly to be used as a token.
108
+ #
109
+ # @note: Taken from `Devise`.
110
+ #
111
+ # @param length [Integer] the length of the token. Default: 20
112
+ # @return [String] the generated token
113
+ def friendly_token(length = 20)
114
+ rlength = (length * 3) / 4
115
+ SecureRandom.urlsafe_base64(rlength).tr('lIO0', 'sxyz')
116
+ end
117
+
118
+ # Securely compare two passwords
119
+ #
120
+ # @param a [String] the hashed password
121
+ # @param b [String] the password to compare
122
+ #
123
+ # @return [Boolean] whether or not the passwords match
124
+ def secure_compare(a, b)
125
+ return false if a.blank? || b.blank? || a.bytesize != b.bytesize
126
+
127
+ l = a.unpack "C#{a.bytesize}"
128
+
129
+ res = 0
130
+ b.each_byte { |byte| res |= byte ^ l.shift }
131
+ res.zero?
132
+ end
133
+
134
+ private
135
+
136
+ # Create a reference
137
+ # @return [Resolver]
138
+ def ref(arg)
139
+ ActiveSupport::Dependencies.reference(arg)
140
+ Resolver.new(arg)
141
+ end
142
+ end
143
+
144
+ self.omniauth_configs = {}
145
+ self.paranoid = false
146
+ self.token_generator = nil
147
+ self.mailer = 'Negroni::Mailer'
148
+
149
+ class << self
150
+ # @!group Module Registration
151
+
152
+ # Register available negroni modules. For the standard modules that Negroni
153
+ # provides, this method is called from lib/negroni/modules.rb. Third-party
154
+ # modules need to be added explicitly using this method.
155
+ #
156
+ # @note that adding a module using this method does not cause it to be used
157
+ # in the authentication process. That requires that the module be listed in
158
+ # the arguments passed to the 'negroni' method in the model class
159
+ # definition.
160
+ #
161
+ # @param module_name [Symbol] the name of the module to register
162
+ # @param options [Hash] a hash of options
163
+ # @option options [String] :model the load path to a custom __model__ for
164
+ # this module (to autoload.)
165
+ # @option options [Symbol, Boolean] :controller the name of an existing or
166
+ # custom __controller__ for this module.
167
+ # @option options [Symbol, Boolean] :route the named __route__ helper for
168
+ # this module.
169
+ #
170
+ # All values which accept a boolean will have the same name as the given
171
+ # module name.
172
+ #
173
+ # @return [Void]
174
+ #
175
+ # @example
176
+ # Negroni.register_module(:party_module)
177
+ # Negroni.register_module(:party_module, model: 'party_module/model')
178
+ # Negroni.register_module(:party_module, insert_at: 0)
179
+ #
180
+ def register_module(module_name, options = {})
181
+ options.assert_valid_keys(:model, :controller, :insert_at)
182
+
183
+ ALL.insert (options[:insert_at] || -1), module_name
184
+
185
+ if (controller = options[:controller])
186
+ register_controller(controller, module_name)
187
+ end
188
+
189
+ options[:model] && register_model(options[:model], module_name)
190
+ end
191
+
192
+ private
193
+
194
+ def register_controller(controller, module_name)
195
+ controller = (controller == true ? module_name : controller)
196
+ CONTROLLERS[module_name] = controller
197
+ end
198
+
199
+ def register_model(model, module_name)
200
+ path = (model == true ? "negroni/models/#{module_name}" : model)
201
+ camelized = ActiveSupport::Inflector.camelize(module_name.to_s)
202
+ Negroni::Models.send(:autoload, camelized.to_sym, path)
203
+ end
204
+ end
205
+ end
206
+
207
+ require 'negroni/models'
208
+ require 'negroni/modules'
209
+ require 'negroni/engine'