Подготовить страницу sign_up с условием?
Я хочу задать вопрос пользователю и позволить ему зарегистрироваться только в том случае, если пользователь правильно ответит на мой вопрос. Я искал изобретать практические действия, но мой случай, похоже, не существует.
Есть ли идиоматический способ справиться с этой ситуацией?
Первой мыслью может быть использование javascript, но ответы хранятся в LDAP, и я ожидаю, что с ними будет легче справляться с рельсами.
Я также думал об отключении маршрута /users/sign_up
, вызывать действие (devise/registration#new
) вручную и отображать представление (devise/registration/new
).
Другим способом, который я могу придумать, является запуск фононного демона, который будет собирать идентификатор сеанса, где пользователь правильно ответил на вопросы. При правильном ответе пользователь будет перенаправлен на общедоступную страницу регистрации, которая будет проверять идентификатор сеанса пользователя с помощью демона.
Ответы
Ответ 1
Чтобы улучшить безопасность предыдущих предложений, лучший из них, похоже, является ключевым, но он небезопасен (независимо от того, шифруются ли файлы cookie или нет - см. мой комментарий к OP)
# app/controllers/preauth_controller.rb
def new
end
def create
if params[:answer] == 'correct answer'
# Create a secret value, the `token`, we will share it to the client
# through the `session` and store it on the server in `Rails.cache`
# (or you can use the database if you like)
#
# The fact that this token expires (by default) in 15 minutes is
# a bonus, it will secure the system against later use of a stolen
# cookie. The token is also secure against brute force attack
@token = SecureRandom.base64
session[:preauthorization_token] = @token
Rails.cache.write("users/preauthorization_tokens/#{@token}", "OK")
redirect_to sign_up_path
else
flash[:error] = 'Incorrect answer'
render :new
end
end
# app/controllers/users_controller.rb
before_filter :verify_preauth, only: [:new, :create]
def verify_preauth
# When we approve preauthorization we confirm that the
# `token` is known to the client, if the client knows the token
# let him sign up, else make him go away
token = session[:preauthorization_token]
redirect_to new_preauth_path unless token and Rails.cache.read("users/preauthorization_tokens/#{token}") == "OK"
end
Дополнительные вещи, чтобы делать/играть с....
- удалить успешно использованную запись
Rails.cache
при создании пользователя
- играйте с настройками
:expires_in
, если хотите, вы обычно хотите, чтобы это было как можно короче и до тех пор, пока это необходимо:), но рельсы по умолчанию 15 минут довольно хороши.
- есть более интересные способы обойти эту и подобные проблемы безопасности с помощью куки файлов, а именно вы можете создать объект
server_session
, который в основном совпадает с session
, но сохраняет данные в Rails.cache
со случайным сохраненным маркером в session
, используемом для доступа к записи в кеш, таким же образом, как и здесь.
- просто перейдите на сеансы на стороне сервера и не беспокойтесь о безопасности сеанса, но это означает более длительное время отклика из-за вашей поездки
Rails.cache
(redis, memcache, AR,...)
- вместо
OK
в значение кеша вы можете сохранить хэш значений, если вам нужно больше данных, безопасно сохраненных на хосте, для работы с этим
- ...
Ответ 2
Предполагая, что у вас есть данные cookie, подписанные (как указано в Rails 3), вы можете сделать так, как вы говорите, и использовать сеанс:
# app/controllers/preauth_controller.rb
def new
end
def create
if params[:answer] == 'correct answer'
session[:preauthorized] = true
redirect_to sign_up_path
end
flash[:error] = 'Incorrect answer'
render :new
end
# app/controllers/users_controller.rb
before_filter :verify_preauth, only: [:new, :create]
def verify_preauth
redirect_to new_preauth_path unless session[:preauthorized]
end
Если данные cookie не подписаны, ключ preauthorized
может быть изменен клиентом и, следовательно, ему не следует доверять.
При условии, что ваша страница зашифрована при передаче через HTTPS через TLS, и у вас нет уязвимостей XSS, это должно быть достаточно безопасным для ваших нужд. Если вы считаете, что это особенно чувствительный фрагмент кода, вы бы хотели больше, чем мыслить пользователем StackOverflow для руководства и внедрения комплексного подхода к защите вашего приложения.
Ответ 3
У меня немного другой подход.
- Покажите вопрос, получите ответ и подтвердите его.
- Установить зашифрованный сеанс.
-
Переопределите контроллер регистрации разработки таким образом, чтобы, даже если они сразу посетили URL-адрес и попытались зарегистрироваться, они не смогут
#app/controllers/registration_controller.rb
class RegistrationsController < Devise::RegistrationsController
before_filter :check_answered_or_not,only:[:create,:new]
def check_answered_or_not
if not session[:answered]==true
redirect_to question_path
end
end
private
def sign_up_params
params.require(:user).permit(:name,:phone,:password,:password_confirmation,:email)
end
def account_update_params
params.require(:user).permit(:name,:phone,:password,:password_confirmation,:email,:current_password)
end
end
мои 2cents
Ответ 4
Итак... Может быть, это должно быть в module Validatable
?
- Сгенерируйте контроллер Validatables с помощью Инструменты
- Настройте этот контроллер примерно так:
(Код этого модуля Вы можете увидеть This)
...
base.class_eval do
validates_presence_of :email, if: :email_required?
validates_uniqueness_of :email, allow_blank: true, if: :email_changed?
validates_format_of :email, with: email_regexp, allow_blank: true, if: :email_changed?
validates_presence_of :password, if: :password_required?
validates_confirmation_of :password, if: :password_required?
validates_length_of :password, within: password_length, allow_blank: true
validates_presence_of :question, if: :question_required?
validates_format_of :question, with: answered_regexp, if: :answered_changed?
end
end
...
def email_required?
true
end
def question_required?
true
end
Это не выполнено, но я надеюсь, что это поможет вам...
Ответ 5
Я думаю, что самый простой способ сделать это - изменить стандартный модуль разработки с пользовательским с before_action
в нем:
# routes.rb
devise_for :users, :controllers => {:registrations => "registrations"}
Со следующей реализацией контроллера:
# app/controllers/registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
before_action :check_answers, only: :new # :new action is responsible for :sign_up route
private
def check_answers
unless session[:gave_answers]
redirect_to ask_questions_path
false
end
end
end
И установив сеанс следующим образом:
# somewhere in questions controller:
if answers_correct?
session[:gave_answers] = true
redirect_to new_registration_path
end
Как только этот контроллер наследует от Devise::RegistrationsController
, все поведение остается по умолчанию, кроме проверки функциональности ответов.
Относительно вашего вопроса об идиоматическом способе - этот подход был описан в официальной документации . Ваше приложение - ваша логика, все в порядке.
UPDATE:
В комментариях @bbozo указал на некоторые проблемы безопасности с этим и другими ответами. Чтобы сделать его более безопасным, вы можете добавить время истечения срока действия и установить некоторый случайный секретный токен (дополнительная информация в комментариях).