devise_challenge_questionable 0.1.2 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -7
- data/README.md +22 -17
- data/app/controllers/devise/challenge_questions_controller.rb +61 -28
- data/app/views/devise/challenge_questions/edit.html.erb +1 -1
- data/app/views/devise/challenge_questions/show.html.erb +9 -6
- data/config/locales/en.yml +2 -1
- data/lib/devise_challenge_questionable/controllers/helpers.rb +17 -2
- data/lib/devise_challenge_questionable/controllers/url_helpers.rb +1 -1
- data/lib/devise_challenge_questionable/hooks/challenge_questions.rb +14 -4
- data/lib/devise_challenge_questionable/model.rb +5 -1
- data/lib/devise_challenge_questionable/routes.rb +1 -0
- data/lib/devise_challenge_questionable/version.rb +1 -1
- data/lib/devise_challenge_questionable.rb +4 -2
- data/lib/generators/devise_challenge_questionable/devise_challenge_questionable_generator.rb +3 -2
- data/lib/generators/devise_challenge_questionable/install_generator.rb +5 -2
- metadata +58 -50
- data/LICENSE +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
5
|
-
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 97b2602ce519f20e3226c3484585eaacda4bb334
|
4
|
+
data.tar.gz: d2456cee8d307d5786b4d1a63a61d9bddeef7081
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 63ff1a9fe2170091b9cdf43c65e8ebf86c64f318e6e88949312b1975b45d77f7d3b1e6150d5696c1741180081b9311a43cab1cbdd07be966307b4b71d9636806
|
7
|
+
data.tar.gz: adceea3b356dd2b0693f10c60d43127f178cb91141fecf18c3f754f9c39c83d0b08cf8caba5aa6446fb801170af2d759a2001f934662e6622ee26403f1fbdbd4
|
data/README.md
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
## Challenge questions plugin for Devise
|
2
2
|
|
3
|
-
This plugin forces a two step login process. After entering username and password, you must then answer a challenge question. Unlike security questions which are typically used when doing things like changing passwords, the challenge question must be answered each time logging in. You can set which users
|
3
|
+
This plugin forces a two step login process. After entering username/email and password, you must then answer a challenge question. Unlike security questions which are typically used when doing things like changing passwords, the challenge question must be answered each time logging in. You can set which users or admins need to answer a challenge question. We will assume your resource is User for the remainder of the Readme.
|
4
4
|
## Features
|
5
5
|
|
6
6
|
* configure max challenge question attempts
|
7
|
-
* configure number of challenge questions stored for each
|
8
|
-
*
|
7
|
+
* configure number of challenge questions stored for each user
|
8
|
+
* configure number of challenge questions asked for each user
|
9
|
+
* per user level control if he really need challenge question authentication
|
9
10
|
|
10
11
|
## Configuration
|
11
12
|
|
@@ -28,7 +29,7 @@ In order to add challenge questions to a model, run the command:
|
|
28
29
|
|
29
30
|
bundle exec rails g devise_challenge_questionable:install
|
30
31
|
|
31
|
-
bundle exec rails g devise_challenge_questionable:views
|
32
|
+
bundle exec rails g devise_challenge_questionable:views users
|
32
33
|
|
33
34
|
Where MODEL is your model name (e.g. User or Admin). This generator will add `:challenge_questionable` to your model
|
34
35
|
and create a migration in `db/migrate/`, which will add `:reset_challenge_questions_token` and `:challenge_question_failed_attempts` to your table.
|
@@ -39,22 +40,22 @@ Finally, run the migration with:
|
|
39
40
|
|
40
41
|
### Manual installation
|
41
42
|
|
42
|
-
To manually enable challenge questions for the
|
43
|
+
To manually enable challenge questions for the User model, you should add the following. Set up relationships. You should already have a devise line so you would just add :challenge_questionable to it. Also, you need to allow accessibility to `:user_challenge_questions_attributes`. Replace user with whatever resource you are using. Typically it would be user or admin.
|
43
44
|
|
44
45
|
```ruby
|
45
|
-
has_many :
|
46
|
-
accepts_nested_attributes_for :
|
46
|
+
has_many :user_challenge_questions, :validate => true, :inverse_of => :user
|
47
|
+
accepts_nested_attributes_for :user_challenge_questions, :allow_destroy => true
|
47
48
|
|
48
49
|
devise :challenge_questionable
|
49
50
|
|
50
|
-
attr_accessible :
|
51
|
+
attr_accessible :user_challenge_questions_attributes
|
51
52
|
```
|
52
53
|
|
53
|
-
You also need to add the `
|
54
|
+
You also need to add the `user_challenge_question.rb` Model.
|
54
55
|
|
55
56
|
```ruby
|
56
|
-
class
|
57
|
-
belongs_to :
|
57
|
+
class UserChallengeQuestion < ActiveRecord::Base
|
58
|
+
belongs_to :user
|
58
59
|
|
59
60
|
validates :challenge_question, :challenge_answer, :presence => true
|
60
61
|
validates :challenge_answer, :length => { :in => 4..56 }, :format => {:with => /^[\w\s:]*$/, :message => "can not contain special characters"}, :allow_blank => true
|
@@ -72,13 +73,13 @@ You also need to add the `{resource}_challenge_question.rb` Model.
|
|
72
73
|
|
73
74
|
private
|
74
75
|
def challenge_question_uniqueness
|
75
|
-
if self.challenge_question.present? && self.
|
76
|
+
if self.challenge_question.present? && self.user.user_challenge_questions.select{|q| q.challenge_question == self.challenge_question}.count > 1
|
76
77
|
errors.add(:challenge_question, 'can only be used once')
|
77
78
|
end
|
78
79
|
end
|
79
80
|
|
80
81
|
def challenge_answer_uniqueness
|
81
|
-
if self.challenge_answer.present? && self.
|
82
|
+
if self.challenge_answer.present? && self.user.user_challenge_questions.select{|q| q.challenge_answer == self.challenge_answer}.count > 1
|
82
83
|
errors.add(:challenge_answer, 'can only be used once')
|
83
84
|
end
|
84
85
|
end
|
@@ -115,7 +116,7 @@ Configuration settings
|
|
115
116
|
# Max challenge question attempts
|
116
117
|
config.max_challenge_question_attempts = 3
|
117
118
|
|
118
|
-
# Number of challenge questions to store for each
|
119
|
+
# Number of challenge questions to store for each user
|
119
120
|
config.number_of_challenge_questions = 3
|
120
121
|
|
121
122
|
# Default challenge question options
|
@@ -125,12 +126,16 @@ Configuration settings
|
|
125
126
|
|
126
127
|
### Customization
|
127
128
|
|
128
|
-
By default challenge questions are enabled for each
|
129
|
+
By default challenge questions are enabled for each user, you can change it with this method in your User model:
|
129
130
|
|
130
131
|
```ruby
|
131
|
-
def
|
132
|
+
def login_challenge_questions?(request)
|
133
|
+
request.ip != '127.0.0.1'
|
134
|
+
end
|
135
|
+
|
136
|
+
def set_challenge_questions?(request)
|
132
137
|
request.ip != '127.0.0.1'
|
133
138
|
end
|
134
139
|
```
|
135
140
|
|
136
|
-
this will disable challenge questions for local
|
141
|
+
this will disable challenge questions for local users
|
@@ -1,8 +1,8 @@
|
|
1
1
|
class Devise::ChallengeQuestionsController < ApplicationController
|
2
2
|
include Devise::Controllers::InternalHelpers
|
3
|
-
prepend_before_filter :require_no_authentication, :only => [ :new, :create, :edit, :update ]
|
3
|
+
prepend_before_filter :require_no_authentication, :only => [ :new, :create, :edit, :update, :max_challenge_question_attempts_reached ]
|
4
4
|
prepend_before_filter :authenticate_scope!, :only => [:show, :authenticate, :manage, :forgot]
|
5
|
-
before_filter :prepare_and_validate, :handle_challenge_questions, :only => [:show, :authenticate]
|
5
|
+
before_filter :prepare_and_validate, :handle_challenge_questions, :only => [:show, :authenticate]
|
6
6
|
|
7
7
|
# GET /resource/challenge_question/new
|
8
8
|
def new
|
@@ -13,7 +13,6 @@ class Devise::ChallengeQuestionsController < ApplicationController
|
|
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
|
-
|
17
16
|
if resource.errors.empty?
|
18
17
|
set_flash_message :notice, :send_instructions
|
19
18
|
redirect_to new_session_path(resource_name)
|
@@ -26,7 +25,7 @@ class Devise::ChallengeQuestionsController < ApplicationController
|
|
26
25
|
def edit
|
27
26
|
self.resource = resource_class.new
|
28
27
|
resource.reset_challenge_questions_token = params[:reset_challenge_questions_token]
|
29
|
-
Devise.
|
28
|
+
Devise.number_of_challenge_questions_stored.times { resource.send("#{resource_name}_challenge_questions").build }
|
30
29
|
render_with_scope :edit
|
31
30
|
end
|
32
31
|
|
@@ -44,37 +43,25 @@ class Devise::ChallengeQuestionsController < ApplicationController
|
|
44
43
|
|
45
44
|
# GET /resource/challenge_question
|
46
45
|
def show
|
47
|
-
@
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
redirect_to edit_challenge_question_path(resource, :reset_challenge_questions_token => resource.reset_challenge_questions_token)
|
52
|
-
else
|
53
|
-
render_with_scope :show
|
46
|
+
@challenge_questions = resource.send("#{resource_name}_challenge_questions").sample(params[:limit].try(:to_i) || Devise.number_of_challenge_questions_asked)
|
47
|
+
respond_to do |format|
|
48
|
+
format.html { render_with_scope :show }
|
49
|
+
format.json { render :json => @challenge_questions.as_json(:only => [:id, :challenge_question]) }
|
54
50
|
end
|
55
51
|
end
|
56
|
-
|
52
|
+
|
57
53
|
def authenticate
|
58
|
-
|
59
|
-
@
|
60
|
-
|
61
|
-
if md5.eql?(@challenge_question.challenge_answer)
|
62
|
-
warden.session(resource_name)[:need_challenge_questions] = false
|
63
|
-
sign_in resource_name, resource
|
64
|
-
redirect_to stored_location_for(resource_name) || :root
|
65
|
-
resource.update_attribute(:challenge_question_failed_attempts, 0)
|
54
|
+
build_challenge_questions
|
55
|
+
if @challenge_questions.present? && challenge_questions_authenticated?
|
56
|
+
challenge_questions_authenticated
|
66
57
|
else
|
67
|
-
|
68
|
-
resource.save
|
69
|
-
set_flash_message :notice, :attempt_failed
|
58
|
+
set_failed_attempt
|
70
59
|
if resource.max_challenge_question_attempts?
|
71
|
-
|
72
|
-
sign_out(resource)
|
73
|
-
render_with_scope :max_challenge_question_attempts_reached and return
|
60
|
+
max_failed_attempts
|
74
61
|
else
|
75
|
-
|
62
|
+
failed_attempts
|
76
63
|
end
|
77
|
-
end
|
64
|
+
end
|
78
65
|
end
|
79
66
|
|
80
67
|
# Build token and redirect
|
@@ -88,6 +75,9 @@ class Devise::ChallengeQuestionsController < ApplicationController
|
|
88
75
|
sign_out(resource)
|
89
76
|
redirect_to new_challenge_question_path(resource_name)
|
90
77
|
end
|
78
|
+
|
79
|
+
def max_challenge_question_attempts_reached
|
80
|
+
end
|
91
81
|
|
92
82
|
private
|
93
83
|
|
@@ -103,4 +93,47 @@ class Devise::ChallengeQuestionsController < ApplicationController
|
|
103
93
|
render_with_scope :max_challenge_question_attempts_reached and return
|
104
94
|
end
|
105
95
|
end
|
96
|
+
|
97
|
+
def challenge_questions_authenticated?
|
98
|
+
@challenge_questions.all?{|question| Digest::MD5.hexdigest(question[:challenge_answer].try(:downcase).to_s).eql?(question[:answer])}
|
99
|
+
end
|
100
|
+
|
101
|
+
def build_challenge_questions
|
102
|
+
respond_to do |format|
|
103
|
+
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
|
+
format.json { @challenge_questions = (params[:challenge_questions] || []).map{|cq| cq.merge(:answer => resource.send("#{resource_name}_challenge_questions").find(cq[:id]).challenge_answer)} }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def challenge_questions_authenticated
|
109
|
+
respond_to do |format|
|
110
|
+
format.html do
|
111
|
+
warden.session(resource_name)[:login_challenge_questions] = false
|
112
|
+
redirect_to stored_location_for(resource_name) || :root
|
113
|
+
end
|
114
|
+
format.json { render :json => {:success => true} }
|
115
|
+
end
|
116
|
+
resource.update_attribute(:challenge_question_failed_attempts, 0)
|
117
|
+
end
|
118
|
+
|
119
|
+
def set_failed_attempt
|
120
|
+
resource.challenge_question_failed_attempts += 1
|
121
|
+
resource.save
|
122
|
+
end
|
123
|
+
|
124
|
+
def max_failed_attempts
|
125
|
+
resource.max_challenge_question_lock_account
|
126
|
+
sign_out(resource)
|
127
|
+
respond_to do |format|
|
128
|
+
format.html { render_with_scope :max_challenge_question_attempts_reached and return }
|
129
|
+
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
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def failed_attempts
|
134
|
+
respond_to do |format|
|
135
|
+
format.html { redirect_to send("#{resource_name}_challenge_question_path" ) }
|
136
|
+
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
|
+
end
|
138
|
+
end
|
106
139
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
<p>Change your current challenge questions by filling out the form below. The questions you set up previously will be replaced with these new questions. All fields are required.</p>
|
3
3
|
|
4
4
|
<fieldset>
|
5
|
-
<h4>Please select <%= Devise.
|
5
|
+
<h4>Please select <%= Devise.number_of_challenge_questions_stored %> questions below, 1 from each drop down. Then type your answers in the field provided.</h4>
|
6
6
|
<%= form_for(resource, :as => resource_name, :url => challenge_question_path(resource_name), :html => { :method => :put }) do |f| %>
|
7
7
|
<%= devise_error_messages! %>
|
8
8
|
<%= f.hidden_field :id %>
|
@@ -2,12 +2,15 @@
|
|
2
2
|
|
3
3
|
<%= form_tag([:authenticate, resource_name, :challenge_question], :method => :put) do %>
|
4
4
|
<fieldset>
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
<%= hidden_field_tag :limit, params[:limit]%>
|
6
|
+
<%- @challenge_questions.each_with_index do |cq, index| %>
|
7
|
+
<p>
|
8
|
+
<%= hidden_field_tag "challenge_questions[#{cq.id}][id]", cq.id%>
|
9
|
+
<%= label_tag "challenge_questions[#{cq.id}][challenge_question]", cq.challenge_question %>
|
10
|
+
<br>
|
11
|
+
<%= password_field_tag "challenge_questions[#{cq.id}][challenge_answer]", nil, :placeholder => 'Answer', :class => 'wide', :style => 'font-size: 1.8em;padding:5px', :autofocus => (true if index == 0), :autocomplete => "off" %>
|
12
|
+
</p>
|
13
|
+
<% end %>
|
11
14
|
<p>
|
12
15
|
<%= submit_tag "Submit", :style => 'font-size:1.8em;'%>
|
13
16
|
or
|
data/config/locales/en.yml
CHANGED
@@ -7,7 +7,8 @@ en:
|
|
7
7
|
challenge_answer: "Answer"
|
8
8
|
devise:
|
9
9
|
challenge_questions:
|
10
|
-
attempt_failed: "Attempt failed."
|
10
|
+
attempt_failed: "Attempt failed. Please try again."
|
11
|
+
max_attempts_reached: "You have reached the maximum attempts."
|
11
12
|
updated_challenge_questions: "Successfully updated challenge questions."
|
12
13
|
send_instructions: "You will receive an email with instructions about how to reset your challenge questions in a few minutes."
|
13
14
|
|
@@ -12,12 +12,21 @@ module DeviseChallengeQuestionable
|
|
12
12
|
def handle_challenge_questions
|
13
13
|
unless devise_controller? && (controller_name != 'registrations' && ![:edit, :update, :destroy].include?(action_name))
|
14
14
|
Devise.mappings.keys.flatten.any? do |scope|
|
15
|
-
if signed_in?(scope)
|
16
|
-
|
15
|
+
if signed_in?(scope)
|
16
|
+
set_challenge_questions(scope) and return if warden.session(scope)[:set_challenge_questions]
|
17
|
+
failed_challenge_question(scope) and return if warden.session(scope)[:login_challenge_questions]
|
17
18
|
end
|
18
19
|
end
|
19
20
|
end
|
20
21
|
end
|
22
|
+
|
23
|
+
def set_challenge_questions(scope)
|
24
|
+
if request.format.present? and request.format.html?
|
25
|
+
redirect_to set_challenge_questions_path_for(scope)
|
26
|
+
else
|
27
|
+
render :nothing => true, :status => :unauthorized
|
28
|
+
end
|
29
|
+
end
|
21
30
|
|
22
31
|
def failed_challenge_question(scope)
|
23
32
|
if request.format.present? and request.format.html?
|
@@ -27,6 +36,12 @@ module DeviseChallengeQuestionable
|
|
27
36
|
render :nothing => true, :status => :unauthorized
|
28
37
|
end
|
29
38
|
end
|
39
|
+
|
40
|
+
def set_challenge_questions_path_for(resource_or_scope = nil)
|
41
|
+
scope = Devise::Mapping.find_scope!(resource_or_scope)
|
42
|
+
change_path = "manage_#{scope}_challenge_question_path"
|
43
|
+
send(change_path)
|
44
|
+
end
|
30
45
|
|
31
46
|
def challenge_questions_path_for(resource_or_scope = nil)
|
32
47
|
scope = Devise::Mapping.find_scope!(resource_or_scope)
|
@@ -2,7 +2,7 @@ module DeviseChallengeQuestionable
|
|
2
2
|
module Controllers
|
3
3
|
module UrlHelpers
|
4
4
|
[:path, :url].each do |path_or_url|
|
5
|
-
[nil, :edit_, :authenticate_, :manage_, :forgot_].each do |action|
|
5
|
+
[nil, :edit_, :authenticate_, :manage_, :forgot_, :max_challenge_question_attempts_reached_].each do |action|
|
6
6
|
class_eval <<-URL_HELPERS, __FILE__, __LINE__ + 1
|
7
7
|
def #{action}challenge_question_#{path_or_url}(resource, *args)
|
8
8
|
resource = case resource
|
@@ -1,8 +1,18 @@
|
|
1
1
|
Warden::Manager.after_authentication do |user, auth, options|
|
2
|
-
|
3
|
-
auth.session(options[:scope])[:need_challenge_questions] = user.need_challenge_questions?(auth.request)
|
4
|
-
end
|
2
|
+
|
5
3
|
if Devise.respond_to?(:token_authentication_key) && auth.env["action_dispatch.request.parameters"].keys.include?(Devise.token_authentication_key.to_s)
|
6
|
-
auth.session(options[:scope])[:
|
4
|
+
auth.session(options[:scope])[:login_challenge_questions] = false
|
5
|
+
auth.session(options[:scope])[:set_challenge_questions] = false
|
6
|
+
else
|
7
|
+
|
8
|
+
if user.respond_to?(:login_challenge_questions?)
|
9
|
+
auth.session(options[:scope])[:login_challenge_questions] = user.login_challenge_questions?(auth.request)
|
10
|
+
end
|
11
|
+
|
12
|
+
if user.respond_to?(:set_challenge_questions?)
|
13
|
+
auth.session(options[:scope])[:set_challenge_questions] = user.set_challenge_questions?(auth.request)
|
14
|
+
end
|
15
|
+
|
7
16
|
end
|
17
|
+
|
8
18
|
end
|
@@ -7,6 +7,7 @@ module ActionDispatch::Routing
|
|
7
7
|
put :authenticate, :path => mapping.path_names[:authenticate]
|
8
8
|
get :manage, :path => mapping.path_names[:manage]
|
9
9
|
get :forgot, :path => mapping.path_names[:forgot]
|
10
|
+
get :max_challenge_question_attempts_reached, :path => mapping.path_names[:max_challenge_question_attempts_reached]
|
10
11
|
end
|
11
12
|
end
|
12
13
|
end
|
@@ -10,9 +10,11 @@ module Devise
|
|
10
10
|
mattr_accessor :challenge_questions
|
11
11
|
@@challenge_questions = ["What was your high school mascot?","In which city was your first elementary school?","In which city was your mother born?","What is the name of your favorite movie?","Who is your favorite athlete?","What was your most memorable gift as a child?","What is your favorite cartoon character?","What is the name of your favorite novel?","Name of favorite childhood pet?","What is the name of your elementary school?","What is your youngest child's middle name?","Last Name of your kindergarten teacher?","What is the first name of your grandmother (your father's mother)?","What is your spouse's nickname?","Name of the place where your wedding reception was held?","Name of a college you applied to but did not attend?","What is the first name of the youngest of your siblings?","What is the first name of the eldest of your siblings?","What is your favorite television show?","If you needed a new first name, what would it be?","What is the first name of your youngest child?","When is your mother's birthday (MM/DD)?","What is your eldest child's middle name?","What is the last name of the funniest friend you know?","Name the highest mountain you've been to the top of?","What is the first name of your grandmother (your mother's mother)?","What is the first name of your grandfather (your mother's father)?","What was the first name of your best man/maid of honor?","What was the last name of your first grade teacher?","What is the last name of your first boyfriend or girlfriend?","Which high school did you attend?","What was your major during college?","What was the name of your first pet?","What was your favorite place from your childhood?","What is your favorite song?","What is your favorite car?","What is your mother’s middle name?","What is the (MM/DD) of your employment?","What is the make/model of first car?","What is the name of the city or town where you were born?","What is the name of your favorite childhood teacher?","What is the name of your favorite childhood friend?","What are your oldest sibling’s (MM/YYYY) of birth?","What is your oldest sibling’s middle name?","What school did you attend for sixth grade?","On what street did you live in third grade?"]
|
12
12
|
|
13
|
+
mattr_accessor :number_of_challenge_questions_stored
|
14
|
+
@@number_of_challenge_questions_stored = 3
|
13
15
|
|
14
|
-
mattr_accessor :
|
15
|
-
@@
|
16
|
+
mattr_accessor :number_of_challenge_questions_asked
|
17
|
+
@@number_of_challenge_questions_asked = 1
|
16
18
|
end
|
17
19
|
|
18
20
|
module DeviseChallengeQuestionable
|
data/lib/generators/devise_challenge_questionable/devise_challenge_questionable_generator.rb
CHANGED
@@ -29,7 +29,7 @@ CONTENT
|
|
29
29
|
belongs_to :#{class_name.underscore}
|
30
30
|
|
31
31
|
validates :challenge_question, :challenge_answer, :presence => true
|
32
|
-
validates :challenge_answer, :length => { :in => 3..56 }, :format => {:with => /^[\\w\\s
|
32
|
+
validates :challenge_answer, :length => { :in => 3..56 }, :format => {:with => /^[\\w\\s:\/]*$/, :message => "can not contain special characters"}, :allow_blank => true
|
33
33
|
|
34
34
|
# Must use custom validation since uniqueness scope will not work with has_many association
|
35
35
|
validate :challenge_question_uniqueness
|
@@ -79,7 +79,8 @@ CONTENT
|
|
79
79
|
inject_into_file path, :after => "devise:\n" do
|
80
80
|
<<-CONTENT
|
81
81
|
challenge_questions:
|
82
|
-
attempt_failed: "Attempt failed."
|
82
|
+
attempt_failed: "Attempt failed. Please try again."
|
83
|
+
max_attempts_reached: "You have reached the maximum attempts."
|
83
84
|
updated_challenge_questions: "Successfully updated challenge questions."
|
84
85
|
send_instructions: "You will receive an email with instructions about how to reset your challenge questions in a few minutes."
|
85
86
|
CONTENT
|
@@ -19,10 +19,13 @@ module DeviseChallengeQuestionable
|
|
19
19
|
config.max_challenge_question_attempts = 3
|
20
20
|
|
21
21
|
# Number of challenge questions to store for each user
|
22
|
-
config.
|
22
|
+
config.number_of_challenge_questions_stored = 3
|
23
|
+
|
24
|
+
# Number of challenge questions to ask for each user
|
25
|
+
config.number_of_challenge_questions_asked = 1
|
23
26
|
|
24
27
|
# Default challenge question options
|
25
|
-
config.challenge_questions = ["What was your high school mascot?","In which city was your first elementary school?","In which city was your mother born?","What is the name of your favorite movie?","Who is your favorite athlete?","What was your most memorable gift as a child?","What is your favorite cartoon character?","What is the name of your favorite novel?","Name of favorite childhood pet?","What is the name of your elementary school?","What is your youngest child's middle name?","
|
28
|
+
config.challenge_questions = ["What was your high school mascot?","In which city was your first elementary school?","In which city was your mother born?","What is the name of your favorite movie?","Who is your favorite athlete?","What was your most memorable gift as a child?","What is your favorite cartoon character?","What is the name of your favorite novel?","Name of favorite childhood pet?","What is the name of your elementary school?","What is your youngest child's middle name?","What is the last name of your kindergarten teacher?","What is the first name of your grandmother (your father's mother)?","What is your spouse's nickname?","What is the name of the place where your wedding reception was held?","What is the name of a college you applied to but did not attend?","What is the first name of the youngest of your siblings?","What is the first name of the eldest of your siblings?","What is your favorite television show?","If you needed a new first name, what would it be?","What is the first name of your youngest child?","When is your mother's birthday (MM/DD)?","What is your eldest child's middle name?","What is the last name of the funniest friend you know?","What is the name the highest mountain you've been to the top of?","What is the first name of your grandmother (your mother's mother)?","What is the first name of your grandfather (your mother's father)?","What was the first name of your best man/maid of honor?","What was the last name of your first grade teacher?","What is the last name of your first boyfriend or girlfriend?","Which high school did you attend?","What was your major during college?","What was the name of your first pet?","What was your favorite place from your childhood?","What is your favorite song?","What is your favorite car?","What is your mother’s middle name?","What is the (MM/DD) of your employment?","What is the make/model of first car?","What is the name of the city or town where you were born?","What is the name of your favorite childhood teacher?","What is the name of your favorite childhood friend?","What are your oldest sibling’s month and year (MM/YYYY) of birth?","What is your oldest sibling’s middle name?","What school did you attend for sixth grade?","On what street did you live in third grade?","What is the name of the street where you first lived?","How many full bathrooms are in your home?","What type of home do you live in?","Where was your wedding rehearsal dinner held?","In what dormitory did you live in college?","What is the first name of your oldest cousin?","What is the last name of your high school counselor?","What is the first name of your favorite aunt?","How many nieces / nephews does / did your mother have?","How many nieces / nephews does / did your father have?","What instrument did you play in school?","What is the name of your favorite college professor?","In how many states have you lived?","How many windows does your kitchen have?","How many televisions do you have in your home?","What is the brand name of your hair dryer?","What is your favorite lipstick color?","What is the name of your favorite reality show?","Who is the designer of your wedding dress?","What is your favorite shoe style?","What is your favorite flower?","What physical attribute do you most admire of your boyfriend / girlfriend / spouse?","What is the brand name of your vacuum?","What’s your favorite fitness class?","What is your favorite spa treatment?","What is the color of your first car?","What is your favorite dessert?","Who is your cell phone carrier?","What is the name of your favorite restaurant?"]
|
26
29
|
|
27
30
|
CONTENT
|
28
31
|
end
|
metadata
CHANGED
@@ -1,60 +1,69 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: devise_challenge_questionable
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.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
|
-
|
13
|
-
|
14
|
-
- !ruby/object:Gem::Dependency
|
11
|
+
date: 2014-02-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
15
14
|
name: rails
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
- !ruby/object:Gem::Version
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
21
19
|
version: 3.0.20
|
22
20
|
type: :runtime
|
23
|
-
version_requirements: *id001
|
24
|
-
- !ruby/object:Gem::Dependency
|
25
|
-
name: devise
|
26
21
|
prerelease: false
|
27
|
-
|
28
|
-
requirements:
|
29
|
-
- -
|
30
|
-
- !ruby/object:Gem::Version
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.0.20
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: devise
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '='
|
32
|
+
- !ruby/object:Gem::Version
|
31
33
|
version: 1.1.3
|
32
34
|
type: :runtime
|
33
|
-
version_requirements: *id002
|
34
|
-
- !ruby/object:Gem::Dependency
|
35
|
-
name: bundler
|
36
35
|
prerelease: false
|
37
|
-
|
38
|
-
requirements:
|
39
|
-
-
|
40
|
-
-
|
41
|
-
|
42
|
-
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.1.3
|
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
|
-
|
45
|
-
|
46
|
-
|
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 he 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:
|
64
|
+
files:
|
55
65
|
- .gitignore
|
56
66
|
- Gemfile
|
57
|
-
- LICENSE
|
58
67
|
- README.md
|
59
68
|
- Rakefile
|
60
69
|
- app/controllers/devise/challenge_questions_controller.rb
|
@@ -83,26 +92,25 @@ files:
|
|
83
92
|
- lib/generators/devise_challenge_questionable/views_generator.rb
|
84
93
|
homepage: https://github.com/akennedy/devise_challenge_questionable
|
85
94
|
licenses: []
|
86
|
-
|
87
95
|
metadata: {}
|
88
|
-
|
89
96
|
post_install_message:
|
90
97
|
rdoc_options: []
|
91
|
-
|
92
|
-
require_paths:
|
98
|
+
require_paths:
|
93
99
|
- lib
|
94
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
95
|
-
requirements:
|
96
|
-
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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'
|
100
110
|
requirements: []
|
101
|
-
|
102
111
|
rubyforge_project: devise_challenge_questionable
|
103
|
-
rubygems_version: 2.
|
112
|
+
rubygems_version: 2.1.10
|
104
113
|
signing_key:
|
105
114
|
specification_version: 4
|
106
115
|
summary: Challenge question plugin for devise
|
107
116
|
test_files: []
|
108
|
-
|
data/LICENSE
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
Copyright (C) 2012 Dmitrii Golub
|
2
|
-
|
3
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
4
|
-
this software and associated documentation files (the "Software"), to deal in
|
5
|
-
the Software without restriction, including without limitation the rights to
|
6
|
-
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
7
|
-
of the Software, and to permit persons to whom the Software is furnished to do
|
8
|
-
so, subject to the following conditions:
|
9
|
-
|
10
|
-
The above copyright notice and this permission notice shall be included in all
|
11
|
-
copies or substantial portions of the Software.
|
12
|
-
|
13
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
-
SOFTWARE.
|