devise_phone 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bcb01262a5e91e8368c10a98a3693a528c53abdb
4
+ data.tar.gz: 9e43d108a7ed7225c60375c27ece4e9d02aaef88
5
+ SHA512:
6
+ metadata.gz: 66429d68ca92ad9c7ede79c87e1ef239709008f557770d3f370f713c56c45d9496fc82cf2ec1ba455c00b1a87d0262034a321e8a55f708ea176f142916e3e506
7
+ data.tar.gz: 47b1c752d7e2b932ea08d83a7d9f0efb1c757a14bc1a7c93427cd919a167994de4d00bbb7c0c40a3bac7d90eae09fef0ca6ee25c6c0025b9c8de6f4c777a8ab8
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,45 @@
1
+ # rcov generated
2
+ coverage
3
+
4
+ # rdoc generated
5
+ rdoc
6
+
7
+ # yard generated
8
+ doc
9
+ .yardoc
10
+
11
+ # bundler
12
+ .bundle
13
+
14
+ # jeweler generated
15
+ pkg
16
+
17
+ # gem generated
18
+ *.gem
19
+
20
+ # Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
21
+ #
22
+ # * Create a file at ~/.gitignore
23
+ # * Include files you want ignored
24
+ # * Run: git config --global core.excludesfile ~/.gitignore
25
+ #
26
+ # After doing this, these files will be ignored in all your git projects,
27
+ # saving you from having to 'pollute' every project you touch with them
28
+ #
29
+ # Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
30
+ #
31
+ # For MacOS:
32
+ #
33
+ .DS_Store
34
+ #
35
+ # For TextMate
36
+ *.tmproj
37
+ tmtags
38
+ #
39
+ # For emacs:
40
+ #*~
41
+ #\#*
42
+ #.\#*
43
+ #
44
+ # For vim:
45
+ #*.swp
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'http://rubygems.org'
2
+
@@ -0,0 +1,8 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+
5
+ PLATFORMS
6
+ ruby
7
+
8
+ DEPENDENCIES
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2015 Hubert Theodore
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,73 @@
1
+ = devise_sms_confirmation
2
+
3
+ Devise Phone is a snap-in for Devise that will make any resource activable via SMS.
4
+ The user will receive an SMS with a token that can be entered on the site to activate the account.
5
+ Ask the user his phone (and phone confirmation to double check) on registration and the token will be sended automagically.
6
+
7
+ == Installation for Rails >= 3.0 and Devise >= 1.1
8
+
9
+ Install DevisePhone gem, it will also install dependencies (such as devise and warden):
10
+
11
+ gem install devise_phone
12
+
13
+ Add DevisePhone to your Gemfile (and Devise and TwilioRuby if you weren't using them):
14
+
15
+ gem 'devise', '~> 3.4.1'
16
+ gem 'twilio-ruby', '~> 4.2.0'
17
+ gem 'devise_phone', '~> 0.0.1'
18
+
19
+ === Automatic installation
20
+
21
+ Run the following generator to add DevisePhone’s configuration option in the Devise configuration file (config/initializers/devise.rb) and the sms sender class in your lib folder:
22
+
23
+ rails generate devise_phone:install
24
+
25
+ When you are done, you are ready to add DevisePhone to any of your Devise models using the following generator:
26
+
27
+ rails generate devise_phone MODEL
28
+
29
+ Replace MODEL by the class name you want to add DevisePhone, like User, Admin, etc. This will add the :phone flag to your model's Devise modules. The generator will also create a migration file (if your ORM support them). Continue reading this file to understand exactly what the generator produces and how to use it.
30
+
31
+ == Configuring views
32
+
33
+ All the views are packaged inside the gem. If you'd like to customize the views, invoke the following generator and it will copy all the views to your application:
34
+
35
+ rails generate devise_phone:views
36
+
37
+ You can also use the generator to generate scoped views:
38
+
39
+ rails generate devise_phone:views users
40
+
41
+ Please refer to {Devise's README}[http://github.com/plataformatec/devise] for more information about views.
42
+
43
+ == Usage
44
+
45
+ The model is specular to the Devise's own Confirmable model. It only requires the user to supply a valid phone number.
46
+
47
+ On registration it will send an SMS with a token to be inserted to complete activation process.
48
+ By default users MUST activate by SMS before entering.
49
+ If you want something more "relaxed" just override <tt>sms_confirmation_required?</tt> in your model and make it your way.
50
+ You can use the convenience filter <tt>require_sms_activated!</tt> in your controller to block sms-unactive users from specific pages.
51
+
52
+ == Controller filter
53
+
54
+ DevisePhone extends your controllers with a <tt>require_sms_activated!</tt> method. Use it to restrict part of the site to "confirmed users" only
55
+
56
+ == I18n
57
+
58
+ DevisePhone installs a localizable file in your config/locales folder.
59
+
60
+ == Contributing to devise_phone
61
+
62
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
63
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
64
+ * Fork the project
65
+ * Start a feature/bugfix branch
66
+ * Commit and push until you are happy with your contribution
67
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
68
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
69
+
70
+ == Copyright
71
+
72
+ Copyright (c) 2015 Hubert Theodore. See LICENSE.txt for
73
+ further details.
@@ -0,0 +1,21 @@
1
+ require 'rake'
2
+ require 'bundler'
3
+ Bundler::GemHelper.install_tasks
4
+
5
+ require 'rake/testtask'
6
+ Rake::TestTask.new(:test) do |test|
7
+ test.libs << 'lib' << 'test'
8
+ test.pattern = 'test/**/*_test.rb'
9
+ test.verbose = true
10
+ end
11
+
12
+ desc 'Default: run tests for all ORMs.'
13
+ task :default => :tests
14
+
15
+ desc 'Run Devise tests for all ORMs.'
16
+ task :tests do
17
+ Dir[File.join(File.dirname(__FILE__), 'test', 'orm', '*.rb')].each do |file|
18
+ orm = File.basename(file).split(".").first
19
+ system "rake test DEVISE_ORM=#{orm}"
20
+ end
21
+ end
@@ -0,0 +1,51 @@
1
+ class Devise::PhoneVerificationsController < DeviseController
2
+
3
+ # GET /resource/phone_verification/new
4
+ def new
5
+ build_resource({})
6
+ render :new
7
+ end
8
+
9
+ # POST /resource/phone_verification
10
+ def create
11
+
12
+ self.send_verification_code
13
+
14
+ # self.resource = resource_class.send_verification_code
15
+
16
+ # if resource.errors.empty?
17
+ # set_flash_message :notice, :send_token, :phone => self.resource.phone
18
+ # redirect_to new_session_path(resource_name)
19
+ # else
20
+ # render :new
21
+ # end
22
+ end
23
+
24
+ # GET /resource/phone_verification/insert
25
+ def insert
26
+ build_resource({})
27
+ end
28
+
29
+ # GET or POST /resource/phone_verification/consume?sms_token=abcdef
30
+ def consume
31
+
32
+ self.verify_phone_number_with_code_entered(params[:code_entered])
33
+
34
+ # self.resource = resource_class.verify_phone_number_with_code_entered(params[:code_entered])
35
+
36
+ # if resource.errors.empty?
37
+ # set_flash_message :notice, :confirmed
38
+ # sign_in_and_redirect(resource_name, resource)
39
+ # else
40
+ # render :new
41
+ # end
42
+
43
+ end
44
+
45
+ protected
46
+
47
+ def build_resource(hash = nil)
48
+ self.resource = resource_class.new
49
+ end
50
+
51
+ end
@@ -0,0 +1,11 @@
1
+ <h2>Activate Phone Number</h2>
2
+
3
+ <%= form_for(resource, :as => resource_name, :url => consume_phone_verification_path(resource_name), :html => { :method => :post }) do |f| %>
4
+
5
+ <p><%=label_tag :code_entered %><br />
6
+ <%=text_field_tag :code_entered, "" %></p>
7
+
8
+ <p><%= f.submit "Activate" %></p>
9
+ <% end %>
10
+
11
+ <%= render :partial => "devise/shared/links" %>
@@ -0,0 +1,12 @@
1
+ <h2>Resend Phone Verification Code</h2>
2
+
3
+ <%= form_for(resource, :as => resource_name, :url => phone_verification_path(resource_name), :html => { :method => :post }) do |f| %>
4
+ <%= devise_error_messages! %>
5
+
6
+ <p><%= f.label :email %><br />
7
+ <%= f.email_field :email %></p>
8
+
9
+ <p><%= f.submit "Resend Phone Verification Code" %></p>
10
+ <% end %>
11
+
12
+ <%= render :partial => "devise/shared/links" %>
@@ -0,0 +1,15 @@
1
+ en:
2
+ errors:
3
+ messages:
4
+ no_phone_associated: "No phone associated"
5
+ sms_already_confirmed: "This token has been already used"
6
+ sms_token_invalid: "was not locked"
7
+ devise:
8
+ sms_activations:
9
+ send_token: 'An activation token was sent by SMS to %{phone}.'
10
+ sms_token_invalid: 'The sms token provided is not valid!'
11
+ confirmed: 'Your account has been activated. You are now signed in.'
12
+ sms_activation_required: 'SMS Activation is required'
13
+ sms_body: 'Your Activation Token is %{sms_confirmation_token}.'
14
+ unconfirmed_sms: 'Your account need to be activated with an SMS token'
15
+
@@ -0,0 +1,42 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
4
+ require 'devise_phone/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "devise_phone"
8
+ s.version = DevisePhone::VERSION
9
+ s.authors = ["Hubert Theodore"]
10
+ s.email = ["htheodore@gmail.com"]
11
+ s.homepage = "https://github.com/tjhubert/devise_phone"
12
+ s.license = "MIT"
13
+ s.summary = "Send SMS to verify phone number"
14
+ s.description = "It sends verification code via SMS (using Twilio). User enters the code to confirm the phone number."
15
+ s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
16
+ # s.files = Dir["{app,config,lib}/**/*"] + %w[LICENSE README.rdoc]
17
+ # s.require_path = "lib"
18
+ # s.rdoc_options = ["--main", "README.rdoc", "--charset=UTF-8"]
19
+
20
+ # s.required_ruby_version = '>= 1.8.6'
21
+ # s.required_rubygems_version = '>= 1.3.6'
22
+ s.bindir = "exe"
23
+ s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
+ s.require_paths = ["lib"]
25
+
26
+ {
27
+ 'bundler' => '~> 1.10',
28
+ # 'rspec',
29
+ 'rake' => '~> 10.0'
30
+ }.each do |lib, version|
31
+ s.add_development_dependency(lib, version)
32
+ end
33
+
34
+ {
35
+ 'rails' => '>= 4.0.0',
36
+ 'devise' => '>= 3.0.0',
37
+ 'twilio-ruby' => '>= 4.0.0'
38
+ }.each do |lib, version|
39
+ s.add_runtime_dependency(lib, version)
40
+ end
41
+
42
+ end
@@ -0,0 +1,31 @@
1
+ require "devise"
2
+ require "twilio-ruby"
3
+
4
+ $: << File.expand_path("..", __FILE__)
5
+
6
+ require "devise_phone/routes"
7
+ require "devise_phone/schema"
8
+ require 'devise_phone/controllers/url_helpers'
9
+ require 'devise_phone/controllers/helpers'
10
+ require 'devise_phone/rails'
11
+
12
+ module Devise
13
+ # mattr_accessor :sms_confirm_within
14
+ # @@sms_confirm_within = 2.days
15
+ # mattr_accessor :sms_confirmation_keys
16
+ # @@sms_confirmation_keys = [:email]
17
+
18
+ # Get the sms sender class from the mailer reference object.
19
+ def self.sms_sender
20
+ @@sms_sender_ref.get
21
+ end
22
+
23
+ # Set the smser reference object to access the smser.
24
+ def self.sms_sender=(class_name)
25
+ @@sms_sender_ref = ActiveSupport::Dependencies.reference(class_name)
26
+ end
27
+
28
+ self.sms_sender = "Devise::SmsSender"
29
+ end
30
+
31
+ Devise.add_module :phone, :model => "models/phone", :controller => :phone_verifications, :route => :phone_verification
@@ -0,0 +1,12 @@
1
+ module DeviseSmsPhone::Controllers::Helpers
2
+ protected
3
+
4
+ # Convenience helper to check if user has confirmed the token (and the phone) or not.
5
+ def require_sms_activated!
6
+ if(send(:"authenticate_#{resource_name}!"))
7
+ res=send(:"current_#{resource_name}")
8
+ fail!(:sms_activation_required) if (!res) || (!res.sms_confirmed?)
9
+ end
10
+ end
11
+ end
12
+ ActionController::Base.send :include, DeviseSmsPhone::Controllers::Helpers
@@ -0,0 +1,24 @@
1
+ module DevisePhone
2
+ module Controllers
3
+ module UrlHelpers
4
+ [:path, :url].each do |path_or_url|
5
+ [nil, :new_, :create_, :activate_].each do |action|
6
+ class_eval <<-URL_HELPERS, __FILE__, __LINE__ + 1
7
+ def #{action}phone_verification_#{path_or_url}(resource, *args)
8
+ resource = case resource
9
+ when Symbol, String
10
+ resource
11
+ when Class
12
+ resource.name.underscore
13
+ else
14
+ resource.class.name.underscore
15
+ end
16
+
17
+ send("#{action}\#{resource}_phone_verification_#{path_or_url}", *args)
18
+ end
19
+ URL_HELPERS
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,11 @@
1
+ # Deny user access whenever his account is not active yet. All strategies that inherits from
2
+ # Devise::Strategies::Authenticatable and uses the validate already check if the user is active?
3
+ # before actively signing him in. However, we need this as hook to validate the user activity
4
+ # in each request and in case the user is using other strategies beside Devise ones.
5
+ Warden::Manager.after_set_user do |record, warden, options|
6
+ if record && record.respond_to?(:active?) && !record.active?
7
+ scope = options[:scope]
8
+ warden.logout(scope)
9
+ throw :warden, :scope => scope, :message => record.inactive_message
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ module DeviseInvitable
2
+ class Engine < ::Rails::Engine
3
+
4
+ ActiveSupport.on_load(:action_controller) { include DevisePhone::Controllers::UrlHelpers }
5
+ ActiveSupport.on_load(:action_view) { include DevisePhone::Controllers::UrlHelpers }
6
+
7
+ config.after_initialize do
8
+
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ module ActionDispatch::Routing
2
+ class Mapper
3
+
4
+ protected
5
+ def devise_phone(mapping, controllers)
6
+ resource :phone_verification, :only => [:new, :create], :path => mapping.path_names[:phone_verification], :controller => controllers[:phone_verifications] do
7
+ match :consume, :path => mapping.path_names[:consume], :as => :consume
8
+ get :insert, :path => mapping.path_names[:insert], :as => :insert
9
+ end
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ module DevisePhone
2
+ module Schema
3
+
4
+ def phone
5
+ apply_devise_schema :phone_number, String
6
+ apply_devise_schema :phone_number_verified, Boolean
7
+ apply_devise_schema :phone_verification_code, String, :limit => 6
8
+ apply_devise_schema :phone_verification_code_sent_at, DateTime
9
+ apply_devise_schema :phone_verified_at, DateTime
10
+ end
11
+ end
12
+ end
13
+
14
+ #Devise::Schema.send :include, DeviseSmsActivable::Schema
@@ -0,0 +1,3 @@
1
+ module DevisePhone
2
+ VERSION = "0.0.1".freeze
3
+ end
@@ -0,0 +1,13 @@
1
+ require 'rails/generators/active_record'
2
+
3
+ module ActiveRecord
4
+ module Generators
5
+ class DevisePhoneGenerator < ActiveRecord::Generators::Base
6
+ source_root File.expand_path("../templates", __FILE__)
7
+
8
+ def copy_devise_migration
9
+ migration_template "migration.rb", "db/migrate/devise_phone_add_to_#{table_name}"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ class DevisePhoneAddTo<%= table_name.camelize %> < ActiveRecord::Migration
2
+ def change
3
+ add_column :<%= table_name %>, :phone_number, :string
4
+ add_column :<%= table_name %>, :phone_number_verified, :boolean
5
+ add_column :<%= table_name %>, :phone_verification_code, :string, :limit => 6
6
+ add_column :<%= table_name %>, :phone_verification_code_sent_at, :datetime
7
+ add_column :<%= table_name %>, :phone_verified_at, :datetime
8
+
9
+ add_index :<%= table_name %>, :phone_number, unique: true
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ module DevisePhone
2
+ module Generators
3
+ class DevisePhoneGenerator < Rails::Generators::NamedBase
4
+ namespace "devise_phone"
5
+
6
+ desc "Add :phone_number directive in the given model. Also generate migration for ActiveRecord"
7
+
8
+ # def devise_generate_model
9
+ # invoke "devise", [name]
10
+ # end
11
+
12
+ def inject_devise_phone_content
13
+ path = File.join("app", "models", "#{file_path}.rb")
14
+ inject_into_file(path, "phone_number, :", :after => "devise :") if File.exists?(path)
15
+ end
16
+
17
+ hook_for :orm
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,52 @@
1
+ module DevisePhone
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ source_root File.expand_path("../../templates", __FILE__)
5
+ desc "Add DevisePhone config variables to the Devise initializer and copy DeviseSms locale files to your application."
6
+
7
+ # def devise_install
8
+ # invoke "devise:install"
9
+ # end
10
+
11
+ def add_config_options_to_initializer
12
+ devise_initializer_path = "config/initializers/devise.rb"
13
+ if File.exist?(devise_initializer_path)
14
+ old_content = File.read(devise_initializer_path)
15
+
16
+ if old_content.match(Regexp.new(/^\s# ==> Configuration for :phone\n/))
17
+ false
18
+ else
19
+ inject_into_file(devise_initializer_path, :before => " # ==> Configuration for :confirmable\n") do
20
+ <<-CONTENT
21
+ # ==> Configuration for :sms_activable
22
+ # The period the generated sms token is valid, after
23
+ # this period, the user won't be able to activate.
24
+ # config.sms_confirm_within = 0.days
25
+
26
+ # The keys searched for confirmation values.
27
+ # config.sms_confirmation_keys = [:email]
28
+
29
+ # Your SmsSender class. The provided one uses
30
+ # moonshado-sms gem so install it and configure
31
+ # if you want to use it.
32
+ # A simple instance of the class has been copied in your lib folder
33
+ # For further informations on using and configuring moonshado-sms gem check
34
+ # https://github.com/moonshado/moonshado-sms
35
+ # config.sms_sender = "Devise::SmsSender"
36
+
37
+ CONTENT
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ def copy_locale
44
+ copy_file "../../../config/locales/en.yml", "config/locales/devise_phone.en.yml"
45
+ end
46
+
47
+ def copy_default_smser
48
+ copy_file "lib/sms_sender.rb", "lib/devise_sms_sender.rb"
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,10 @@
1
+ require 'generators/devise/views_generator'
2
+
3
+ module DevisePhone
4
+ module Generators
5
+ class ViewsGenerator < Devise::Generators::ViewsGenerator
6
+ source_root File.expand_path("../../../../app/views", __FILE__)
7
+ desc 'Copies all DevisePhone views to your application.'
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,8 @@
1
+ require 'generators/devise/orm_helpers'
2
+
3
+ module Mongoid
4
+ module Generators
5
+ class DevisePhoneGenerator < Rails::Generators::NamedBase
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,19 @@
1
+ class Devise::SmsSender
2
+ #Actually sends the sms token. feel free to modify and adapt to your provider and/or gem
3
+ def send_sms_verification_code_to(user)
4
+ number_to_send_to = user.phone_number
5
+ verification_code = user.phone_verification_code
6
+
7
+ twilio_sid = "ACd35391c08cde7926e2295d1812ada918"
8
+ twilio_token = "44d79a36adb3d54cc15711d94d149119"
9
+ twilio_phone_number = "6502810746"
10
+
11
+ @twilio_client = Twilio::REST::Client.new twilio_sid, twilio_token
12
+
13
+ @twilio_client.account.sms.messages.create(
14
+ :from => "+1#{twilio_phone_number}",
15
+ :to => number_to_send_to,
16
+ :body => "Hi! This is MathCrunch. Your verification code is #{verification_code}"
17
+ )
18
+ end
19
+ end
@@ -0,0 +1,215 @@
1
+ require "devise_phone/hooks"
2
+
3
+ module Devise
4
+ module Models
5
+ # SmsActivable is responsible to verify if an account is already confirmed to
6
+ # sign in, and to send sms with confirmation instructions.
7
+ # Confirmation instructions are sent to the user phone after creating a
8
+ # record and when manually requested by a new confirmation instruction request.
9
+ #
10
+ # == Options
11
+ #
12
+ # Confirmable adds the following options to devise_for:
13
+ #
14
+ # * +sms_confirm_within+: the time you want to allow the user to access his account
15
+ # before confirming it. After this period, the user access is denied. You can
16
+ # use this to let your user access some features of your application without
17
+ # confirming the account, but blocking it after a certain period (ie 7 days).
18
+ # By default confirm_within is 0 days, so the user must confirm before entering.
19
+ # If you want to allow user to use parts of the site and block others override
20
+ # sms_confirmation_required? and check manually on selected pages using the
21
+ # require_sms_activated! helper or sms_confirmed? property on record
22
+ #
23
+ # == Examples
24
+ #
25
+ # User.find(1).sms_confirm! # returns true unless it's already confirmed
26
+ # User.find(1).sms_confirmed? # true/false
27
+ # User.find(1).send_sms_token # manually send token
28
+ #
29
+ module Phone
30
+ extend ActiveSupport::Concern
31
+
32
+ included do
33
+ before_create :set_phone_attributes, :if => :phone_verification_needed?
34
+ after_create :generate_verification_code_and_send_sms, :if => :phone_verification_needed?
35
+ end
36
+
37
+ # # Confirm a user by setting it's sms_confirmed_at to actual time. If the user
38
+ # # is already confirmed, add en error to email field
39
+ # def confirm_sms!
40
+ # unless_sms_confirmed do
41
+ # self.sms_confirmation_token = nil
42
+ # self.sms_confirmed_at = Time.now
43
+ # save(:validate => false)
44
+ # end
45
+ # end
46
+
47
+ # # Verifies whether a user is sms-confirmed or not
48
+ # def confirmed_sms?
49
+ # !!sms_confirmed_at
50
+ # end
51
+
52
+ # Send confirmation token by sms
53
+ def generate_verification_code_and_send_sms
54
+ if(self.phone_number?)
55
+ self.phone_verification_code = generate_phone_verification_code
56
+ ::Devise.sms_sender.send_sms_verification_code_to(self)
57
+ else
58
+ # self.errors.add(:sms_confirmation_token, :no_phone_associated)
59
+ false
60
+ end
61
+ end
62
+
63
+ # # Resend sms confirmation token. This method does not need to generate a new token.
64
+ # def resend_sms_token
65
+ # unless_sms_confirmed { send_sms_token }
66
+ # end
67
+
68
+ # Overwrites active? from Devise::Models::Activatable for sms confirmation
69
+ # by verifying whether a user is active to sign in or not. If the user
70
+ # is already confirmed, it should never be blocked. Otherwise we need to
71
+ # calculate if the confirm time has not expired for this user.
72
+
73
+ # def active?
74
+ # !sms_confirmation_required? || confirmed_sms? || confirmation_sms_period_valid?
75
+ # end
76
+
77
+ # # The message to be shown if the account is inactive.
78
+ # def inactive_message
79
+ # !confirmed_sms? ? I18n.t(:"devise.sms_activations.unconfirmed_sms") : super
80
+ # end
81
+
82
+ # # If you don't want confirmation to be sent on create, neither a code
83
+ # # to be generated, call skip_sms_confirmation!
84
+ # def skip_sms_confirmation!
85
+ # self.sms_confirmed_at = Time.now
86
+ # end
87
+
88
+ protected
89
+
90
+ # Callback to overwrite if an sms confirmation is required or not.
91
+ def phone_verification_needed?
92
+ phone_number.present? && !phone_number_verified
93
+ end
94
+
95
+ # def sms_confirmation_required?
96
+ # !confirmed_sms?
97
+ # end
98
+
99
+ # Checks if the confirmation for the user is within the limit time.
100
+ # We do this by calculating if the difference between today and the
101
+ # confirmation sent date does not exceed the confirm in time configured.
102
+ # Confirm_in is a model configuration, must always be an integer value.
103
+ #
104
+ # Example:
105
+ #
106
+ # # sms_confirm_within = 1.day and sms_confirmation_sent_at = today
107
+ # confirmation_period_valid? # returns true
108
+ #
109
+ # # sms_confirm_within = 5.days and sms_confirmation_sent_at = 4.days.ago
110
+ # confirmation_period_valid? # returns true
111
+ #
112
+ # # sms_confirm_within = 5.days and sms_confirmation_sent_at = 5.days.ago
113
+ # confirmation_period_valid? # returns false
114
+ #
115
+ # # sms_confirm_within = 0.days
116
+ # confirmation_period_valid? # will always return false
117
+ #
118
+ # def confirmation_sms_period_valid?
119
+ # sms_confirmation_sent_at && sms_confirmation_sent_at.utc >= self.class.sms_confirm_within.ago
120
+ # end
121
+
122
+ # # Checks whether the record is confirmed or not, yielding to the block
123
+ # # if it's already confirmed, otherwise adds an error to email.
124
+ # def unless_sms_confirmed
125
+ # unless confirmed_sms?
126
+ # yield
127
+ # else
128
+ # self.errors.add(:sms_confirmation_token, :sms_already_confirmed)
129
+ # false
130
+ # end
131
+ # end
132
+
133
+ # Generates a new random token for confirmation, and stores the time
134
+ # this token is being generated
135
+ def set_phone_attributes
136
+
137
+ self.phone_number_verified = false
138
+ self.phone_verification_code_sent_at = DateTime.now
139
+ self.phone_verified_at = nil
140
+ # removes all white spaces, hyphens, and parenthesis
141
+ self.phone_number.gsub!(/[\s\-\(\)]+/, '')
142
+ end
143
+
144
+ def generate_phone_verification_code
145
+ # begin
146
+ verification_code = SecureRandom.hex(3)
147
+ # end while self.class.exists?(phone_verification_code: verification_code)
148
+ verification_code
149
+ end
150
+
151
+ # def generate_sms_token!
152
+ # generate_sms_token && save(:validate => false)
153
+ # end
154
+
155
+ module ClassMethods
156
+ # # Attempt to find a user by it's email. If a record is found, send a new
157
+ # # sms token instructions to it. If not user is found, returns a new user
158
+ # # with an email not found error.
159
+ # # Options must contain the user email
160
+ # def send_sms_token(attributes={})
161
+ # sms_confirmable = find_or_initialize_with_errors(sms_confirmation_keys, attributes, :not_found)
162
+ # sms_confirmable.resend_sms_token if sms_confirmable.persisted?
163
+ # sms_confirmable
164
+ # end
165
+
166
+ # # Find a user by it's sms confirmation token and try to confirm it.
167
+ # # If no user is found, returns a new user with an error.
168
+ # # If the user is already confirmed, create an error for the user
169
+ # # Options must have the sms_confirmation_token
170
+ # def confirm_by_sms_token(sms_confirmation_token)
171
+ # sms_confirmable = find_or_initialize_with_error_by(:sms_confirmation_token, sms_confirmation_token)
172
+ # sms_confirmable.confirm_sms! if sms_confirmable.persisted?
173
+ # sms_confirmable
174
+ # end
175
+
176
+ def mark_phone_as_verified!
177
+ update!(phone_number_verified: true,
178
+ phone_verification_code: nil,
179
+ phone_verification_code_sent_at: nil,
180
+ phone_verified_at: DateTime.now)
181
+ end
182
+
183
+ def verify_phone_number_with_code_entered(code_entered)
184
+ if self.phone_verification_code == code_entered
185
+ mark_phone_as_verified!
186
+ end
187
+ end
188
+
189
+ def send_verification_code
190
+ self.set_phone_attributes
191
+ if self.save!
192
+ send_sms_for_phone_verification
193
+ end
194
+ end
195
+
196
+ # # Generates a small token that can be used conveniently on SMS's.
197
+ # # The token is 5 chars long and uppercased.
198
+
199
+ # def generate_small_token(column)
200
+ # loop do
201
+ # token = Devise.friendly_token[0,5].upcase
202
+ # break token unless to_adapter.find_first({ column => token })
203
+ # end
204
+ # end
205
+
206
+ # # Generate an sms token checking if one does not already exist in the database.
207
+ # def sms_confirmation_token
208
+ # generate_small_token(:sms_confirmation_token)
209
+ # end
210
+
211
+ # Devise::Models.config(self, :sms_confirm_within, :sms_confirmation_keys)
212
+ end
213
+ end
214
+ end
215
+ end
@@ -0,0 +1,5 @@
1
+ require 'devise'
2
+
3
+ $: << File.expand_path("..", __FILE__)
4
+
5
+ require 'lib/devise_phone'
metadata ADDED
@@ -0,0 +1,144 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: devise_phone
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Hubert Theodore
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-06-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 4.0.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 4.0.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: devise
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 3.0.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 3.0.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: twilio-ruby
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 4.0.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 4.0.0
83
+ description: It sends verification code via SMS (using Twilio). User enters the code
84
+ to confirm the phone number.
85
+ email:
86
+ - htheodore@gmail.com
87
+ executables: []
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".document"
92
+ - ".gitignore"
93
+ - Gemfile
94
+ - Gemfile.lock
95
+ - LICENSE
96
+ - README.rdoc
97
+ - Rakefile
98
+ - app/controllers/devise/phone_verifications_controller.rb
99
+ - app/views/devise/sms_activations/insert.html.erb
100
+ - app/views/devise/sms_activations/new.html.erb
101
+ - config/locales/en.yml
102
+ - devise_phone.gemspec
103
+ - lib/devise_phone.rb
104
+ - lib/devise_phone/controllers/helpers.rb
105
+ - lib/devise_phone/controllers/url_helpers.rb
106
+ - lib/devise_phone/hooks.rb
107
+ - lib/devise_phone/rails.rb
108
+ - lib/devise_phone/routes.rb
109
+ - lib/devise_phone/schema.rb
110
+ - lib/devise_phone/version.rb
111
+ - lib/generators/active_record/devise_phone_generator.rb
112
+ - lib/generators/active_record/templates/migration.rb
113
+ - lib/generators/devise_phone/devise_phone_generator.rb
114
+ - lib/generators/devise_phone/install_generator.rb
115
+ - lib/generators/devise_phone/views_generator.rb
116
+ - lib/generators/mongoid/devise_phone_generator.rb
117
+ - lib/generators/templates/lib/sms_sender.rb
118
+ - lib/models/phone.rb
119
+ - rails/init.rb
120
+ homepage: https://github.com/tjhubert/devise_phone
121
+ licenses:
122
+ - MIT
123
+ metadata: {}
124
+ post_install_message:
125
+ rdoc_options: []
126
+ require_paths:
127
+ - lib
128
+ required_ruby_version: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ required_rubygems_version: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ requirements: []
139
+ rubyforge_project:
140
+ rubygems_version: 2.4.6
141
+ signing_key:
142
+ specification_version: 4
143
+ summary: Send SMS to verify phone number
144
+ test_files: []