devise_castle 1.0.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: e509c6594a298c779e9e682385bca11b4731ed22
4
+ data.tar.gz: fba122942aeaf4b8a4875284fb65a34d14e507f7
5
+ SHA512:
6
+ metadata.gz: ca5a1d49f20a03317dba378c8017b51d314bc209d49eed7b21b646782325a7661556097aa05a46df143f32bff77c836ee6b1290f9a08ea8122c9f4044c005ca5
7
+ data.tar.gz: 3683b87fbbce2fdb04a9ce8fc090686a0f055d885586f2bf36eb2d5d1dcec9d5eb22c8dcee4b322851dfb89762c009bc29a4a29b3b52ab497deb2263b40e66d3
@@ -0,0 +1,60 @@
1
+ class Devise::DeviseCastleController < DeviseController
2
+ include Devise::Controllers::Helpers
3
+
4
+ before_filter :return_not_found, except: :new
5
+
6
+ before_filter do
7
+ env['castle.skip_authorization'] = true
8
+ end
9
+
10
+ def new
11
+ challenge = castle.challenges.create
12
+ Devise.mappings.keys.flatten.any? do |scope|
13
+ redirect_to send(
14
+ "edit_#{scope}_two_factor_authentication_path", challenge.id)
15
+ end
16
+ end
17
+
18
+ def edit
19
+ @challenge = castle.challenges.find(params[:id])
20
+
21
+ # Prevent "undefined method `errors' for nil:NilClass"
22
+ self.resource = resource_class.new
23
+
24
+ render action: "#{@challenge.delivery_method}/edit"
25
+ end
26
+
27
+ def update
28
+ challenge_id = params.require(:challenge_id)
29
+ code = params.require(:code)
30
+
31
+ begin
32
+ castle.challenges.verify(challenge_id, response: code)
33
+
34
+ castle.trust_device if params[:trust_device]
35
+
36
+ Devise.mappings.keys.flatten.any? do |scope|
37
+ redirect_to after_sign_in_path_for(scope)
38
+ end
39
+ rescue Castle::Error
40
+ sign_out_with_message(:no_retries_remaining, :alert)
41
+ end
42
+ end
43
+
44
+ protected
45
+
46
+ def sign_out_with_message(message, kind = :notice)
47
+ signed_out = sign_out(resource_name)
48
+ set_flash_message kind, message if signed_out
49
+ redirect_to after_sign_out_path_for(resource_name)
50
+ end
51
+
52
+ private
53
+
54
+ def return_not_found
55
+ unless castle.mfa_in_progress?
56
+ redirect_to after_sign_in_path_for(resource_name)
57
+ end
58
+ end
59
+
60
+ end
@@ -0,0 +1,3 @@
1
+ module Devise
2
+ class TwoFactorAuthenticationController < DeviseCastleController; end
3
+ end
@@ -0,0 +1,17 @@
1
+ class DeviseCastle::SessionsController < Devise::SessionsController
2
+ unloadable unless Rails.version =~/^4/
3
+
4
+ protected
5
+
6
+ def auth_options
7
+ # find the username
8
+ key = serialize_options(resource)[:methods].first
9
+ username = sign_in_params[key]
10
+
11
+ # find the user if any
12
+ user = resource_class.find_for_authentication(key => username)
13
+
14
+ # make it available to Warden hooks
15
+ super.merge(username: username, user: user)
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ <h2><%= t "devise.two_factor_authentication.header" %></h2>
2
+
3
+ <p><%= t "devise.two_factor_authentication.edit.authenticator.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.edit.common.code_label") %><br />
8
+ <%= text_field_tag :code %></p>
9
+ <p><%= check_box_tag :trust_device %> <%= label_tag :trust_device %></p>
10
+ <%= hidden_field_tag(:challenge_id, @challenge.id) %>
11
+ <p><%= submit_tag t("devise.two_factor_authentication.edit.common.submit_button") %></p>
12
+ <% end -%>
13
+
14
+ <p><%= t "devise.two_factor_authentication.edit.common.recovery_message" %>
@@ -0,0 +1,17 @@
1
+ <h2><%= t "devise.two_factor_authentication.header" %></h2>
2
+
3
+ <p>
4
+ <%= t "devise.two_factor_authentication.edit.sms.instructions" %>
5
+ <%= link_to t("devise.two_factor_authentication.edit.sms.resend"), send("new_#{resource_name}_two_factor_authentication_path") %>.
6
+ </p>
7
+
8
+ <%= form_tag([resource_name, :two_factor_authentication], :method => :put) do %>
9
+ <%= devise_error_messages! %>
10
+ <p><%= label_tag :code, t("devise.two_factor_authentication.edit.common.code_label") %><br />
11
+ <%= text_field_tag :code %></p>
12
+ <p><%= check_box_tag :trust_device %> <%= label_tag :trust_device %></p>
13
+ <%= hidden_field_tag(:challenge_id, @challenge.id) %>
14
+ <p><%= submit_tag t("devise.two_factor_authentication.edit.common.submit_button") %></p>
15
+ <% end -%>
16
+
17
+ <p><%= t "devise.two_factor_authentication.edit.common.recovery_message" %>
@@ -0,0 +1,14 @@
1
+ <h2><%= t "devise.two_factor_authentication.header" %></h2>
2
+
3
+ <p><%= t "devise.two_factor_authentication.edit.yubikey.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.edit.common.code_label") %><br />
8
+ <%= text_field_tag :code %></p>
9
+ <p><%= check_box_tag :trust_device %> <%= label_tag :trust_device %></p>
10
+ <%= hidden_field_tag(:challenge_id, @challenge.id) %>
11
+ <p><%= submit_tag t("devise.two_factor_authentication.edit.common.submit_button") %></p>
12
+ <% end -%>
13
+
14
+ <p><%= t "devise.two_factor_authentication.edit.common.recovery_message" %>
@@ -0,0 +1,29 @@
1
+ require 'active_support/concern'
2
+ require 'devise'
3
+ require 'devise_castle/hooks'
4
+ require 'devise_castle/routes'
5
+ require 'devise_castle/hooks'
6
+ require 'devise_castle/import'
7
+ require 'devise_castle/mapping'
8
+ require 'castle'
9
+
10
+ module Devise
11
+ mattr_accessor :castle_api_secret
12
+ @@castle_api_secret = ''
13
+ end
14
+
15
+ module DeviseCastle
16
+ module Controllers
17
+ autoload :Helpers, 'devise_castle/controllers/helpers'
18
+ end
19
+ end
20
+
21
+ if defined?(Rails::Railtie)
22
+ require 'devise_castle/railtie'
23
+ Rails::Engine
24
+ end
25
+
26
+ Devise.add_module(:castle,
27
+ :controller => :two_factor_authentication,
28
+ :route => :castle,
29
+ :model => 'devise_castle/model')
@@ -0,0 +1,29 @@
1
+ module DeviseCastle
2
+ module Controllers
3
+ module Helpers
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ rescue_from Castle::UserUnauthorizedError do |error|
8
+ Devise.mappings.keys.flatten.any? do |scope|
9
+ warden.logout(scope)
10
+ throw :warden, :scope => scope, :message => :signed_out
11
+ end
12
+ end
13
+
14
+ rescue_from Castle::ChallengeRequiredError do |error|
15
+ Devise.mappings.keys.flatten.any? do |scope|
16
+ if request.format.present? and request.format.html?
17
+ session["#{scope}_return_to"] = request.path if request.get?
18
+ # todo: doesn't seem to work
19
+ redirect_to send("new_#{scope}_two_factor_authentication_path")
20
+ else
21
+ render nothing: true, status: :unauthorized
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,50 @@
1
+ # Instantiate Castle client on every request
2
+ Warden::Manager.on_request do |warden|
3
+ warden.request.env['castle'] =
4
+ Castle::Client.new(warden.request, warden.cookies)
5
+ end
6
+
7
+ # Track logout.succeeded
8
+ Warden::Manager.before_logout do |record, warden, opts|
9
+ if record.respond_to?(:castle_id)
10
+ castle = warden.request.env['castle']
11
+ castle.logout
12
+ castle.track(user_id: record._castle_id, name: '$logout.succeeded')
13
+ end
14
+ end
15
+
16
+ # Track login.failed
17
+ Warden::Manager.before_failure do |env, opts|
18
+ if opts[:action] == 'unauthenticated' && opts[:username]
19
+
20
+ user_id = if opts[:user].respond_to?(:castle_id)
21
+ opts[:user]._castle_id
22
+ end
23
+
24
+ castle = env['castle']
25
+ castle.track(
26
+ name: '$login.failed',
27
+ user_id: user_id,
28
+ details: {
29
+ '$login' => opts[:username]
30
+ })
31
+ end
32
+ end
33
+
34
+ # Track login.succeeded
35
+ Warden::Manager.after_set_user :except => :fetch do |record, warden, opts|
36
+ if record.respond_to?(:castle_id)
37
+ castle = warden.request.env['castle']
38
+ castle.track(user_id: record._castle_id, name: '$login.succeeded')
39
+ castle.login(record._castle_id, email: record.email)
40
+ end
41
+ end
42
+
43
+ # Continous authentication
44
+ Warden::Manager.after_set_user do |record, warden, opts|
45
+ if record.respond_to?(:castle_id)
46
+ env = warden.request.env
47
+ castle = env['castle']
48
+ castle.authorize! unless env['castle.skip_authorization']
49
+ end
50
+ end
@@ -0,0 +1,85 @@
1
+ module DeviseCastle
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 Castle.config.api_secret.present?
19
+ raise ImportError, "Please add an Castle 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 = Castle::User.import(users: batch)
33
+ rescue Castle::Error => error
34
+ raise ImportError, error.message
35
+ end
36
+
37
+ users.zip(resources).each do |user, resource|
38
+ resource.castle_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("castle_id IS NULL").find_in_batches(:batch_size => batch_size) do |resources|
47
+ resources_for_wire = map_resources_to_castle_format(resources)
48
+ yield(resources_for_wire, resources) unless resources.count.zero?
49
+ end
50
+ elsif mongoid?
51
+ 0.step(resource_class.where(:castle_id => nil).count, batch_size) do |offset|
52
+ resources_for_wire = map_resources_to_castle_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_castle_format(resources)
59
+ resources.map do |resource|
60
+ format = {}
61
+ format[:email] = resource.email unless resource.email.blank?
62
+ format[:id] = resource._castle_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,14 @@
1
+ module DeviseCastle
2
+ module Mapping
3
+ def self.included(base)
4
+ base.alias_method_chain :default_controllers, :castle
5
+ end
6
+
7
+ private
8
+ def default_controllers_with_castle(options)
9
+ options[:controllers] ||= {}
10
+ options[:controllers][:sessions] ||= "devise_castle/sessions"
11
+ default_controllers_without_castle(options)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,52 @@
1
+ module Devise
2
+ module Models
3
+ module Castle
4
+ extend ActiveSupport::Concern
5
+
6
+ ::Castle.api_secret = Devise.castle_api_secret
7
+
8
+ included do
9
+ before_destroy :destroy_castle_user
10
+
11
+ def destroy_castle_user
12
+ castle_user_block do
13
+ ::Castle::User.destroy_existing(id)
14
+ end
15
+ end
16
+
17
+ def castle_user_block
18
+ begin
19
+ yield
20
+ rescue ::Castle::Error
21
+ true
22
+ end
23
+ end
24
+
25
+ # Override this in your Devise model to use a custom identifier
26
+ # for the Castle API:s
27
+ def castle_id
28
+ id
29
+ end
30
+
31
+ # Since the identifier will be used in API routes, it needs to be
32
+ # URI encoded
33
+ def _castle_id
34
+ URI.encode(castle_id.to_s)
35
+ end
36
+ end
37
+
38
+ # Overwrites valid_for_authentication? from Devise::Models::Authenticatable
39
+ # for verifying whether a user is allowed to sign in or not.
40
+ def valid_for_authentication?
41
+ return super unless persisted?
42
+
43
+ if super
44
+ true
45
+ else
46
+ # TODO: track unsuccessful login
47
+ false
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,11 @@
1
+ module DeviseCastle
2
+ class Engine < ::Rails::Engine
3
+ ActiveSupport.on_load(:action_controller) do
4
+ include DeviseCastle::Controllers::Helpers
5
+ end
6
+
7
+ config.after_initialize do
8
+ Devise::Mapping.send :include, DeviseCastle::Mapping
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ module ActionDispatch::Routing
2
+ class Mapper
3
+ protected
4
+
5
+ def devise_castle(mapping, controllers)
6
+ resources :two_factor_authentication, :only => [:new, :show, :update, :edit], :path => mapping.path_names[:two_factor_authentication], :controller => controllers[:two_factor_authentication]
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module DeviseCastle
2
+ VERSION = "1.0.0".freeze
3
+ end
@@ -0,0 +1,14 @@
1
+ require 'rails/generators/active_record'
2
+
3
+ module ActiveRecord
4
+ module Generators
5
+ class DeviseCastleGenerator < ActiveRecord::Generators::Base
6
+ source_root File.expand_path("../templates", __FILE__)
7
+
8
+ def copy_devise_castle_migration
9
+ migration_template "migration.rb", "db/migrate/add_castle_to_#{table_name}.rb"
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ class AddCastleTo<%= table_name.camelize %> < ActiveRecord::Migration
2
+ def up
3
+ change_table :<%= table_name %> do |t|
4
+ t.string :castle_id
5
+ end
6
+
7
+ add_index :<%= table_name %>, :castle_id, :unique => true
8
+ end
9
+
10
+ def down
11
+ remove_column :<%= table_name %>, :castle_id
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ module DeviseCastle
2
+ module Generators
3
+ class DeviseCastleGenerator < Rails::Generators::NamedBase
4
+ namespace "devise_castle"
5
+
6
+ desc "Add :castle directive in the given model. Also generate migration for ActiveRecord"
7
+
8
+ def inject_devise_castle_content
9
+ path = File.join("app", "models", "#{file_path}.rb")
10
+ if File.exists?(path)
11
+ inject_into_file(path, "castle, :", :after => "devise :")
12
+ end
13
+ end
14
+
15
+ hook_for :orm
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,12 @@
1
+ module DeviseCastle
2
+ module Generators
3
+ class ImportGenerator < Rails::Generators::NamedBase
4
+ desc "Import users to Castle"
5
+
6
+ def import_users_to_castle
7
+ Import.run(resource_class: class_name)
8
+ end
9
+
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,32 @@
1
+ module DeviseCastle
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ source_root File.expand_path("../../templates", __FILE__)
5
+ desc "Add DeviseCastle config variables to the Devise initializer"
6
+ argument :api_secret, :desc => "Your Castle API secret, which can be found on your Castle 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 :castle\n/))
14
+ false
15
+ else
16
+ inject_into_file(devise_initializer_path, :before => " # ==> Mailer Configuration\n") do
17
+ <<-CONTENT
18
+ # ==> Configuration for :castle
19
+ config.castle_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_castle.en.yml"
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,18 @@
1
+ require 'generators/devise/views_generator'
2
+
3
+ module DeviseCastle
4
+ module Generators
5
+ class ViewsGenerator < Rails::Generators::Base
6
+ desc 'Copies all DeviseCastle 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
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,8 @@
1
+ require 'generators/devise/orm_helpers'
2
+
3
+ module Mongoid
4
+ module Generators
5
+ class DeviseCastleGenerator < Rails::Generators::NamedBase
6
+ end
7
+ end
8
+ end
metadata ADDED
@@ -0,0 +1,136 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: devise_castle
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Johan Brissmyr
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-19 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: castle-rb
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 Castle. Secure your authentication stack with real-time
84
+ monitoring, instantly notifying you and your users on potential account hijacks.
85
+ email: johan@castle.io
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - app/controllers/devise/devise_castle_controller.rb
91
+ - app/controllers/devise/two_factor_authentication_controller.rb
92
+ - app/controllers/devise_castle/sessions_controller.rb
93
+ - app/views/devise/two_factor_authentication/authenticator/edit.html.erb
94
+ - app/views/devise/two_factor_authentication/sms/edit.html.erb
95
+ - app/views/devise/two_factor_authentication/yubikey/edit.html.erb
96
+ - lib/devise_castle.rb
97
+ - lib/devise_castle/controllers/helpers.rb
98
+ - lib/devise_castle/hooks.rb
99
+ - lib/devise_castle/import.rb
100
+ - lib/devise_castle/mapping.rb
101
+ - lib/devise_castle/model.rb
102
+ - lib/devise_castle/railtie.rb
103
+ - lib/devise_castle/routes.rb
104
+ - lib/devise_castle/version.rb
105
+ - lib/generators/active_record/devise_castle_generator.rb
106
+ - lib/generators/active_record/templates/migration.rb
107
+ - lib/generators/devise_castle/devise_castle_generator.rb
108
+ - lib/generators/devise_castle/import_generator.rb
109
+ - lib/generators/devise_castle/install_generator.rb
110
+ - lib/generators/devise_castle/views_generator.rb
111
+ - lib/generators/mongoid/devise_castle_generator.rb
112
+ homepage: https://github.com/castle/devise_castle
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.4.3
133
+ signing_key:
134
+ specification_version: 4
135
+ summary: Devise extension for Castle
136
+ test_files: []