devise_challenge_questionable 2.0.0 → 3.3.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
- ---
2
- SHA512:
3
- data.tar.gz: 8695e654d2bb19c2cc3898c7c37ef3a1b50f4581b8d6984abffe64df691289de2ddbfbaf03454e21a371872c7226ca73fb336077b458bd4cf2d2f3e6bb9fe981
4
- metadata.gz: 6ac2620ad0657c87ae83e8e5c48cc13fd10064ddcdf93048aedbfd47137709f072008d501024c78172a78e01e6a47859d924824cb276b04e10fbb3af18b6d1f1
5
- SHA1:
6
- data.tar.gz: 3c16a6153e9482d879cc84b9c48fd6bd4e28bcd3
7
- metadata.gz: fdae3b18830c50d05005ebcd56efd230d81f12fb
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a77778f0523bd02065afa11b69367b3430cbb92a5359b8b8817d2723efabba4e
4
+ data.tar.gz: 6e91e372d2199dffaed5ade3eddf2d0a3e43eae70f0ba1ffe807915cafc217cb
5
+ SHA512:
6
+ metadata.gz: 24febe5a3b8ae5ff545e5f9a9498f3e0d6bad1e93e1176877e9436c5bc4326d37435c54296f31bf8f820a04fef3b306b8d23ef80d45caa37cda77548b4786542
7
+ data.tar.gz: 3ac583e0d4c08d5f3ab00f973a88a7b801649201444a386ae496d2b68c39ecb4dff32c526f23f2ba1b375e563bd5048816a5656537a42cb13821421b943b4876
data/.gitignore CHANGED
@@ -18,3 +18,4 @@ patches-*
18
18
  capybara-*.html
19
19
  dump.rdb
20
20
  *.ids
21
+ .idea/
data/README.md CHANGED
@@ -26,9 +26,9 @@ Once that's done, run:
26
26
  In order to add challenge questions to a model, run the command:
27
27
 
28
28
  bundle exec rails g devise_challenge_questionable MODEL
29
-
29
+
30
30
  bundle exec rails g devise_challenge_questionable:install
31
-
31
+
32
32
  bundle exec rails g devise_challenge_questionable:views users
33
33
 
34
34
  Where MODEL is your model name (e.g. User or Admin). This generator will add `:challenge_questionable` to your model
@@ -45,9 +45,9 @@ To manually enable challenge questions for the User model, you should add the fo
45
45
  ```ruby
46
46
  has_many :user_challenge_questions, :validate => true, :inverse_of => :user
47
47
  accepts_nested_attributes_for :user_challenge_questions, :allow_destroy => true
48
-
48
+
49
49
  devise :challenge_questionable
50
-
50
+
51
51
  attr_accessible :user_challenge_questions_attributes
52
52
  ```
53
53
 
@@ -68,9 +68,13 @@ You also need to add the `user_challenge_question.rb` Model.
68
68
  before_save :digest_challenge_answer
69
69
 
70
70
  def digest_challenge_answer
71
- write_attribute(:challenge_answer, Digest::MD5.hexdigest(self.challenge_answer.downcase)) unless self.challenge_answer.nil?
71
+ if ENV['PASSWORD_PEPPER']
72
+ write_attribute(:challenge_answer, ::BCrypt::Password.create(self.challenge_answer.downcase + ENV['PASSWORD_PEPPER'], :cost => Devise.stretches)) unless self.challenge_answer.nil?
73
+ else
74
+ write_attribute(:challenge_answer, ::BCrypt::Password.create(self.challenge_answer.downcase, :cost => Devise.stretches)) unless self.challenge_answer.nil?
75
+ end
72
76
  end
73
-
77
+
74
78
  private
75
79
  def challenge_question_uniqueness
76
80
  if self.challenge_question.present? && self.user.user_challenge_questions.select{|q| q.challenge_question == self.challenge_question}.count > 1
@@ -83,7 +87,7 @@ You also need to add the `user_challenge_question.rb` Model.
83
87
  errors.add(:challenge_answer, 'can only be used once')
