devise_challenge_questionable 2.0.0 → 3.3.2

Sign up to get free protection for your applications and to get access to all the features.
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
-