devise-stormpath 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -10,3 +10,13 @@ In the Gemfile for your application:
10
10
  In devise model:
11
11
 
12
12
  devise :stormpath_authenticatable
13
+
14
+ Password reset
15
+ --------------
16
+
17
+ Model:
18
+
19
+ devise :stormpath_authenticatable, :stormpath_recoverable
20
+
21
+ Setup Password Reset Workflow at https://api.stormpath.com
22
+ Set Base URL to //<app host>/<devise scope>/password/edit (i.e. https://example.com/users/password/edit)
@@ -0,0 +1,9 @@
1
+ class Devise::StormpathPasswordsController < Devise::PasswordsController
2
+ prepend_before_filter :proxy_stormpath_token
3
+
4
+ protected
5
+
6
+ def proxy_stormpath_token
7
+ params[:reset_password_token] = params[:sptoken]
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ #TODO Customize messages
2
+
3
+ en:
4
+ devise:
5
+ stormpath_passwords:
6
+ send_instructions: 'You will receive an email with instructions about how to reset your password in a few minutes.'
7
+ updated: 'Your password was changed successfully. You are now signed in.'
8
+ updated_not_active: 'Your password was changed successfully.'
9
+ send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes."
10
+ no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided."
@@ -7,6 +7,7 @@ Gem::Specification.new do |s|
7
7
  s.platform = Gem::Platform::RUBY
8
8
  s.summary = 'Devise extension to allow authentication via Stormpath'
9
9
  s.email = 'liquidautumn@gmail.com'
10
+ s.homepage = "http://www.stormpath.com"
10
11
  s.description = s.summary
11
12
  s.author = 'Denis Grankin'
12
13
 
@@ -16,7 +17,7 @@ Gem::Specification.new do |s|
16
17
  s.require_paths = ["lib"]
17
18
 
18
19
  s.add_dependency('devise')
19
- s.add_dependency('stormpath-rails')
20
+ s.add_dependency('stormpath-rails', '~> 0.4.0')
20
21
  s.add_dependency('activesupport')
21
22
 
22
23
  s.add_development_dependency('rake', '~> 10.0.2')
@@ -1,9 +1,11 @@
1
- require 'devise'
2
1
  require 'stormpath-rails'
3
2
 
4
- require 'devise/stormpath/strategy'
3
+ require 'devise'
4
+ require 'devise/stormpath/routes'
5
+ require 'devise/stormpath/rails'
6
+ require 'devise/strategies/stormpath_authenticatable'
5
7
 
6
8
  Warden::Strategies.add(:stormpath_authenticatable, Devise::Strategies::StormpathAuthenticatable)
7
9
 
8
- Devise.add_module(:stormpath_authenticatable,
9
- :route => :session, :strategy => true, :controller => :sessions, :model => 'devise/stormpath/model')
10
+ Devise.add_module(:stormpath_authenticatable, :route => :session, :strategy => true, :controller => :sessions, :model => 'devise/models/stormpath_authenticatable')
11
+ Devise.add_module(:stormpath_recoverable, :route => :password, :controller => :stormpath_passwords, :model => 'devise/models/stormpath_recoverable')
@@ -5,6 +5,10 @@ module Devise
5
5
  module StormpathAuthenticatable
6
6
  extend ActiveSupport::Concern
7
7
 
8
+ def self.required_fields(klass)
9
+ [:current_password, :password, :password_confirmation]
10
+ end
11
+
8
12
  included do
9
13
  attr_accessor :password_confirmation, :password
10
14
  attr_reader :current_password