84
88
  end
85
89
  end
86
-
90
+
87
91
  def challenge_answer_repeating
88
92
  if self.challenge_answer.present? && self.challenge_answer =~ /(.)\1{2,}/
89
93
  errors.add(:challenge_answer, 'can not have more then two repeating characters in a row')
@@ -132,7 +136,7 @@ By default challenge questions are enabled for each user, you can change it with
132
136
  def login_challenge_questions?(request)
133
137
  request.ip != '127.0.0.1'
134
138
  end
135
-
139
+
136
140
  def set_challenge_questions?(request)
137
141
  request.ip != '127.0.0.1'
138
142
  end
@@ -1,43 +1,43 @@
1
1
  class Devise::ChallengeQuestionsController < DeviseController
2
2
 
3
- prepend_before_filter :require_no_authentication, :only => [ :new, :create, :edit, :update, :max_challenge_question_attempts_reached ]
4
- prepend_before_filter :authenticate_scope!, :only => [:show, :authenticate, :manage, :forgot]
5
- before_filter :prepare_and_validate, :handle_challenge_questions, :only => [:show, :authenticate]
6
-
3
+ prepend_before_action :require_no_authentication, :only => [ :new, :create, :edit, :update, :max_challenge_question_attempts_reached ]
4
+ prepend_before_action :authenticate_scope!, :only => [:show, :authenticate, :manage, :forgot]
5
+ before_action :prepare_and_validate, :handle_challenge_questions, :only => [:show, :authenticate]
6
+
7
7
  # GET /resource/challenge_question/new
8
8
  def new
9
- build_resource
10
- render_with_scope :new
9
+ self.resource = resource_class.new
10
+ render :new
11
11
  end
12
-
12
+
13
13
  # POST /resource/challenge_question
14
14
  def create
15
15
  self.resource = resource_class.send_reset_challenge_questions_instructions(params[resource_name])
16
16
  if resource.errors.empty?
17
17
  set_flash_message :notice, :send_instructions
18
- redirect_to new_session_path(resource_name)
18
+ redirect_to home_path
19
19
  else
20
- render_with_scope :new
20
+ render :new
21
21
  end
22
22
  end
23
-
23
+
24
24
  # GET /resource/challenge_question/edit?reset_challenge_questions_token=abcdef
25
25
  def edit
26
26
  self.resource = resource_class.new
27
27
  resource.reset_challenge_questions_token = params[:reset_challenge_questions_token]
28
28
  Devise.number_of_challenge_questions_stored.times { resource.send("#{resource_name}_challenge_questions").build }
29
- render_with_scope :edit
29
+ render :edit
30
30
  end
31
-
31
+
32
32
  # PUT /resource/challenge_question
33
33
  def update
34
34
  self.resource = resource_class.reset_challenge_questions_by_token(params[resource_name])
35
35
 
36
36
  if resource.errors.empty?
37
37
  set_flash_message :notice, :updated_challenge_questions
38
- redirect_to :root
38
+ sign_in_and_redirect(resource)
39
39
  else
40
- render_with_scope :edit
40
+ render :edit
41
41
  end
42
42
  end
43
43
 
@@ -45,11 +45,11 @@ class Devise::ChallengeQuestionsController < DeviseController
45
45
  def show
46
46
  @challenge_questions = resource.send("#{resource_name}_challenge_questions").sample(params[:limit].try(:to_i) || Devise.number_of_challenge_questions_asked)
47
47
  respond_to do |format|
48
- format.html { render_with_scope :show }
48
+ format.html { render :show }
49
49
  format.json { render :json => @challenge_questions.as_json(:only => [:id, :challenge_question]) }
50
50
  end
51
51
  end
52
-
52
+
53
53
  def authenticate
54
54
  build_challenge_questions
55
55
  if @challenge_questions.present? && challenge_questions_authenticated?
@@ -61,21 +61,21 @@ class Devise::ChallengeQuestionsController < DeviseController
61
61
  else
62
62
  failed_attempts
