From 9760a8bef8620266a81836aa346af08190889c4d Mon Sep 17 00:00:00 2001 From: Alexander Merkulov Date: Sat, 6 Feb 2016 00:14:32 +0300 Subject: [PATCH] Extract callbacks. Add setting for exctracted callbacks. Load it by default. Fix generator template. Add line to readme. --- .gitignore | 1 + README.md | 3 +- app/models/devise_token_auth/concerns/user.rb | 23 ++------------- .../concerns/user_omniauth_callbacks.rb | 28 +++++++++++++++++++ lib/devise_token_auth/engine.rb | 4 ++- .../templates/devise_token_auth.rb | 4 +++ 6 files changed, 41 insertions(+), 22 deletions(-) create mode 100644 app/models/devise_token_auth/concerns/user_omniauth_callbacks.rb diff --git a/.gitignore b/.gitignore index b4390ac67..a6f6298cf 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ test/dummy/.sass-cache test/dummy/config/application.yml coverage .idea +.irb_history .ruby-version diff --git a/README.md b/README.md index 0f64e710c..9a4c251ac 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,7 @@ The following settings are available for configuration in `config/initializers/d | **`redirect_whitelist`** | `nil` | As an added security measure, you can limit the URLs to which the API will redirect after email token validation (password reset, email confirmation, etc.). This value should be an array containing exact matches to the client URLs to be visited after validation. | | **`enable_standard_devise_support`** | `false` | By default, only Bearer Token authentication is implemented out of the box. If, however, you wish to integrate with legacy Devise authentication, you can do so by enabling this flag. NOTE: This feature is highly experimental! | | **`remove_tokens_after_password_reset`** | `false` | By default, old tokens are not invalidated when password is changed. Enable this option if you want to make passwords updates to logout other devices. | +| **`default_callbacks`** | `true` | By default User model will include the `DeviseTokenAuth::Concerns::UserOmniauthCallbacks` concern, which has `email`, `uid` validations & `uid` synchronization callbacks. | Additionally, you can configure other aspects of devise by manually creating the traditional devise.rb file at `config/initializers/devise.rb`. Here are some examples of what you can do in this file: @@ -356,7 +357,7 @@ The authentication routes must be mounted to your project. This gem includes a r mount_devise_token_auth_for 'User', at: 'auth' ~~~ -Any model class can be used, but the class will need to include [`DeviseTokenAuth::Concerns::User`](#model-concerns) for authentication to work properly. +Any model class can be used, but the class will need to include [`DeviseTokenAuth::Concerns::User`](#model-concerns) for authentication to work properly and [`DeviseTokenAuth::Concerns::UserOmniauthCallbacks`](#model-concerns) to use default omniauth callbacks & validations. You can mount this engine to any route that you like. `/auth` is used by default to conform with the defaults of the [ng-token-auth](https://github.com/lynndylanhurley/ng-token-auth) module and the [jToker](https://github.com/lynndylanhurley/j-toker) plugin. diff --git a/app/models/devise_token_auth/concerns/user.rb b/app/models/devise_token_auth/concerns/user.rb index 3dcc5a02b..216a9e5e0 100644 --- a/app/models/devise_token_auth/concerns/user.rb +++ b/app/models/devise_token_auth/concerns/user.rb @@ -27,20 +27,14 @@ def self.tokens_match?(token_hash, token) serialize :tokens, JSON end - validates :email, presence: true, email: true, if: Proc.new { |u| u.provider == 'email' } - validates_presence_of :uid, if: Proc.new { |u| u.provider != 'email' } - - # only validate unique emails among email registration users - validate :unique_email_user, on: :create + if DeviseTokenAuth.default_callbacks + include DeviseTokenAuth::Concerns::UserOmniauthCallbacks + end # can't set default on text fields in mysql, simulate here instead. after_save :set_empty_token_hash after_initialize :set_empty_token_hash - # keep uid in sync with email - before_save :sync_uid - before_create :sync_uid - # get rid of dead tokens before_save :destroy_expired_tokens @@ -239,21 +233,10 @@ def token_validation_response protected - # only validate unique email among users that registered by email - def unique_email_user - if provider == 'email' and self.class.where(provider: 'email', email: email).count > 0 - errors.add(:email, I18n.t("errors.messages.already_in_use")) - end - end - def set_empty_token_hash self.tokens ||= {} if has_attribute?(:tokens) end - def sync_uid - self.uid = email if provider == 'email' - end - def destroy_expired_tokens if self.tokens self.tokens.delete_if do |cid, v| diff --git a/app/models/devise_token_auth/concerns/user_omniauth_callbacks.rb b/app/models/devise_token_auth/concerns/user_omniauth_callbacks.rb new file mode 100644 index 000000000..ac500861d --- /dev/null +++ b/app/models/devise_token_auth/concerns/user_omniauth_callbacks.rb @@ -0,0 +1,28 @@ +module DeviseTokenAuth::Concerns::UserOmniauthCallbacks + extend ActiveSupport::Concern + + included do + validates :email, presence: true, email: true, if: Proc.new { |u| u.provider == 'email' } + validates_presence_of :uid, if: Proc.new { |u| u.provider != 'email' } + + # only validate unique emails among email registration users + validate :unique_email_user, on: :create + + # keep uid in sync with email + before_save :sync_uid + before_create :sync_uid + end + + protected + + # only validate unique email among users that registered by email + def unique_email_user + if provider == 'email' and self.class.where(provider: 'email', email: email).count > 0 + errors.add(:email, I18n.t("errors.messages.already_in_use")) + end + end + + def sync_uid + self.uid = email if provider == 'email' + end +end diff --git a/lib/devise_token_auth/engine.rb b/lib/devise_token_auth/engine.rb index fc1cbd855..c75240dfa 100644 --- a/lib/devise_token_auth/engine.rb +++ b/lib/devise_token_auth/engine.rb @@ -19,7 +19,8 @@ class Engine < ::Rails::Engine :redirect_whitelist, :check_current_password_before_update, :enable_standard_devise_support, - :remove_tokens_after_password_reset + :remove_tokens_after_password_reset, + :default_callbacks self.change_headers_on_each_request = true self.max_number_of_devices = 10 @@ -32,6 +33,7 @@ class Engine < ::Rails::Engine self.check_current_password_before_update = false self.enable_standard_devise_support = false self.remove_tokens_after_password_reset = false + self.default_callbacks = true def self.setup(&block) yield self diff --git a/lib/generators/devise_token_auth/templates/devise_token_auth.rb b/lib/generators/devise_token_auth/templates/devise_token_auth.rb index 768f13990..5ae1a23ad 100644 --- a/lib/generators/devise_token_auth/templates/devise_token_auth.rb +++ b/lib/generators/devise_token_auth/templates/devise_token_auth.rb @@ -30,6 +30,10 @@ # password is updated. # config.check_current_password_before_update = :attributes + # By default we will use callbacks for single omniauth. + # It depends on fields like email, provider and uid. + # config.default_callbacks = true + # By default, only Bearer Token authentication is implemented out of the box. # If, however, you wish to integrate with legacy Devise authentication, you can # do so by enabling this flag. NOTE: This feature is highly experimental!