devise_userbin 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a9d187a96d8b7b0e8a5b7f8b65b4858d2ce695f6
4
+ data.tar.gz: 8f0f15fcc67bffedc6f27d122c1d11b28f1a54ce
5
+ SHA512:
6
+ metadata.gz: 0dda852d3e5db62bac675a4de00e92b7f3a6a2c9b64864261225104e4b790b8d716a09856942b2e408f73778c530b0b1231a1bd27b88f7fc96579f1bb64827bd
7
+ data.tar.gz: 88dcd33100f805e93197169ddff36bf72eac617b128a5e75efe3373b693c1da60c3d9def12d8e8dee7afc22edf7ec500ca6e388a3e3f84a6e2d61c705f45558c
@@ -0,0 +1,37 @@
1
+ class Devise::DeviseUserbinController < DeviseController
2
+ include Devise::Controllers::Helpers
3
+
4
+ def show
5
+ self.resource = resource_class.new
6
+ end
7
+
8
+ def update
9
+ render :show and return if params[:code].nil?
10
+
11
+ Devise.mappings.keys.flatten.any? do |scope|
12
+ begin
13
+ session["#{scope}_userbin"] =
14
+ Userbin.verify_code(session["#{scope}_userbin"], params[:code])
15
+ set_flash_message :notice, :success
16
+ redirect_to after_sign_in_path_for(scope)
17
+ rescue Userbin::UserUnauthorizedError => error
18
+ set_flash_message :alert, :failed
19
+ self.resource = resource_class.new
20
+ respond_with_navigational(resource_name) { render :show }
21
+ rescue Userbin::Forbidden => error
22
+ sign_out_with_message(:no_retries_remaining, :alert)
23
+ rescue Userbin::Error => error
24
+ sign_out_with_message(:alert, :alert)
25
+ end
26
+ end
27
+ end
28
+
29
+ protected
30
+
31
+ def sign_out_with_message(message, kind = :notice)
32
+ signed_out = sign_out(resource_name)
33
+ set_flash_message kind, message if signed_out
34
+ redirect_to after_sign_out_path_for(resource_name)
35
+ end
36
+
37
+ end
@@ -0,0 +1,8 @@
1
+ class Devise::SecuritySettingsController < DeviseController
2
+ include Devise::Controllers::Helpers
3
+
4
+ def show
5
+ session_token = session["#{resource_name}_userbin"]
6
+ redirect_to Userbin.security_page_url(session_token)
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ module Devise
2
+ class TwoFactorAuthenticationController < DeviseUserbinController; end
3
+ end
@@ -0,0 +1,3 @@
1
+ module Devise
2
+ class TwoFactorRecoveryController < DeviseUserbinController; end
3
+ end
@@ -0,0 +1,15 @@
1
+ <h2><%= t "devise.two_factor_authentication.show.header" %></h2>
2
+
3
+ <p><%= t "devise.two_factor_authentication.show.instructions" %></p>
4
+
5
+ <%= form_tag([resource_name, :two_factor_authentication], :method => :put) do %>
6
+ <%= devise_error_messages! %>
7
+ <p><%= label_tag :code, t("devise.two_factor_authentication.show.code_label") %><br />
8
+ <%= text_field_tag :code %></p>
9
+ <p><%= submit_tag t("devise.two_factor_authentication.show.submit_button") %></p>
10
+ <% end -%>
11
+
12
+ <p><%= t "devise.two_factor_authentication.show.recovery_message" %><br />
13
+ <%= link_to t("devise.two_factor_authentication.show.recovery_action"), [resource_name, :two_factor_recovery] %></p>
14
+
15
+ <%= link_to "Sign out", destroy_session_path(resource_name), :method => Devise.sign_out_via %>
@@ -0,0 +1,10 @@
1
+ <h2><%= t "devise.two_factor_recovery.show.header" %></h2>
2
+
3
+ <%= form_tag([resource_name, :two_factor_recovery], :method => :put) do %>
4
+ <%= devise_error_messages! %>
5
+ <p><%= label_tag :code, t("devise.two_factor_recovery.show.code_label")%><br />
6
+ <%= text_field_tag :code %></p>
7
+ <p><%= submit_tag t("devise.two_factor_recovery.show.submit_button") %></p>
8
+ <% end -%>
9
+
10
+ <p><%= t "devise.two_factor_recovery.show.message" %></p>
@@ -0,0 +1,31 @@
1
+ require 'active_support/concern'
2
+ require 'devise'
3
+ require 'devise_userbin/hooks'
4
+ require 'devise_userbin/routes'
5
+ require 'devise_userbin/hooks'
6
+ require 'devise_userbin/import'
7
+ require 'userbin'
8
+
9
+ if defined?(Rails::Railtie)
10
+ require 'devise_userbin/railtie'
11
+ Rails::Engine
12
+ end
13
+
14
+ module Devise
15
+ mattr_accessor :userbin_api_secret
16
+ @@userbin_api_secret = ''
17
+ end
18
+
19
+ module DeviseUserbin
20
+ module Controllers
21
+ autoload :Helpers, 'devise_userbin/controllers/helpers'
22
+ end
23
+ module Views
24
+ autoload :Helpers, 'devise_userbin/controllers/view_helpers'
25
+ end
26
+ end
27
+
28
+ Devise.add_module(:userbin,
29
+ :controller => :two_factor_authentication,
30
+ :route => :userbin,
31
+ :model => 'devise_userbin/model')
@@ -0,0 +1,41 @@
1
+ module DeviseUserbin
2
+ module Controllers
3
+ module Helpers
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ before_filter :handle_two_factor_authentication
8
+ end
9
+
10
+ private
11
+
12
+ def handle_two_factor_authentication
13
+ unless devise_controller?
14
+ Devise.mappings.keys.flatten.any? do |scope|
15
+ if signed_in?(scope)
16
+ if Userbin.two_factor_authenticate!(session["#{scope}_userbin"])
17
+ handle_required_two_factor_authentication(scope)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ def handle_required_two_factor_authentication(scope)
25
+ if request.format.present? and request.format.html?
26
+ session["#{scope}_return_to"] = request.path if request.get?
27
+ redirect_to two_factor_authentication_path_for(scope)
28
+ else
29
+ render nothing: true, status: :unauthorized
30
+ end
31
+ end
32
+
33
+ def two_factor_authentication_path_for(resource_or_scope = nil)
34
+ scope = Devise::Mapping.find_scope!(resource_or_scope)
35
+ change_path = "#{scope}_two_factor_authentication_path"
36
+ send(change_path)
37
+ end
38
+
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,13 @@
1
+ module DeviseUserbin
2
+ module Views
3
+ module Helpers
4
+
5
+ def security_page_url(opts = {})
6
+ scope = opts[:scope] || ::Devise.default_scope
7
+ session_token = session["#{scope}_userbin"]
8
+ Userbin.security_page_url(session_token)
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,34 @@
1
+ # Everytime current_<scope> is prepared
2
+ #
3
+ Warden::Manager.after_set_user :only => :fetch do |record, warden, opts|
4
+ scope = opts[:scope]
5
+
6
+ begin
7
+ session_token = warden.request.session["#{scope}_userbin"]
8
+
9
+ session_token =
10
+ Userbin.authenticate(session_token, record._userbin_id, {
11
+ properties: {
12
+ email: record.email
13
+ },
14
+ context: {
15
+ ip: warden.request.ip,
16
+ user_agent: warden.request.user_agent
17
+ }
18
+ })
19
+
20
+ warden.request.session["#{scope}_userbin"] = session_token
21
+
22
+ rescue Userbin::Error => error
23
+ warden.logout(scope)
24
+ throw :warden, :scope => scope, :message => :timeout
25
+ end
26
+
27
+ end
28
+
29
+ Warden::Manager.before_logout do |record, warden, opts|
30
+ begin
31
+ session_token = warden.request.session.delete("#{opts[:scope]}_userbin")
32
+ Userbin.deauthenticate(session_token)
33
+ rescue Userbin::Error; end
34
+ end
@@ -0,0 +1,85 @@
1
+ module DeviseUserbin
2
+ class ImportError < Exception; end
3
+
4
+ class Import
5
+ attr_reader :resource_class, :batch_size
6
+
7
+ def initialize(options = {})
8
+ begin
9
+ @resource_class = eval(options[:resource_class])
10
+ rescue NameError
11
+ raise ImportError, "No such class: #{options[:resource_class]}"
12
+ end
13
+
14
+ unless supported_orm?
15
+ raise ImportError, "Only ActiveRecord and Mongoid models are supported"
16
+ end
17
+
18
+ unless Userbin.config.api_secret.present?
19
+ raise ImportError, "Please add an Userbin API secret to your devise.rb"
20
+ end
21
+
22
+ @batch_size = [(options[:batch_size] || 100), 100].min
23
+ end
24
+
25
+ def self.run(*args)
26
+ new(*args).run
27
+ end
28
+
29
+ def run
30
+ batches do |batch, resources|
31
+ begin
32
+ users = Userbin::User.import(users: batch)
33
+ rescue Userbin::Error => error
34
+ raise ImportError, error.message
35
+ end
36
+
37
+ users.zip(resources).each do |user, resource|
38
+ resource.userbin_id = user.id
39
+ resource.save(validate: false)
40
+ end
41
+ end
42
+ end
43
+
44
+ def batches
45
+ if active_record?
46
+ resource_class.where("userbin_id IS NULL").find_in_batches(:batch_size => batch_size) do |resources|
47
+ resources_for_wire = map_resources_to_userbin_format(resources)
48
+ yield(resources_for_wire, resources) unless resources.count.zero?
49
+ end
50
+ elsif mongoid?
51
+ 0.step(resource_class.where(:userbin_id => nil).count, batch_size) do |offset|
52
+ resources_for_wire = map_resources_to_userbin_format(resource_class.limit(batch_size).skip(offset))
53
+ yield(resources_for_wire, resources) unless resources.count.zero?
54
+ end
55
+ end
56
+ end
57
+
58
+ def map_resources_to_userbin_format(resources)
59
+ resources.map do |resource|
60
+ format = {}
61
+ format[:email] = resource.email unless resource.email.blank?
62
+ format[:id] = resource._userbin_id
63
+ format[:created_at] = resource.created_at if resource.respond_to? :created_at
64
+ format
65
+ end.compact
66
+ end
67
+
68
+ def prepare_batch(batch)
69
+ { :users => batch }.to_json
70
+ end
71
+
72
+ def active_record?
73
+ (defined?(ActiveRecord::Base) && (resource_class < ActiveRecord::Base))
74
+ end
75
+
76
+ def mongoid?
77
+ (defined?(Mongoid::Document) && (resource_class < Mongoid::Document))
78
+ end
79
+
80
+ def supported_orm?
81
+ active_record? || mongoid?
82
+ end
83
+
84
+ end
85
+ end
@@ -0,0 +1,53 @@
1
+ module Devise
2
+ module Models
3
+ module Userbin
4
+ extend ActiveSupport::Concern
5
+
6
+ ::Userbin.api_secret = Devise.userbin_api_secret
7
+
8
+ included do
9
+ before_destroy :destroy_userbin_user
10
+
11
+ def destroy_userbin_user
12
+ userbin_user_block do
13
+ ::Userbin::User.destroy_existing(id)
14
+ end
15
+ end
16
+
17
+ def userbin_user_block
18
+ begin
19
+ yield
20
+ rescue ::Userbin::Error => error
21
+ self.errors[:base] << error.to_s
22
+ false
23
+ end
24
+ end
25
+
26
+ # Override this in your Devise model to use a custom identifier
27
+ # for the Userbin API:s
28
+ def userbin_id
29
+ id
30
+ end
31
+
32
+ # Since the identifier will be used in API routes, it needs to be
33
+ # URI encoded
34
+ def _userbin_id
35
+ URI.encode(userbin_id.to_s)
36
+ end
37
+ end
38
+
39
+ # Overwrites valid_for_authentication? from Devise::Models::Authenticatable
40
+ # for verifying whether a user is allowed to sign in or not.
41
+ def valid_for_authentication?
42
+ return super unless persisted?
43
+
44
+ if super
45
+ true
46
+ else
47
+ # TODO: track unsuccessful login
48
+ false
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,10 @@
1
+ module DeviseUserbin
2
+ class Engine < ::Rails::Engine
3
+ ActiveSupport.on_load(:action_controller) do
4
+ include DeviseUserbin::Controllers::Helpers
5
+ end
6
+ ActiveSupport.on_load(:action_view) do
7
+ include DeviseUserbin::Views::Helpers
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,13 @@
1
+ module ActionDispatch::Routing
2
+ class Mapper
3
+ protected
4
+
5
+ def devise_userbin(mapping, controllers)
6
+ resource :two_factor_authentication, :only => [:show, :update], :path => mapping.path_names[:two_factor_authentication], :controller => controllers[:two_factor_authentication]
7
+
8
+ resource :two_factor_recovery, :only => [:show, :update], :path => mapping.path_names[:two_factor_recovery], :controller => controllers[:two_factor_recovery]
9
+
10
+ resource :security_settings, :only => [:show], :path => mapping.path_names[:security_settings], :controller => controllers[:security_settings]
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ module DeviseUserbin
2
+ VERSION = "0.1.0".freeze
3
+ end
@@ -0,0 +1,14 @@
1
+ require 'rails/generators/active_record'
2
+
3
+ module ActiveRecord
4
+ module Generators
5
+ class DeviseUserbinGenerator < ActiveRecord::Generators::Base
6
+ source_root File.expand_path("../templates", __FILE__)
7
+
8
+ def copy_devise_userbin_migration
9
+ migration_template "migration.rb", "db/migrate/add_userbin_to_#{table_name}.rb"
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ class AddUserbinTo<%= table_name.camelize %> < ActiveRecord::Migration
2
+ def up
3
+ change_table :<%= table_name %> do |t|
4
+ t.string :userbin_id
5
+ end
6
+
7
+ add_index :<%= table_name %>, :userbin_id, :unique => true
8
+ end
9
+
10
+ def down
11
+ remove_column :<%= table_name %>, :userbin_id
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ module DeviseUserbin
2
+ module Generators
3
+ class DeviseUserbinGenerator < Rails::Generators::NamedBase
4
+ namespace "devise_userbin"
5
+
6
+ desc "Add :userbin directive in the given model. Also generate migration for ActiveRecord"
7
+
8
+ def inject_devise_userbin_content
9
+ path = File.join("app", "models", "#{file_path}.rb")
10
+ if File.exists?(path)
11
+ inject_into_file(path, "userbin, :", :after => "devise :")
12
+ end
13
+ end
14
+
15
+ hook_for :orm
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,12 @@
1
+ module DeviseUserbin
2
+ module Generators
3
+ class ImportGenerator < Rails::Generators::NamedBase
4
+ desc "Import users to Userbin"
5
+
6
+ def import_users_to_userbin
7
+ Import.run(resource_class: class_name)
8
+ end
9
+
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,32 @@
1
+ module DeviseUserbin
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ source_root File.expand_path("../../templates", __FILE__)
5
+ desc "Add DeviseUserbin config variables to the Devise initializer"
6
+ argument :api_secret, :desc => "Your Userbin API secret, which can be found on your Userbin dashboard."
7
+
8
+ def add_config_options_to_initializer
9
+ devise_initializer_path = "config/initializers/devise.rb"
10
+ if File.exist?(devise_initializer_path)
11
+ old_content = File.read(devise_initializer_path)
12
+
13
+ if old_content.match(Regexp.new(/^\s*# ==> Configuration for :userbin\n/))
14
+ false
15
+ else
16
+ inject_into_file(devise_initializer_path, :before => " # ==> Mailer Configuration\n") do
17
+ <<-CONTENT
18
+ # ==> Configuration for :userbin
19
+ config.userbin_api_secret = '#{api_secret}'
20
+
21
+ CONTENT
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ def copy_locale
28
+ copy_file "../../../config/locales/en.yml", "config/locales/devise_userbin.en.yml"
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,19 @@
1
+ require 'generators/devise/views_generator'
2
+
3
+ module DeviseUserbin
4
+ module Generators
5
+ class ViewsGenerator < Rails::Generators::Base
6
+ desc 'Copies all DeviseUserbin views to your application.'
7
+
8
+ argument :scope, :required => false, :default => nil,
9
+ :desc => "The scope to copy views to"
10
+
11
+ include ::Devise::Generators::ViewPathTemplates
12
+ source_root File.expand_path("../../../../app/views/devise", __FILE__)
13
+ def copy_views
14
+ view_directory :two_factor_authentication
15
+ view_directory :two_factor_recovery
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,8 @@
1
+ require 'generators/devise/orm_helpers'
2
+
3
+ module Mongoid
4
+ module Generators
5
+ class DeviseUserbinGenerator < Rails::Generators::NamedBase
6
+ end
7
+ end
8
+ end
metadata ADDED
@@ -0,0 +1,136 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: devise_userbin
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Johan Brissmyr
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: devise
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: userbin
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 1.0.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 1.0.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.1.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 1.1.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.9'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.9'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rails
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '3.1'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '3.1'
83
+ description: Devise extension for Userbin. Secure your application with multi-factor
84
+ authentication, user activity monitoring, and real-time threat protection.
85
+ email: johan@userbin.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - app/controllers/devise/devise_userbin_controller.rb
91
+ - app/controllers/devise/security_settings_controller.rb
92
+ - app/controllers/devise/two_factor_authentication_controller.rb
93
+ - app/controllers/devise/two_factor_recovery_controller.rb
94
+ - app/views/devise/two_factor_authentication/show.html.erb
95
+ - app/views/devise/two_factor_recovery/show.html.erb
96
+ - lib/devise_userbin.rb
97
+ - lib/devise_userbin/controllers/helpers.rb
98
+ - lib/devise_userbin/controllers/view_helpers.rb
99
+ - lib/devise_userbin/hooks.rb
100
+ - lib/devise_userbin/import.rb
101
+ - lib/devise_userbin/model.rb
102
+ - lib/devise_userbin/railtie.rb
103
+ - lib/devise_userbin/routes.rb
104
+ - lib/devise_userbin/version.rb
105
+ - lib/generators/active_record/devise_userbin_generator.rb
106
+ - lib/generators/active_record/templates/migration.rb
107
+ - lib/generators/devise_userbin/devise_userbin_generator.rb
108
+ - lib/generators/devise_userbin/import_generator.rb
109
+ - lib/generators/devise_userbin/install_generator.rb
110
+ - lib/generators/devise_userbin/views_generator.rb
111
+ - lib/generators/mongoid/devise_userbin_generator.rb
112
+ homepage: https://github.com/userbin/devise_userbin
113
+ licenses:
114
+ - MIT
115
+ metadata: {}
116
+ post_install_message:
117
+ rdoc_options: []
118
+ require_paths:
119
+ - lib
120
+ required_ruby_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ required_rubygems_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ requirements: []
131
+ rubyforge_project:
132
+ rubygems_version: 2.2.2
133
+ signing_key:
134
+ specification_version: 4
135
+ summary: Devise extension for Userbin
136
+ test_files: []