63
63
  end
64
- end
64
+ end
65
65
  end
66
-
66
+
67
67
  # Build token and redirect
68
68
  def manage
69
69
  resource.set_reset_challenge_questions_token
70
70
  sign_out(resource)
71
- redirect_to edit_challenge_question_path(resource, :reset_challenge_questions_token => resource.reset_challenge_questions_token)
71
+ redirect_to edit_user_challenge_question_path(resource, :reset_challenge_questions_token => resource.reset_challenge_questions_token)
72
72
  end
73
-
73
+
74
74
  def forgot
75
75
  sign_out(resource)
76
- redirect_to new_challenge_question_path(resource_name)
76
+ redirect_to new_user_challenge_question_path(resource_name)
77
77
  end
78
-
78
+
79
79
  def max_challenge_question_attempts_reached
80
80
  end
81
81
 
@@ -90,21 +90,28 @@ class Devise::ChallengeQuestionsController < DeviseController
90
90
  @limit = resource.class.max_challenge_question_attempts
91
91
  if resource.max_challenge_question_attempts?
92
92
  sign_out(resource)
93
- render_with_scope :max_challenge_question_attempts_reached and return
93
+ render :max_challenge_question_attempts_reached and return
94
94
  end
95
95
  end
96
-
96
+
97
97
  def challenge_questions_authenticated?
98
- @challenge_questions.all?{|question| Digest::MD5.hexdigest(question[:challenge_answer].try(:downcase).to_s).eql?(question[:answer])}
98
+ @challenge_questions.all? do |question|
99
+ @user_hash = ::BCrypt::Password.new(question[:answer])
100
+ if ENV['PASSWORD_PEPPER']
101
+ @user_hash.is_password?(question[:challenge_answer].try(:downcase) + ENV['PASSWORD_PEPPER'])
102
+ else
103
+ @user_hash.is_password?(question[:challenge_answer].try(:downcase))
104
+ end
105
+ end
99
106
  end
100
-
107
+
101
108
  def build_challenge_questions
102
109
  respond_to do |format|
103
110
  format.html { @challenge_questions = (params[:challenge_questions] || []).map{|cq, params| params.merge(:answer => resource.send("#{resource_name}_challenge_questions").find(params[:id]).challenge_answer)} }
104
111
  format.json { @challenge_questions = (params[:challenge_questions] || []).map{|cq| cq.merge(:answer => resource.send("#{resource_name}_challenge_questions").find(cq[:id]).challenge_answer)} }
105
112
  end
106
113
  end
107
-
114
+
108
115
  def challenge_questions_authenticated
109
116
  respond_to do |format|
110
117
  format.html do
@@ -115,25 +122,25 @@ class Devise::ChallengeQuestionsController < DeviseController
115
122
  end
116
123
  resource.update_attribute(:challenge_question_failed_attempts, 0)
117
124
  end
118
-
125
+
119
126
  def set_failed_attempt
120
127
  resource.challenge_question_failed_attempts += 1
121
128
  resource.save
122
129
  end
123
-
130
+
124
131
  def max_failed_attempts
125
132
  resource.max_challenge_question_lock_account
126
133
  sign_out(resource)
127
134
  respond_to do |format|
128
- format.html { render_with_scope :max_challenge_question_attempts_reached and return }
135
+ format.html { render :max_challenge_question_attempts_reached and return }
129
136
  format.json { render :json => {:max_attempts_reached => true, :errors => I18n.t(:"#{resource_name}.#{:max_attempts_reached}", :resource_name => resource_name, :scope => [:devise, controller_name.to_sym], :default => :attempt_failed)} }
130
137
  end
131
138
  end
132
-
139
+
133
140
  def failed_attempts
134
141
  respond_to do |format|
135
142
  format.html { redirect_to send("#{resource_name}_challenge_question_path" ) }
136
143
  format.json { render :json => {:errors => I18n.t(:"#{resource_name}.#{:attempt_failed}", :resource_name => resource_name, :scope => [:devise, controller_name.to_sym], :default => :attempt_failed)} }
