kingsman 0.0.0.beta → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +5 -4
- data/app/controllers/kingsman/confirmations_controller.rb +54 -0
- data/app/controllers/kingsman/omniauth_callbacks_controller.rb +36 -0
- data/app/controllers/kingsman/passwords_controller.rb +83 -0
- data/app/controllers/kingsman/registrations_controller.rb +168 -0
- data/app/controllers/kingsman/sessions_controller.rb +83 -0
- data/app/controllers/kingsman/unlocks_controller.rb +52 -0
- data/app/controllers/kingsman_controller.rb +252 -0
- data/app/jobs/application_job.rb +2 -0
- data/app/mailers/kingsman/mailer.rb +30 -0
- data/app/views/kingsman/confirmations/new.html.erb +16 -0
- data/app/views/kingsman/home/index.html.erb +1 -0
- data/app/views/kingsman/mailer/confirmation_instructions.html.erb +5 -0
- data/app/views/kingsman/mailer/email_changed.html.erb +7 -0
- data/app/views/kingsman/mailer/password_change.html.erb +3 -0
- data/app/views/kingsman/mailer/reset_password_instructions.html.erb +8 -0
- data/app/views/kingsman/mailer/unlock_instructions.html.erb +7 -0
- data/app/views/kingsman/passwords/edit.html.erb +25 -0
- data/app/views/kingsman/passwords/new.html.erb +16 -0
- data/app/views/kingsman/registrations/edit.html.erb +42 -0
- data/app/views/kingsman/registrations/new.html.erb +29 -0
- data/app/views/kingsman/sessions/new.html.erb +26 -0
- data/app/views/kingsman/shared/_error_messages.html.erb +15 -0
- data/app/views/kingsman/shared/_links.html.erb +25 -0
- data/app/views/kingsman/unlocks/new.html.erb +16 -0
- data/app/views/kingsman/up/index.html.erb +11 -0
- data/config/application.rb +0 -0
- data/config/locales/en.yml +63 -0
- data/config.ru +6 -0
- data/lib/generators/active_record/kingsman_generator.rb +127 -0
- data/lib/generators/active_record/templates/migration.rb +20 -0
- data/lib/generators/active_record/templates/migration_existing.rb +27 -0
- data/lib/generators/kingsman/controllers_generator.rb +46 -0
- data/lib/generators/kingsman/install_generator.rb +42 -0
- data/lib/generators/kingsman/kingsman_generator.rb +28 -0
- data/lib/generators/kingsman/orm_helpers.rb +40 -0
- data/lib/generators/kingsman/views_generator.rb +145 -0
- data/lib/generators/mongoid/kingsman_generator.rb +57 -0
- data/lib/generators/templates/README +36 -0
- data/lib/generators/templates/controllers/README +14 -0
- data/lib/generators/templates/controllers/confirmations_controller.rb +30 -0
- data/lib/generators/templates/controllers/omniauth_callbacks_controller.rb +30 -0
- data/lib/generators/templates/controllers/passwords_controller.rb +34 -0
- data/lib/generators/templates/controllers/registrations_controller.rb +62 -0
- data/lib/generators/templates/controllers/sessions_controller.rb +27 -0
- data/lib/generators/templates/controllers/unlocks_controller.rb +30 -0
- data/lib/generators/templates/kingsman.rb +313 -0
- data/lib/generators/templates/markerb/confirmation_instructions.markerb +5 -0
- data/lib/generators/templates/markerb/email_changed.markerb +7 -0
- data/lib/generators/templates/markerb/password_change.markerb +3 -0
- data/lib/generators/templates/markerb/reset_password_instructions.markerb +8 -0
- data/lib/generators/templates/markerb/unlock_instructions.markerb +7 -0
- data/lib/generators/templates/simple_form_for/confirmations/new.html.erb +20 -0
- data/lib/generators/templates/simple_form_for/passwords/edit.html.erb +27 -0
- data/lib/generators/templates/simple_form_for/passwords/new.html.erb +18 -0
- data/lib/generators/templates/simple_form_for/registrations/edit.html.erb +35 -0
- data/lib/generators/templates/simple_form_for/registrations/new.html.erb +25 -0
- data/lib/generators/templates/simple_form_for/sessions/new.html.erb +20 -0
- data/lib/generators/templates/simple_form_for/unlocks/new.html.erb +19 -0
- data/lib/kingsman/autoloader.rb +31 -0
- data/lib/kingsman/controllers/helpers.rb +221 -0
- data/lib/kingsman/controllers/rememberable.rb +57 -0
- data/lib/kingsman/controllers/responder.rb +35 -0
- data/lib/kingsman/controllers/scoped_views.rb +19 -0
- data/lib/kingsman/controllers/sign_in_out.rb +112 -0
- data/lib/kingsman/controllers/store_location.rb +76 -0
- data/lib/kingsman/controllers/url_helpers.rb +72 -0
- data/lib/kingsman/delegator.rb +18 -0
- data/lib/kingsman/encryptor.rb +19 -0
- data/lib/kingsman/failure_app.rb +280 -0
- data/lib/kingsman/hooks/activatable.rb +12 -0
- data/lib/kingsman/hooks/csrf_cleaner.rb +14 -0
- data/lib/kingsman/hooks/forgetable.rb +11 -0
- data/lib/kingsman/hooks/lockable.rb +9 -0
- data/lib/kingsman/hooks/proxy.rb +23 -0
- data/lib/kingsman/hooks/rememberable.rb +9 -0
- data/lib/kingsman/hooks/timeoutable.rb +35 -0
- data/lib/kingsman/hooks/trackable.rb +11 -0
- data/lib/kingsman/hooks.rb +6 -0
- data/lib/kingsman/jets/routes.rb +195 -0
- data/lib/kingsman/jets/warden_compat.rb +15 -0
- data/lib/kingsman/jets.rb +39 -0
- data/lib/kingsman/mailers/helpers.rb +93 -0
- data/lib/kingsman/mapping.rb +148 -0
- data/lib/kingsman/models/authenticatable.rb +310 -0
- data/lib/kingsman/models/confirmable.rb +369 -0
- data/lib/kingsman/models/database_authenticatable.rb +206 -0
- data/lib/kingsman/models/lockable.rb +214 -0
- data/lib/kingsman/models/omniauthable.rb +29 -0
- data/lib/kingsman/models/recoverable.rb +156 -0
- data/lib/kingsman/models/registerable.rb +29 -0
- data/lib/kingsman/models/rememberable.rb +158 -0
- data/lib/kingsman/models/timeoutable.rb +45 -0
- data/lib/kingsman/models/trackable.rb +51 -0
- data/lib/kingsman/models/validatable.rb +68 -0
- data/lib/kingsman/models.rb +122 -0
- data/lib/kingsman/modules.rb +33 -0
- data/lib/kingsman/omniauth/config.rb +47 -0
- data/lib/kingsman/omniauth/url_helpers.rb +28 -0
- data/lib/kingsman/omniauth.rb +20 -0
- data/lib/kingsman/orm/active_record.rb +13 -0
- data/lib/kingsman/orm/mongoid.rb +8 -0
- data/lib/kingsman/orm.rb +37 -0
- data/lib/kingsman/parameter_filter.rb +44 -0
- data/lib/kingsman/parameter_sanitizer.rb +173 -0
- data/lib/kingsman/secret_key_finder.rb +27 -0
- data/lib/kingsman/strategies/authenticatable.rb +178 -0
- data/lib/kingsman/strategies/base.rb +22 -0
- data/lib/kingsman/strategies/database_authenticatable.rb +31 -0
- data/lib/kingsman/strategies/rememberable.rb +67 -0
- data/lib/kingsman/token_generator.rb +32 -0
- data/lib/kingsman/version.rb +1 -1
- data/lib/kingsman.rb +427 -3
- metadata +304 -11
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kingsman
|
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 +kingsman+:
|
13
|
+
#
|
14
|
+
# * +email_regexp+: the regular expression used to validate e-mails;
|
15
|
+
# * +password_length+: a range expressing password length. Defaults to 6..128.
|
16
|
+
#
|
17
|
+
module Validatable
|
18
|
+
# All validations used by this module.
|
19
|
+
VALIDATIONS = [:validates_presence_of, :validates_uniqueness_of, :validates_format_of,
|
20
|
+
:validates_confirmation_of, :validates_length_of].freeze
|
21
|
+
|
22
|
+
def self.required_fields(klass)
|
23
|
+
[]
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.included(base)
|
27
|
+
base.extend ClassMethods
|
28
|
+
assert_validations_api!(base)
|
29
|
+
|
30
|
+
base.class_eval do
|
31
|
+
validates_presence_of :email, if: :email_required?
|
32
|
+
validates_uniqueness_of :email, allow_blank: true, case_sensitive: true, if: :kingsman_will_save_change_to_email?
|
33
|
+
validates_format_of :email, with: email_regexp, allow_blank: true, if: :kingsman_will_save_change_to_email?
|
34
|
+
|
35
|
+
validates_presence_of :password, if: :password_required?
|
36
|
+
validates_confirmation_of :password, if: :password_required?
|
37
|
+
validates_length_of :password, within: password_length, allow_blank: true
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.assert_validations_api!(base) #:nodoc:
|
42
|
+
unavailable_validations = VALIDATIONS.select { |v| !base.respond_to?(v) }
|
43
|
+
|
44
|
+
unless unavailable_validations.empty?
|
45
|
+
raise "Could not use :validatable module since #{base} does not respond " \
|
46
|
+
"to the following methods: #{unavailable_validations.to_sentence}."
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
protected
|
51
|
+
|
52
|
+
# Checks whether a password is needed or not. For validations only.
|
53
|
+
# Passwords are always required if it's a new record, or if the password
|
54
|
+
# or confirmation are being set somewhere.
|
55
|
+
def password_required?
|
56
|
+
!persisted? || !password.nil? || !password_confirmation.nil?
|
57
|
+
end
|
58
|
+
|
59
|
+
def email_required?
|
60
|
+
true
|
61
|
+
end
|
62
|
+
|
63
|
+
module ClassMethods
|
64
|
+
Kingsman::Models.config(self, :email_regexp, :password_length)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kingsman
|
4
|
+
module Models
|
5
|
+
class MissingAttribute < StandardError
|
6
|
+
def initialize(attributes)
|
7
|
+
@attributes = attributes
|
8
|
+
end
|
9
|
+
|
10
|
+
def message
|
11
|
+
"The following attribute(s) is (are) missing on your model: #{@attributes.join(", ")}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Creates configuration values for Kingsman and for the given module.
|
16
|
+
#
|
17
|
+
# Kingsman::Models.config(Kingsman::Models::DatabaseAuthenticatable, :stretches)
|
18
|
+
#
|
19
|
+
# The line above creates:
|
20
|
+
#
|
21
|
+
# 1) An accessor called Kingsman.stretches, which value is used by default;
|
22
|
+
#
|
23
|
+
# 2) Some class methods for your model Model.stretches and Model.stretches=
|
24
|
+
# which have higher priority than Kingsman.stretches;
|
25
|
+
#
|
26
|
+
# 3) And an instance method stretches.
|
27
|
+
#
|
28
|
+
# To add the class methods you need to have a module ClassMethods defined
|
29
|
+
# inside the given class.
|
30
|
+
#
|
31
|
+
def self.config(mod, *accessors) #:nodoc:
|
32
|
+
class << mod; attr_accessor :available_configs; end
|
33
|
+
mod.available_configs = accessors
|
34
|
+
|
35
|
+
accessors.each do |accessor|
|
36
|
+
mod.class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
37
|
+
def #{accessor}
|
38
|
+
if defined?(@#{accessor})
|
39
|
+
@#{accessor}
|
40
|
+
elsif superclass.respond_to?(:#{accessor})
|
41
|
+
superclass.#{accessor}
|
42
|
+
else
|
43
|
+
Kingsman.#{accessor}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def #{accessor}=(value)
|
48
|
+
@#{accessor} = value
|
49
|
+
end
|
50
|
+
METHOD
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.check_fields!(klass)
|
55
|
+
failed_attributes = []
|
56
|
+
instance = klass.new
|
57
|
+
|
58
|
+
klass.kingsman_modules.each do |mod|
|
59
|
+
constant = const_get(mod.to_s.classify)
|
60
|
+
|
61
|
+
constant.required_fields(klass).each do |field|
|
62
|
+
failed_attributes << field unless instance.respond_to?(field)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
if failed_attributes.any?
|
67
|
+
fail Kingsman::Models::MissingAttribute.new(failed_attributes)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Include the chosen kingsman modules in your model:
|
72
|
+
#
|
73
|
+
# kingsman :database_authenticatable, :confirmable, :recoverable
|
74
|
+
#
|
75
|
+
# You can also give any of the kingsman configuration values in form of a hash,
|
76
|
+
# with specific values for this model. Please check your Kingsman initializer
|
77
|
+
# for a complete description on those values.
|
78
|
+
#
|
79
|
+
def kingsman(*modules)
|
80
|
+
options = modules.extract_options!.dup
|
81
|
+
|
82
|
+
selected_modules = modules.map(&:to_sym).uniq.sort_by do |s|
|
83
|
+
Kingsman::ALL.index(s) || -1 # follow Kingsman::ALL order
|
84
|
+
end
|
85
|
+
|
86
|
+
kingsman_modules_hook! do
|
87
|
+
include Kingsman::Orm
|
88
|
+
include Kingsman::Models::Authenticatable
|
89
|
+
|
90
|
+
selected_modules.each do |m|
|
91
|
+
mod = Kingsman::Models.const_get(m.to_s.classify)
|
92
|
+
|
93
|
+
if mod.const_defined?("ClassMethods")
|
94
|
+
class_mod = mod.const_get("ClassMethods")
|
95
|
+
extend class_mod
|
96
|
+
|
97
|
+
if class_mod.respond_to?(:available_configs)
|
98
|
+
available_configs = class_mod.available_configs
|
99
|
+
available_configs.each do |config|
|
100
|
+
next unless options.key?(config)
|
101
|
+
send(:"#{config}=", options.delete(config))
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
include mod
|
107
|
+
end
|
108
|
+
|
109
|
+
self.kingsman_modules |= selected_modules
|
110
|
+
options.each { |key, value| send(:"#{key}=", value) }
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# The hook which is called inside kingsman.
|
115
|
+
# So your ORM can include kingsman compatibility stuff.
|
116
|
+
def kingsman_modules_hook!
|
117
|
+
yield
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
require 'kingsman/models/authenticatable'
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'active_support/core_ext/object/with_options'
|
2
|
+
|
3
|
+
# Kingsman.with_options model: true do |k|
|
4
|
+
# k.add_module :database_authenticatable
|
5
|
+
# # k.add_module :database_authenticatable, controller: :sessions, route: { session: [nil, :new, :destroy] }
|
6
|
+
# end
|
7
|
+
|
8
|
+
Kingsman.with_options model: true do |k|
|
9
|
+
# Strategies first
|
10
|
+
k.with_options strategy: true do |s|
|
11
|
+
routes = [nil, :new, :destroy]
|
12
|
+
s.add_module :database_authenticatable, controller: :sessions, route: { session: routes }
|
13
|
+
s.add_module :rememberable, no_input: true
|
14
|
+
end
|
15
|
+
|
16
|
+
# Other authentications
|
17
|
+
k.add_module :omniauthable, controller: :omniauth_callbacks, route: :omniauth_callback
|
18
|
+
|
19
|
+
# Misc after
|
20
|
+
routes = [nil, :new, :edit]
|
21
|
+
k.add_module :recoverable, controller: :passwords, route: { password: routes }
|
22
|
+
k.add_module :registerable, controller: :registrations, route: { registration: (routes << :cancel) }
|
23
|
+
k.add_module :validatable
|
24
|
+
|
25
|
+
# The ones which can sign out after
|
26
|
+
routes = [nil, :new]
|
27
|
+
k.add_module :confirmable, controller: :confirmations, route: { confirmation: routes }
|
28
|
+
k.add_module :lockable, controller: :unlocks, route: { unlock: routes }
|
29
|
+
k.add_module :timeoutable
|
30
|
+
|
31
|
+
# Stats for last, so we make sure the user is really signed in
|
32
|
+
k.add_module :trackable
|
33
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kingsman
|
4
|
+
module OmniAuth
|
5
|
+
class StrategyNotFound < NameError
|
6
|
+
def initialize(strategy)
|
7
|
+
@strategy = strategy
|
8
|
+
super("Could not find a strategy with name `#{strategy}'. " \
|
9
|
+
"Please ensure it is required or explicitly set it using the :strategy_class option.")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Config
|
14
|
+
attr_accessor :strategy
|
15
|
+
attr_reader :args, :options, :provider, :strategy_name
|
16
|
+
|
17
|
+
def initialize(provider, args)
|
18
|
+
@provider = provider
|
19
|
+
@args = args
|
20
|
+
@options = @args.last.is_a?(Hash) ? @args.last : {}
|
21
|
+
@strategy = nil
|
22
|
+
@strategy_name = options[:name] || @provider
|
23
|
+
@strategy_class = options.delete(:strategy_class)
|
24
|
+
end
|
25
|
+
|
26
|
+
def strategy_class
|
27
|
+
@strategy_class ||= find_strategy || autoload_strategy
|
28
|
+
end
|
29
|
+
|
30
|
+
def find_strategy
|
31
|
+
::OmniAuth.strategies.find do |strategy_class|
|
32
|
+
strategy_class.to_s =~ /#{::OmniAuth::Utils.camelize(strategy_name)}$/ ||
|
33
|
+
strategy_class.default_options[:name] == strategy_name
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def autoload_strategy
|
38
|
+
name = ::OmniAuth::Utils.camelize(provider.to_s)
|
39
|
+
if ::OmniAuth::Strategies.const_defined?(name)
|
40
|
+
::OmniAuth::Strategies.const_get(name)
|
41
|
+
else
|
42
|
+
raise StrategyNotFound, name
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kingsman
|
4
|
+
module OmniAuth
|
5
|
+
# Note: scope is at the end of the method name for Jets whereas it is at the beginning for Rails
|
6
|
+
module UrlHelpers
|
7
|
+
def omniauth_authorize_path(resource_or_scope, provider, *args)
|
8
|
+
scope = Kingsman::Mapping.find_scope!(resource_or_scope)
|
9
|
+
_kingsman_route_context.send("#{provider}_omniauth_authorize_#{scope}_path", *args)
|
10
|
+
end
|
11
|
+
|
12
|
+
def omniauth_authorize_url(resource_or_scope, provider, *args)
|
13
|
+
scope = Kingsman::Mapping.find_scope!(resource_or_scope)
|
14
|
+
_kingsman_route_context.send("#{provider}_omniauth_authorize_#{scope}_url", *args)
|
15
|
+
end
|
16
|
+
|
17
|
+
def omniauth_callback_path(resource_or_scope, provider, *args)
|
18
|
+
scope = Kingsman::Mapping.find_scope!(resource_or_scope)
|
19
|
+
_kingsman_route_context.send("#{provider}_omniauth_callback_#{scope}_path", *args)
|
20
|
+
end
|
21
|
+
|
22
|
+
def omniauth_callback_url(resource_or_scope, provider, *args)
|
23
|
+
scope = Kingsman::Mapping.find_scope!(resource_or_scope)
|
24
|
+
_kingsman_route_context.send("#{provider}_omniauth_callback_#{scope}_url", *args)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
begin
|
4
|
+
gem "omniauth", ">= 1.0.0"
|
5
|
+
|
6
|
+
require "omniauth"
|
7
|
+
rescue LoadError
|
8
|
+
warn "Could not load 'omniauth'. Please ensure you have the omniauth gem >= 1.0.0 installed and listed in your Gemfile."
|
9
|
+
raise
|
10
|
+
end
|
11
|
+
|
12
|
+
# Clean up the default path_prefix. It will be automatically set by Kingsman.
|
13
|
+
OmniAuth.config.path_prefix = nil
|
14
|
+
|
15
|
+
OmniAuth.config.on_failure = Proc.new do |env|
|
16
|
+
env['kingsman.mapping'] = Kingsman::Mapping.find_by_path!(env['PATH_INFO'], :path)
|
17
|
+
controller_name = ActiveSupport::Inflector.camelize(env['kingsman.mapping'].controllers[:omniauth_callbacks])
|
18
|
+
controller_klass = ActiveSupport::Inflector.constantize("#{controller_name}Controller")
|
19
|
+
controller_klass.action(:failure).call(env)
|
20
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# loaded from config/initalizers/kingsman.rb
|
4
|
+
#
|
5
|
+
# Kingsman.setup do |config|
|
6
|
+
# require 'kingsman/orm/active_record'
|
7
|
+
#
|
8
|
+
require 'orm_adapter'
|
9
|
+
require 'orm_adapter/adapters/active_record'
|
10
|
+
|
11
|
+
ActiveSupport.on_load(:active_record) do
|
12
|
+
extend Kingsman::Models
|
13
|
+
end
|
data/lib/kingsman/orm.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
module Kingsman
|
2
|
+
module Orm # :nodoc:
|
3
|
+
def self.active_record?(model)
|
4
|
+
defined?(ActiveRecord) && model < ActiveRecord::Base
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.included(model)
|
8
|
+
model.include DirtyTrackingMethods
|
9
|
+
end
|
10
|
+
|
11
|
+
module DirtyTrackingMethods
|
12
|
+
def kingsman_email_before_last_save
|
13
|
+
email_before_last_save
|
14
|
+
end
|
15
|
+
|
16
|
+
def kingsman_email_in_database
|
17
|
+
email_in_database
|
18
|
+
end
|
19
|
+
|
20
|
+
def kingsman_saved_change_to_email?
|
21
|
+
saved_change_to_email?
|
22
|
+
end
|
23
|
+
|
24
|
+
def kingsman_saved_change_to_encrypted_password?
|
25
|
+
saved_change_to_encrypted_password?
|
26
|
+
end
|
27
|
+
|
28
|
+
def kingsman_will_save_change_to_email?
|
29
|
+
will_save_change_to_email?
|
30
|
+
end
|
31
|
+
|
32
|
+
def kingsman_respond_to_and_will_save_change_to_attribute?(attribute)
|
33
|
+
respond_to?("will_save_change_to_#{attribute}?") && send("will_save_change_to_#{attribute}?")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kingsman
|
4
|
+
class ParameterFilter
|
5
|
+
def initialize(case_insensitive_keys, strip_whitespace_keys)
|
6
|
+
@case_insensitive_keys = case_insensitive_keys || []
|
7
|
+
@strip_whitespace_keys = strip_whitespace_keys || []
|
8
|
+
end
|
9
|
+
|
10
|
+
def filter(conditions)
|
11
|
+
conditions = stringify_params(conditions.dup)
|
12
|
+
|
13
|
+
conditions.merge!(filtered_hash_by_method_for_given_keys(conditions.dup, :downcase, @case_insensitive_keys))
|
14
|
+
conditions.merge!(filtered_hash_by_method_for_given_keys(conditions.dup, :strip, @strip_whitespace_keys))
|
15
|
+
|
16
|
+
conditions
|
17
|
+
end
|
18
|
+
|
19
|
+
def filtered_hash_by_method_for_given_keys(conditions, method, condition_keys)
|
20
|
+
condition_keys.each do |k|
|
21
|
+
next unless conditions.key?(k)
|
22
|
+
|
23
|
+
value = conditions[k]
|
24
|
+
conditions[k] = value.send(method) if value.respond_to?(method)
|
25
|
+
end
|
26
|
+
|
27
|
+
conditions
|
28
|
+
end
|
29
|
+
|
30
|
+
# Force keys to be string to avoid injection on mongoid related database.
|
31
|
+
def stringify_params(conditions)
|
32
|
+
return conditions unless conditions.is_a?(Hash)
|
33
|
+
conditions.each do |k, v|
|
34
|
+
conditions[k] = v.to_s if param_requires_string_conversion?(v)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def param_requires_string_conversion?(value)
|
41
|
+
true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kingsman
|
4
|
+
# The +ParameterSanitizer+ deals with permitting specific parameters values
|
5
|
+
# for each +Kingsman+ scope in the application.
|
6
|
+
#
|
7
|
+
# The sanitizer knows about Kingsman default parameters (like +password+ and
|
8
|
+
# +password_confirmation+ for the `RegistrationsController`), and you can
|
9
|
+
# extend or change the permitted parameters list on your controllers.
|
10
|
+
#
|
11
|
+
# === Permitting new parameters
|
12
|
+
#
|
13
|
+
# You can add new parameters to the permitted list using the +permit+ method
|
14
|
+
# in a +before_action+ method, for instance.
|
15
|
+
#
|
16
|
+
# class ApplicationController < ActionController::Base
|
17
|
+
# before_action :configure_permitted_parameters, if: :kingsman_controller?
|
18
|
+
#
|
19
|
+
# protected
|
20
|
+
#
|
21
|
+
# def configure_permitted_parameters
|
22
|
+
# # Permit the `subscribe_newsletter` parameter along with the other
|
23
|
+
# # sign up parameters.
|
24
|
+
# kingsman_parameter_sanitizer.permit(:sign_up, keys: [:subscribe_newsletter])
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# Using a block yields an +ActionController::Parameters+ object so you can
|
29
|
+
# permit nested parameters and have more control over how the parameters are
|
30
|
+
# permitted in your controller.
|
31
|
+
#
|
32
|
+
# def configure_permitted_parameters
|
33
|
+
# kingsman_parameter_sanitizer.permit(:sign_up) do |user|
|
34
|
+
# user.permit(newsletter_preferences: [])
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
class ParameterSanitizer
|
38
|
+
DEFAULT_PERMITTED_ATTRIBUTES = {
|
39
|
+
sign_in: [:password, :remember_me],
|
40
|
+
sign_up: [:password, :password_confirmation],
|
41
|
+
account_update: [:password, :password_confirmation, :current_password]
|
42
|
+
}
|
43
|
+
|
44
|
+
def initialize(resource_class, resource_name, params)
|
45
|
+
@auth_keys = extract_auth_keys(resource_class)
|
46
|
+
@params = params
|
47
|
+
@resource_name = resource_name
|
48
|
+
@permitted = {}
|
49
|
+
|
50
|
+
DEFAULT_PERMITTED_ATTRIBUTES.each_pair do |action, keys|
|
51
|
+
permit(action, keys: keys)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Sanitize the parameters for a specific +action+.
|
56
|
+
#
|
57
|
+
# === Arguments
|
58
|
+
#
|
59
|
+
# * +action+ - A +Symbol+ with the action that the controller is
|
60
|
+
# performing, like +sign_up+, +sign_in+, etc.
|
61
|
+
#
|
62
|
+
# === Examples
|
63
|
+
#
|
64
|
+
# # Inside the `RegistrationsController#create` action.
|
65
|
+
# resource = build_resource(kingsman_parameter_sanitizer.sanitize(:sign_up))
|
66
|
+
# resource.save
|
67
|
+
#
|
68
|
+
# Returns an +ActiveSupport::HashWithIndifferentAccess+ with the permitted
|
69
|
+
# attributes.
|
70
|
+
def sanitize(action)
|
71
|
+
permissions = @permitted[action]
|
72
|
+
|
73
|
+
if permissions.respond_to?(:call)
|
74
|
+
cast_to_hash permissions.call(default_params)
|
75
|
+
elsif permissions.present?
|
76
|
+
cast_to_hash permit_keys(default_params, permissions)
|
77
|
+
else
|
78
|
+
unknown_action!(action)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Add or remove new parameters to the permitted list of an +action+.
|
83
|
+
#
|
84
|
+
# === Arguments
|
85
|
+
#
|
86
|
+
# * +action+ - A +Symbol+ with the action that the controller is
|
87
|
+
# performing, like +sign_up+, +sign_in+, etc.
|
88
|
+
# * +keys:+ - An +Array+ of keys that also should be permitted.
|
89
|
+
# * +except:+ - An +Array+ of keys that shouldn't be permitted.
|
90
|
+
# * +block+ - A block that should be used to permit the action
|
91
|
+
# parameters instead of the +Array+ based approach. The block will be
|
92
|
+
# called with an +ActionController::Parameters+ instance.
|
93
|
+
#
|
94
|
+
# === Examples
|
95
|
+
#
|
96
|
+
# # Adding new parameters to be permitted in the `sign_up` action.
|
97
|
+
# kingsman_parameter_sanitizer.permit(:sign_up, keys: [:subscribe_newsletter])
|
98
|
+
#
|
99
|
+
# # Removing the `password` parameter from the `account_update` action.
|
100
|
+
# kingsman_parameter_sanitizer.permit(:account_update, except: [:password])
|
101
|
+
#
|
102
|
+
# # Using the block form to completely override how we permit the
|
103
|
+
# # parameters for the `sign_up` action.
|
104
|
+
# kingsman_parameter_sanitizer.permit(:sign_up) do |user|
|
105
|
+
# user.permit(:email, :password, :password_confirmation)
|
106
|
+
# end
|
107
|
+
#
|
108
|
+
#
|
109
|
+
# Returns nothing.
|
110
|
+
def permit(action, keys: nil, except: nil, &block)
|
111
|
+
if block_given?
|
112
|
+
@permitted[action] = block
|
113
|
+
end
|
114
|
+
|
115
|
+
if keys.present?
|
116
|
+
@permitted[action] ||= @auth_keys.dup
|
117
|
+
@permitted[action].concat(keys)
|
118
|
+
end
|
119
|
+
|
120
|
+
if except.present?
|
121
|
+
@permitted[action] ||= @auth_keys.dup
|
122
|
+
@permitted[action] = @permitted[action] - except
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
private
|
127
|
+
|
128
|
+
# Cast a sanitized +ActionController::Parameters+ to a +HashWithIndifferentAccess+
|
129
|
+
# that can be used elsewhere.
|
130
|
+
#
|
131
|
+
# Returns an +ActiveSupport::HashWithIndifferentAccess+.
|
132
|
+
def cast_to_hash(params)
|
133
|
+
# TODO: Remove the `with_indifferent_access` method call when we only support Rails 5+.
|
134
|
+
params && params.to_h.with_indifferent_access
|
135
|
+
end
|
136
|
+
|
137
|
+
def default_params
|
138
|
+
if hashable_resource_params?
|
139
|
+
@params.fetch(@resource_name)
|
140
|
+
else
|
141
|
+
empty_params
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def hashable_resource_params?
|
146
|
+
@params[@resource_name].respond_to?(:permit)
|
147
|
+
end
|
148
|
+
|
149
|
+
def empty_params
|
150
|
+
ActionController::Parameters.new({})
|
151
|
+
end
|
152
|
+
|
153
|
+
def permit_keys(parameters, keys)
|
154
|
+
parameters.permit(*keys)
|
155
|
+
end
|
156
|
+
|
157
|
+
def extract_auth_keys(klass)
|
158
|
+
auth_keys = klass.authentication_keys
|
159
|
+
|
160
|
+
auth_keys.respond_to?(:keys) ? auth_keys.keys : auth_keys
|
161
|
+
end
|
162
|
+
|
163
|
+
def unknown_action!(action)
|
164
|
+
raise NotImplementedError, <<-MESSAGE.strip_heredoc
|
165
|
+
"Kingsman doesn't know how to sanitize parameters for '#{action}'".
|
166
|
+
If you want to define a new set of parameters to be sanitized use the
|
167
|
+
`permit` method first:
|
168
|
+
|
169
|
+
kingsman_parameter_sanitizer.permit(:#{action}, keys: [:param1, :param2, :param3])
|
170
|
+
MESSAGE
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kingsman
|
4
|
+
class SecretKeyFinder
|
5
|
+
def initialize(application)
|
6
|
+
@application = application
|
7
|
+
end
|
8
|
+
|
9
|
+
def find
|
10
|
+
if @application.respond_to?(:credentials) && key_exists?(@application.credentials)
|
11
|
+
@application.credentials.secret_key_base
|
12
|
+
elsif @application.respond_to?(:secrets) && key_exists?(@application.secrets)
|
13
|
+
@application.secrets.secret_key_base
|
14
|
+
elsif @application.config.respond_to?(:secret_key_base) && key_exists?(@application.config)
|
15
|
+
@application.config.secret_key_base
|
16
|
+
elsif @application.respond_to?(:secret_key_base) && key_exists?(@application)
|
17
|
+
@application.secret_key_base
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def key_exists?(object)
|
24
|
+
object.secret_key_base.present?
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|