@@ -0,0 +1,66 @@
1
+ module Devise
2
+ module Models
3
+ module StormpathRecoverable
4
+ extend ActiveSupport::Concern
5
+
6
+ def self.required_fields(klass)
7
+ [:reset_password_token, :password, :password_confirmation]
8
+ end
9
+
10
+ included do
11
+ attr_accessible :reset_password_token, :password_confirmation
12
+ attr_accessor :reset_password_token
13
+ end
14
+
15
+ protected
16
+
17
+ def after_password_reset
18
+ end
19
+
20
+ module ClassMethods
21
+ def send_reset_password_instructions(attributes={})
22
+ begin
23
+ identifier = attributes[:email] || attributes[:username]
24
+ raise RuntimeError.new "Email or username required" unless identifier
25
+ account = ::Stormpath::Rails::Client.send_password_reset_email(identifier)
26
+ return self.where(stormpath_url: account.get_href).first
27
+ rescue RuntimeError => error
28
+ build_invalid_account(attributes, error)
29
+ end
30
+ end
31
+
32
+ def reset_password_by_token(attributes={})
33
+ begin
34
+ validate_attributes(attributes)
35
+ account = ::Stormpath::Rails::Client.verify_password_reset_token(attributes[:reset_password_token])
36
+ account.set_password attributes[:password]
37
+ account.save
38
+ self.where(stormpath_url: account.get_href).first
39
+ rescue RuntimeError => error
40
+ build_invalid_account(attributes, error)
41
+ end
42
+ end
43
+
44
+ def build_invalid_account(attributes, error)
45
+ u = self.new(attributes)
46
+ u.errors[:base] << error.message
47
+ u
48
+ end
49
+
50
+ #TODO work on better error handling and move strings to locales
51
+ def validate_attributes(attributes)
52
+ raise RuntimeError.new "Password reset token required" unless attributes[:reset_password_token]
53
+ raise RuntimeError.new "Password should match confirmation" unless attributes[:password] == attributes[:password_confirmation]
54
+ raise RuntimeError.new "Password required" unless attributes[:password] && !attributes[:password].empty?
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ #mimic default devise recoverable
61
+ class Mapping
62
+ def recoverable?
63
+ @recoverable ||= self.modules.any? { |m| m.to_s =~ /recoverable/ }
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,9 @@
1
+ module Devise
2
+ module Stormpath
3
+ class Engine < Rails::Engine
4
+ initializer "stormpath.params.filter" do |app|
5
+ app.config.filter_parameters << :sptoken
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ ActionDispatch::Routing::Mapper.class_eval do
2
+ protected
3
+
4
+ def devise_password(mapping, controllers)
5
+ resource :password, :only => [:new, :create, :edit, :update],
6
+ :path => mapping.path_names[:password], :controller => controllers[:stormpath_passwords]
7
+ end
8
+ end
@@ -1,5 +1,5 @@
1
1
  module Devise
2
2
  module Stormpath
3
- VERSION = "0.1.1"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
@@ -1,18 +1,15 @@
1
1
  require "spec_helper"
2
- require "devise/stormpath/model"
2
+ require "devise/models/stormpath_authenticatable"
3
3
 
4
- class User
5
- include Devise::Models::StormpathAuthenticatable
6
- end
7
-
8
- class ResourceError < Exception
9
- end
10
4
 
11
5
  describe Devise::Models::StormpathAuthenticatable do
6
+ class User
7
+ include Devise::Models::StormpathAuthenticatable
8
+ end
12
9
 
13
10
  let(:user) { mock("user") }
14
11
 
15
- describe "#authenticate_with_stormpath" do
12
+ describe "::authenticate_with_stormpath" do
16
13
  it "should return user if authenticated on stormpath and local user exists with returned href" do
17
14
  Stormpath::Rails::Client.should_receive(:authenticate_account).with("username", "password").and_return(mock("account", get_href: "user href"))
18
15
  User.should_receive(:where).with(stormpath_url: "user href").and_return([user])