137
144
  end
138
145
  end
139
- end
146
+ end
@@ -7,7 +7,7 @@
7
7
  <%= devise_error_messages! %>
8
8
  <%= f.hidden_field :id %>
9
9
  <%= f.hidden_field :reset_challenge_questions_token %>
10
-
10
+
11
11
  <%= f.fields_for :"#{resource_name}_challenge_questions", resource.send("#{resource_name}_challenge_questions") do |cq| %>
12
12
  <% if cq.object.new_record? %>
13
13
  <p>
@@ -40,25 +40,10 @@
40
40
 
41
41
  <script type="text/javascript" language="javascript">
42
42
  if (window.jQuery) {
43
- jQuery(document).ready(function() {
44
- $("select").change(function () {
45
- var $this = $(this);
46
- var prevVal = $this.data("prev");
47
- var otherSelects = $("select").not(this);
48
- otherSelects.find('option:contains('+ $(this).val() +')').attr('disabled', 'disabled');
49
- if (prevVal) {
50
- otherSelects.find('option:contains('+ prevVal +')').removeAttr('disabled');
51
- }
52
- $this.data("prev", $this.val());
53
- });
54
-
55
- $("select").each(function () {
56
- var $this = $(this);
57
- var prevVal = $this.data("prev");
58
- var otherSelects = $("select").not(this);
59
- otherSelects.find('option:contains('+ $(this).val() +')').attr('disabled', 'disabled');
60
- $this.data("prev", $this.val());
61
- });
62
- });
43
+ $('select').on('change', function(e) {
44
+ var $this = $(this);
45
+ var otherSelects = $("select").not(this);
46
+ otherSelects.find('option:contains('+ $(this).val() +')').attr('disabled','disabled')
47
+ })
63
48
  }
64
- </script>
49
+ </script>
@@ -12,7 +12,7 @@ Gem::Specification.new do |s|
12
12
  s.description = <<-EOF
13
13
  ### Features ###
14
14
  * configure max challenge question attempts
15
- * per user level control if he really need challenge questions
15
+ * per user level control if they really need challenge questions
16
16
  EOF
17
17
 
18
18
  s.rubyforge_project = "devise_challenge_questionable"
@@ -22,8 +22,8 @@ Gem::Specification.new do |s|
22
22
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
23
23
  s.require_paths = ["lib"]
24
24
 
25
- s.add_runtime_dependency 'rails', '>= 3.0.20'
26
- s.add_runtime_dependency 'devise', '>= 2.0.0.rc'
25
+ s.add_runtime_dependency 'rails', '>= 5.0.0'
26
+ s.add_runtime_dependency 'devise', '>= 4.0.0'
27
27
 
28
28
  s.add_development_dependency 'bundler'
29
29
  end
@@ -4,7 +4,7 @@ module DeviseChallengeQuestionable
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  included do
7
- before_filter :handle_challenge_questions
7
+ before_action :handle_challenge_questions
8
8
  end
9
9
 
10
10
  private
@@ -51,4 +51,4 @@ module DeviseChallengeQuestionable
51
51
 
52
52
  end
53
53
  end
54
- end
54
+ end
@@ -2,7 +2,7 @@ module DeviseChallengeQuestionable
2
2
  module Mailer
3
3
  # Deliver reset challenge question instructions when is requested
4
4
  def reset_challenge_questions_instructions(record)
5
- setup_mail(record, :reset_challenge_questions_instructions)
5
+ devise_mail(record, :reset_challenge_questions_instructions)
6
6
  end
7
7
  end
8
- end
8
+ end
@@ -3,12 +3,12 @@ module Devise
3
3
  module Models
4
4
  module ChallengeQuestionable
5
5
  extend ActiveSupport::Concern
6
-
6
+
7
7
  # Update challenge questions saving the record and clearing token. Returns true if
8
8
  # the challenge questions are valid and the record was saved, false otherwise.
9
9
  def reset_challenge_questions!(attributes)
