devise-stormpath 0.1.1 → 0.2.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.
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