@@ -0,0 +1,42 @@
1
+ require "spec_helper"
2
+ require "devise/models/stormpath_recoverable"
3
+
4
+ describe Devise::Models::StormpathRecoverable do
5
+ class User
6
+ def self.attr_accessible(*args)
7
+ end
8
+
9
+ include Devise::Models::StormpathRecoverable
10
+ end
11
+
12
+ let(:base) { mock("base") }
13
+ let(:errors) { mock("errors", :[] => base) }
14
+ let(:user) { mock("user", errors: errors) }
15
+
16
+ describe "::send_reset_password_instructions" do
17
+ it "should return user if password reset email sent and local user exists with returned href" do
18
+ Stormpath::Rails::Client.should_receive(:send_password_reset_email).with("john@example.com").and_return(mock("account", get_href: "user href"))
19
+ User.should_receive(:where).with(stormpath_url: "user href").and_return([user])
20
+ User.send_reset_password_instructions(email: "john@example.com").should == user
21
+ end
22
+
23
+ it "should return errored user instance if no email provided" do
24
+ Stormpath::Rails::Client.should_receive(:send_password_reset_email).and_raise(ResourceError.new("Error"))
25
+ User.stub(:new).and_return(user)
26
+ base.should_receive(:<<).with("Error")
27
+ User.send_reset_password_instructions(email: "").should == user
28
+ end
29
+ end
30
+
31
+ describe "::reset_password_by_token" do
32
+ let(:account) { mock("account", get_href: "user href") }
33
+
34
+ it "should update password at Stormpath and return user if password reset token is valid" do
35
+ Stormpath::Rails::Client.should_receive(:verify_password_reset_token).with("token").and_return(account)
36
+ account.should_receive(:set_password).with("password")
37
+ account.should_receive(:save)
38
+ User.should_receive(:where).with(stormpath_url: "user href").and_return([user])
39
+ User.reset_password_by_token(reset_password_token: "token", password: "password", password_confirmation: "password").should == user
40
+ end
41
+ end
42
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require "active_support/concern"
2
+
1
3
  Dir["./spec/support/**/*.rb"].sort.each {|f| require f}
2
4
 
3
5
  RSpec.configure do |config|
@@ -0,0 +1,2 @@
1
+ class ResourceError < RuntimeError
2
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devise-stormpath
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-24 00:00:00.000000000 Z
12
+ date: 2013-01-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: devise
@@ -32,17 +32,17 @@ dependencies:
32
32
  requirement: !ruby/object:Gem::Requirement
33
33
  none: false
34
34
  requirements:
35
- - - ! '>='
35
+ - - ~>
36
36
  - !ruby/object:Gem::Version
37
- version: '0'
37
+ version: 0.4.0
38
38
  type: :runtime
39
39
  prerelease: false
40
40
  version_requirements: !ruby/object:Gem::Requirement
41
41
  none: false
42
42
  requirements:
43
- - - ! '>='
43
+ - - ~>
44
44
  - !ruby/object:Gem::Version
45
- version: '0'
45
+ version: 0.4.0
46
46
  - !ruby/object:Gem::Dependency
47
47
  name: activesupport
48
48
  requirement: !ruby/object:Gem::Requirement
@@ -136,15 +136,22 @@ files:
136
136
  - LICENSE
137
137
  - README.md
138
138
  - Rakefile
139
+ - app/controllers/devise/stormpath_passwords_controller.rb
140
+ - config/locales/en.yml
139
141
  - devise-stormpath.gemspec
140
142
  - lib/devise-stormpath.rb
141
- - lib/devise/stormpath/model.rb
142
- - lib/devise/stormpath/strategy.rb
143
+ - lib/devise/models/stormpath_authenticatable.rb
144
+ - lib/devise/models/stormpath_recoverable.rb
145
+ - lib/devise/stormpath/rails.rb
146
+ - lib/devise/stormpath/routes.rb
143
147
  - lib/devise/stormpath/version.rb
144
- - spec/devise/stormpath/model_spec.rb
148
+ - lib/devise/strategies/stormpath_authenticatable.rb
149
+ - spec/devise/models/stormpath_authenticatable_spec.rb
150
+ - spec/devise/models/stormpath_recoverable_spec.rb
145
151
  - spec/spec_helper.rb
146
152
  - spec/support/client.rb
147
- homepage:
153
+ - spec/support/resource_error.rb
154
+ homepage: http://www.stormpath.com
148
155
  licenses: []
149
156
  post_install_message:
150
157
  rdoc_options: []
@@ -156,12 +163,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
156
163
  - - ! '>='
157
164
  - !ruby/object:Gem::Version
158
165
  version: '0'
166
+ segments:
167
+ - 0
168
+ hash: 434371363033819234
159
169
  required_rubygems_version: !ruby/object:Gem::Requirement
160
170
  none: false
161
171
  requirements:
162
172
  - - ! '>='
163
173
  - !ruby/object:Gem::Version
164
174
  version: '0'
175
+ segments:
176
+ - 0
177
+ hash: 434371363033819234
165
178
  requirements: []
166
179
  rubyforge_project:
167
180
  rubygems_version: 1.8.24