10
10
  self.send("#{self.class.name.underscore}_challenge_questions").destroy_all
11
- self.attributes = {"#{self.class.name.underscore}_challenge_questions_attributes" => attributes["#{self.class.name.underscore}_challenge_questions_attributes"]}
11
+ self.attributes = {"#{self.class.name.underscore}_challenge_questions_attributes" => attributes["#{self.class.name.underscore}_challenge_questions_attributes"]}
12
12
  clear_reset_challenge_questions_token if self.valid?
13
13
  self.save
14
14
  end
@@ -16,11 +16,11 @@ module Devise
16
16
  def login_challenge_questions?(request)
17
17
  true
18
18
  end
19
-
19
+
20
20
  def set_challenge_questions?(request)
21
21
  true
22
22
  end
23
-
23
+
24
24
  def max_challenge_question_lock_account
25
25
  self.lock_access! if self.class.lock_strategy_enabled?(:failed_attempts)
26
26
  end
@@ -28,18 +28,18 @@ module Devise
28
28
  def max_challenge_question_attempts?
29
29
  challenge_question_failed_attempts >= self.class.max_challenge_question_attempts
30
30
  end
31
-
31
+
32
32
  # Resets reset challenge question token and send reset challenge question instructions by email
33
33
  def send_reset_challenge_questions_instructions
34
34
  generate_reset_challenge_questions_token!
35
35
  ::Devise.mailer.reset_challenge_questions_instructions(self).deliver
36
36
  end
37
-
37
+
38
38
  # Generates a new random token for reset challenge_question
39
39
  def set_reset_challenge_questions_token
40
40
  generate_reset_challenge_questions_token!
41
41
  end
42
-
42
+
43
43
  def unlock_access_including_challenge_questions!
44
44
  if_access_locked do
45
45
  self.locked_at = nil
@@ -49,7 +49,7 @@ module Devise
49
49
  save(:validate => false)
50
50
  end
51
51
  end
52
-
52
+
53
53
  protected
54
54
 
55
55
  # Generates a new random token for reset challenge_question
@@ -67,14 +67,14 @@ module Devise
67
67
  def clear_reset_challenge_questions_token
68
68
  self.reset_challenge_questions_token = nil
69
69
  end
70
-
70
+
71
71
  def clear_challenge_question_failed_attempts
72
72
  self.challenge_question_failed_attempts = 0
73
73
  end
74
-
74
+
75
75
  module ClassMethods
76
76
  ::Devise::Models.config(self, :max_challenge_question_attempts)
77
-
77
+
78
78
  # Attempt to find a user by it's email. If a record is found, send new
79
79
  # challenge_question instructions to it. If not user is found, returns a new user
80
80
  # with an email not found error.
@@ -87,7 +87,10 @@ module Devise
87
87
 
88
88
  # Generate a token checking if one does not already exist in the database.
89
89
  def reset_challenge_questions_token
90
- generate_token(:reset_challenge_questions_token)
90
+ loop do
91
+ token = Devise.friendly_token
92
+ break token unless User.find_by(:reset_challenge_questions_token => token)
93
+ end
91
94
  end
92
95
 
93
96
  # Attempt to find a user by it's reset_challenge_questions_token to reset it's
@@ -103,4 +106,4 @@ module Devise
103
106
  end
104
107
  end
105
108
  end
106
- end
109
+ end
@@ -1,3 +1,3 @@
1
1
  module DeviseChallengeQuestionable
2
- VERSION = "2.0.0"
2
+ VERSION = "3.3.2"
3
3
  end
@@ -1,14 +1,18 @@
1
1
  class <%= class_name %>ChallengeQuestion < ActiveRecord::Base
2
-
2
+
3
3
  belongs_to :<%= class_name.underscore %>
4
-
4
+
5
5
  validates :challenge_question, :uniqueness => {:scope => :<%= class_name.underscore %>_id}
6
6
  validates :challenge_answer, :presence => true
7
-
7
+
8
8
  before_save :digest_challenge_answer
9
-
9
+
10
10
  def digest_challenge_answer
11
- write_attribute(:challenge_answer, Digest::MD5.hexdigest(self.challenge_answer)) unless self.challenge_answer.nil?
11
+ if ENV['PASSWORD_PEPPER']
12
+ write_attribute(:challenge_answer, ::BCrypt::Password.create(self.challenge_answer.downcase + ENV['PASSWORD_PEPPER'], :cost => Devise.stretches)) unless self.challenge_answer.nil?
13
+ else
14
+ write_attribute(:challenge_answer, ::BCrypt::Password.create(self.challenge_answer.downcase, :cost => Devise.stretches)) unless self.challenge_answer.nil?
15
+ end
12
16
  end
13
-
14
- end
17
+
18
+ end
metadata CHANGED
@@ -1,58 +1,68 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: devise_challenge_questionable
3
- version: !ruby/object:Gem::Version
4
- version: 2.0.0
3
+ version: !ruby/object:Gem::Version
4
+ version: 3.3.2
5
5
  platform: ruby
6
- authors:
6
+ authors:
7
7
  - Andrew Kennedy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
-
12
- date: 2016-05-16 00:00:00 Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
11
+ date: 2020-05-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
15
14
  name: rails
16
- prerelease: false
17
- requirement: &id001 !ruby/object:Gem::Requirement
18
- requirements:
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
19
17
  - - ">="
20
- - !ruby/object:Gem::Version
21
- version: 3.0.20
18
+ - !ruby/object:Gem::Version
19
+ version: 5.0.0
22
20
  type: :runtime
23
- version_requirements: *id001
24
- - !ruby/object:Gem::Dependency
25
- name: devise
26
21
  prerelease: false
27
- requirement: &id002 !ruby/object:Gem::Requirement
28
- requirements:
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
29
24
  - - ">="
30
- - !ruby/object:Gem::Version
31
- version: 2.0.0.rc
25
+ - !ruby/object:Gem::Version
26
+ version: 5.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: devise
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 4.0.0
32
34
  type: :runtime
33
- version_requirements: *id002
34
- - !ruby/object:Gem::Dependency
35
- name: bundler
36
35
  prerelease: false
37
- requirement: &id003 !ruby/object:Gem::Requirement
38
- requirements:
39
- - &id004
40
- - ">="
41
- - !ruby/object:Gem::Version
42
- version: "0"
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 4.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
43
48
  type: :development
44
- version_requirements: *id003
45
- description: " ### Features ###\n * configure max challenge question attempts\n * per user level control if he really need challenge questions\n"
46
- email:
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: |2
56
+ ### Features ###
57
+ * configure max challenge question attempts
58
+ * per user level control if they really need challenge questions
59
+ email:
47
60
  - andrew@akennedy.io
48
61
  executables: []
49
-
50
62
  extensions: []
51
-
52
63
  extra_rdoc_files: []
53
-
54
- files:
55
- - .gitignore
64
+ files:
65
+ - ".gitignore"
56
66
  - Gemfile
57
67
  - README.md
58
68
  - Rakefile
@@ -82,26 +92,25 @@ files:
82
92
  - lib/generators/devise_challenge_questionable/views_generator.rb
83
93
  homepage: https://github.com/akennedy/devise_challenge_questionable
84
94
  licenses: []
85
-
86
95
  metadata: {}
87
-
88
96
  post_install_message:
89
97
  rdoc_options: []
90
-
91
- require_paths:
98
+ require_paths:
92
99
  - lib
93
- required_ruby_version: !ruby/object:Gem::Requirement
94
- requirements:
95
- - *id004
96
- required_rubygems_version: !ruby/object:Gem::Requirement
97
- requirements:
98
- - *id004
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
99
110
  requirements: []
100
-
101
111
  rubyforge_project: devise_challenge_questionable
102
- rubygems_version: 2.0.15
112
+ rubygems_version: 2.7.8
103
113
  signing_key:
104
114
  specification_version: 4
105
115
  summary: Challenge question plugin for devise
106
116
  test_files: []
107
-