sorcery 0.8.6 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sorcery might be problematic. Click here for more details.

Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/.travis.yml +75 -14
  4. data/CHANGELOG.md +23 -1
  5. data/Gemfile +1 -0
  6. data/README.md +137 -86
  7. data/gemfiles/active_record-rails40.gemfile +7 -0
  8. data/gemfiles/active_record-rails41.gemfile +3 -2
  9. data/gemfiles/mongo_mapper-rails40.gemfile +9 -0
  10. data/gemfiles/mongo_mapper-rails41.gemfile +2 -1
  11. data/gemfiles/mongoid-rails40.gemfile +9 -0
  12. data/gemfiles/mongoid-rails41.gemfile +3 -5
  13. data/gemfiles/mongoid3-rails32.gemfile +9 -0
  14. data/lib/generators/sorcery/USAGE +1 -1
  15. data/lib/generators/sorcery/install_generator.rb +19 -5
  16. data/lib/generators/sorcery/templates/initializer.rb +34 -9
  17. data/lib/generators/sorcery/templates/migration/brute_force_protection.rb +3 -1
  18. data/lib/generators/sorcery/templates/migration/core.rb +2 -2
  19. data/lib/generators/sorcery/templates/migration/external.rb +3 -1
  20. data/lib/sorcery.rb +75 -43
  21. data/lib/sorcery/adapters/active_record_adapter.rb +120 -0
  22. data/lib/sorcery/adapters/base_adapter.rb +30 -0
  23. data/lib/sorcery/adapters/data_mapper_adapter.rb +176 -0
  24. data/lib/sorcery/adapters/mongo_mapper_adapter.rb +110 -0
  25. data/lib/sorcery/adapters/mongoid_adapter.rb +97 -0
  26. data/lib/sorcery/controller.rb +5 -64
  27. data/lib/sorcery/controller/config.rb +65 -0
  28. data/lib/sorcery/controller/submodules/activity_logging.rb +16 -21
  29. data/lib/sorcery/controller/submodules/brute_force_protection.rb +6 -6
  30. data/lib/sorcery/controller/submodules/external.rb +8 -28
  31. data/lib/sorcery/controller/submodules/remember_me.rb +4 -4
  32. data/lib/sorcery/controller/submodules/session_timeout.rb +10 -6
  33. data/lib/sorcery/model.rb +43 -175
  34. data/lib/sorcery/model/config.rb +96 -0
  35. data/lib/sorcery/model/submodules/activity_logging.rb +29 -36
  36. data/lib/sorcery/model/submodules/brute_force_protection.rb +21 -37
  37. data/lib/sorcery/model/submodules/external.rb +53 -9
  38. data/lib/sorcery/model/submodules/remember_me.rb +12 -31
  39. data/lib/sorcery/model/submodules/reset_password.rb +21 -39
  40. data/lib/sorcery/model/submodules/user_activation.rb +21 -63
  41. data/lib/sorcery/model/temporary_token.rb +4 -4
  42. data/lib/sorcery/providers/base.rb +11 -0
  43. data/lib/sorcery/providers/facebook.rb +1 -1
  44. data/lib/sorcery/providers/github.rb +1 -1
  45. data/lib/sorcery/providers/google.rb +1 -1
  46. data/lib/sorcery/providers/heroku.rb +57 -0
  47. data/lib/sorcery/providers/jira.rb +77 -0
  48. data/lib/sorcery/providers/linkedin.rb +1 -1
  49. data/lib/sorcery/providers/liveid.rb +1 -1
  50. data/lib/sorcery/providers/salesforce.rb +50 -0
  51. data/lib/sorcery/providers/twitter.rb +1 -1
  52. data/lib/sorcery/providers/vk.rb +6 -4
  53. data/lib/sorcery/providers/xing.rb +1 -1
  54. data/lib/sorcery/test_helpers/internal.rb +7 -3
  55. data/lib/sorcery/test_helpers/rails/controller.rb +5 -1
  56. data/lib/sorcery/version.rb +3 -0
  57. data/sorcery.gemspec +6 -2
  58. data/spec/active_record/user_activity_logging_spec.rb +9 -0
  59. data/spec/controllers/controller_activity_logging_spec.rb +124 -0
  60. data/spec/controllers/controller_brute_force_protection_spec.rb +43 -0
  61. data/spec/{active_record → controllers}/controller_http_basic_auth_spec.rb +14 -11
  62. data/spec/{active_record → controllers}/controller_oauth2_spec.rb +128 -56
  63. data/spec/{active_record → controllers}/controller_oauth_spec.rb +94 -70
  64. data/spec/{active_record → controllers}/controller_remember_me_spec.rb +32 -12
  65. data/spec/{active_record → controllers}/controller_session_timeout_spec.rb +15 -5
  66. data/spec/{shared_examples/controller_shared_examples.rb → controllers/controller_spec.rb} +34 -19
  67. data/spec/{datamapper → data_mapper}/user_activation_spec.rb +1 -1
  68. data/spec/data_mapper/user_activity_logging_spec.rb +14 -0
  69. data/spec/{datamapper → data_mapper}/user_brute_force_protection_spec.rb +1 -1
  70. data/spec/{datamapper → data_mapper}/user_oauth_spec.rb +1 -1
  71. data/spec/{datamapper → data_mapper}/user_remember_me_spec.rb +1 -1
  72. data/spec/{datamapper → data_mapper}/user_reset_password_spec.rb +1 -1
  73. data/spec/{datamapper → data_mapper}/user_spec.rb +1 -1
  74. data/spec/mongoid/user_spec.rb +13 -0
  75. data/spec/orm/active_record.rb +12 -0
  76. data/spec/orm/{datamapper.rb → data_mapper.rb} +16 -2
  77. data/spec/orm/mongo_mapper.rb +0 -1
  78. data/spec/orm/mongoid.rb +4 -0
  79. data/spec/rails_app/app/controllers/sorcery_controller.rb +62 -1
  80. data/spec/rails_app/app/{datamapper → data_mapper}/authentication.rb +0 -0
  81. data/spec/rails_app/app/{datamapper → data_mapper}/user.rb +0 -0
  82. data/spec/rails_app/app/mongo_mapper/user.rb +2 -0
  83. data/spec/rails_app/config/routes.rb +9 -0
  84. data/spec/rails_app/db/migrate/core/20101224223620_create_users.rb +2 -2
  85. data/spec/shared_examples/user_activation_shared_examples.rb +7 -7
  86. data/spec/shared_examples/user_activity_logging_shared_examples.rb +73 -5
  87. data/spec/shared_examples/user_brute_force_protection_shared_examples.rb +127 -9
  88. data/spec/shared_examples/user_oauth_shared_examples.rb +3 -6
  89. data/spec/shared_examples/user_remember_me_shared_examples.rb +6 -3
  90. data/spec/shared_examples/user_reset_password_shared_examples.rb +10 -10
  91. data/spec/shared_examples/user_shared_examples.rb +117 -30
  92. data/spec/spec_helper.rb +7 -22
  93. metadata +36 -58
  94. data/Gemfile.rails4 +0 -22
  95. data/VERSION +0 -1
  96. data/lib/sorcery/model/adapters/active_record.rb +0 -54
  97. data/lib/sorcery/model/adapters/datamapper.rb +0 -123
  98. data/lib/sorcery/model/adapters/mongo_mapper.rb +0 -60
  99. data/lib/sorcery/model/adapters/mongoid.rb +0 -88
  100. data/lib/sorcery/test_helpers/rails.rb +0 -7
  101. data/spec/active_record/controller_activity_logging_spec.rb +0 -29
  102. data/spec/active_record/controller_brute_force_protection_spec.rb +0 -158
  103. data/spec/active_record/controller_spec.rb +0 -8
  104. data/spec/active_record/integration_spec.rb +0 -23
  105. data/spec/datamapper/controller_activity_logging_spec.rb +0 -17
  106. data/spec/datamapper/controller_spec.rb +0 -8
  107. data/spec/datamapper/user_activity_logging_spec.rb +0 -9
  108. data/spec/mongo_mapper/controller_spec.rb +0 -8
  109. data/spec/mongoid/controller_activity_logging_spec.rb +0 -16
  110. data/spec/mongoid/controller_spec.rb +0 -8
  111. data/spec/rails_app/public/404.html +0 -26
  112. data/spec/rails_app/public/422.html +0 -26
  113. data/spec/rails_app/public/500.html +0 -26
  114. data/spec/rails_app/public/favicon.ico +0 -0
  115. data/spec/rails_app/public/images/rails.png +0 -0
  116. data/spec/rails_app/public/javascripts/application.js +0 -2
  117. data/spec/rails_app/public/javascripts/controls.js +0 -965
  118. data/spec/rails_app/public/javascripts/dragdrop.js +0 -974
  119. data/spec/rails_app/public/javascripts/effects.js +0 -1123
  120. data/spec/rails_app/public/javascripts/prototype.js +0 -6001
  121. data/spec/rails_app/public/javascripts/rails.js +0 -175
  122. data/spec/rails_app/public/robots.txt +0 -5
  123. data/spec/rails_app/public/stylesheets/.gitkeep +0 -0
  124. data/spec/shared_examples/controller_activity_logging_shared_examples.rb +0 -125
  125. data/spec/shared_examples/controller_oauth2_shared_examples.rb +0 -52
  126. data/spec/shared_examples/controller_oauth_shared_examples.rb +0 -62
@@ -1,175 +0,0 @@
1
- (function() {
2
- // Technique from Juriy Zaytsev
3
- // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
4
- function isEventSupported(eventName) {
5
- var el = document.createElement('div');
6
- eventName = 'on' + eventName;
7
- var isSupported = (eventName in el);
8
- if (!isSupported) {
9
- el.setAttribute(eventName, 'return;');
10
- isSupported = typeof el[eventName] == 'function';
11
- }
12
- el = null;
13
- return isSupported;
14
- }
15
-
16
- function isForm(element) {
17
- return Object.isElement(element) && element.nodeName.toUpperCase() == 'FORM'
18
- }
19
-
20
- function isInput(element) {
21
- if (Object.isElement(element)) {
22
- var name = element.nodeName.toUpperCase()
23
- return name == 'INPUT' || name == 'SELECT' || name == 'TEXTAREA'
24
- }
25
- else return false
26
- }
27
-
28
- var submitBubbles = isEventSupported('submit'),
29
- changeBubbles = isEventSupported('change')
30
-
31
- if (!submitBubbles || !changeBubbles) {
32
- // augment the Event.Handler class to observe custom events when needed
33
- Event.Handler.prototype.initialize = Event.Handler.prototype.initialize.wrap(
34
- function(init, element, eventName, selector, callback) {
35
- init(element, eventName, selector, callback)
36
- // is the handler being attached to an element that doesn't support this event?
37
- if ( (!submitBubbles && this.eventName == 'submit' && !isForm(this.element)) ||
38
- (!changeBubbles && this.eventName == 'change' && !isInput(this.element)) ) {
39
- // "submit" => "emulated:submit"
40
- this.eventName = 'emulated:' + this.eventName
41
- }
42
- }
43
- )
44
- }
45
-
46
- if (!submitBubbles) {
47
- // discover forms on the page by observing focus events which always bubble
48
- document.on('focusin', 'form', function(focusEvent, form) {
49
- // special handler for the real "submit" event (one-time operation)
50
- if (!form.retrieve('emulated:submit')) {
51
- form.on('submit', function(submitEvent) {
52
- var emulated = form.fire('emulated:submit', submitEvent, true)
53
- // if custom event received preventDefault, cancel the real one too
54
- if (emulated.returnValue === false) submitEvent.preventDefault()
55
- })
56
- form.store('emulated:submit', true)
57
- }
58
- })
59
- }
60
-
61
- if (!changeBubbles) {
62
- // discover form inputs on the page
63
- document.on('focusin', 'input, select, texarea', function(focusEvent, input) {
64
- // special handler for real "change" events
65
- if (!input.retrieve('emulated:change')) {
66
- input.on('change', function(changeEvent) {
67
- input.fire('emulated:change', changeEvent, true)
68
- })
69
- input.store('emulated:change', true)
70
- }
71
- })
72
- }
73
-
74
- function handleRemote(element) {
75
- var method, url, params;
76
-
77
- var event = element.fire("ajax:before");
78
- if (event.stopped) return false;
79
-
80
- if (element.tagName.toLowerCase() === 'form') {
81
- method = element.readAttribute('method') || 'post';
82
- url = element.readAttribute('action');
83
- params = element.serialize();
84
- } else {
85
- method = element.readAttribute('data-method') || 'get';
86
- url = element.readAttribute('href');
87
- params = {};
88
- }
89
-
90
- new Ajax.Request(url, {
91
- method: method,
92
- parameters: params,
93
- evalScripts: true,
94
-
95
- onComplete: function(request) { element.fire("ajax:complete", request); },
96
- onSuccess: function(request) { element.fire("ajax:success", request); },
97
- onFailure: function(request) { element.fire("ajax:failure", request); }
98
- });
99
-
100
- element.fire("ajax:after");
101
- }
102
-
103
- function handleMethod(element) {
104
- var method = element.readAttribute('data-method'),
105
- url = element.readAttribute('href'),
106
- csrf_param = $$('meta[name=csrf-param]')[0],
107
- csrf_token = $$('meta[name=csrf-token]')[0];
108
-
109
- var form = new Element('form', { method: "POST", action: url, style: "display: none;" });
110
- element.parentNode.insert(form);
111
-
112
- if (method !== 'post') {
113
- var field = new Element('input', { type: 'hidden', name: '_method', value: method });
114
- form.insert(field);
115
- }
116
-
117
- if (csrf_param) {
118
- var param = csrf_param.readAttribute('content'),
119
- token = csrf_token.readAttribute('content'),
120
- field = new Element('input', { type: 'hidden', name: param, value: token });
121
- form.insert(field);
122
- }
123
-
124
- form.submit();
125
- }
126
-
127
-
128
- document.on("click", "*[data-confirm]", function(event, element) {
129
- var message = element.readAttribute('data-confirm');
130
- if (!confirm(message)) event.stop();
131
- });
132
-
133
- document.on("click", "a[data-remote]", function(event, element) {
134
- if (event.stopped) return;
135
- handleRemote(element);
136
- event.stop();
137
- });
138
-
139
- document.on("click", "a[data-method]", function(event, element) {
140
- if (event.stopped) return;
141
- handleMethod(element);
142
- event.stop();
143
- });
144
-
145
- document.on("submit", function(event) {
146
- var element = event.findElement(),
147
- message = element.readAttribute('data-confirm');
148
- if (message && !confirm(message)) {
149
- event.stop();
150
- return false;
151
- }
152
-
153
- var inputs = element.select("input[type=submit][data-disable-with]");
154
- inputs.each(function(input) {
155
- input.disabled = true;
156
- input.writeAttribute('data-original-value', input.value);
157
- input.value = input.readAttribute('data-disable-with');
158
- });
159
-
160
- var element = event.findElement("form[data-remote]");
161
- if (element) {
162
- handleRemote(element);
163
- event.stop();
164
- }
165
- });
166
-
167
- document.on("ajax:after", "form", function(event, element) {
168
- var inputs = element.select("input[type=submit][disabled=true][data-disable-with]");
169
- inputs.each(function(input) {
170
- input.value = input.readAttribute('data-original-value');
171
- input.removeAttribute('data-original-value');
172
- input.disabled = false;
173
- });
174
- });
175
- })();
@@ -1,5 +0,0 @@
1
- # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
2
- #
3
- # To ban all spiders from the entire site uncomment the next two lines:
4
- # User-Agent: *
5
- # Disallow: /
@@ -1,125 +0,0 @@
1
- shared_examples_for "controller_activity_logging" do
2
-
3
- before(:all) do
4
- sorcery_reload!([:activity_logging])
5
- end
6
-
7
- specify { expect(subject).to respond_to(:current_users) }
8
- let(:user) { create_new_user }
9
-
10
- before(:each) { user }
11
-
12
- it "'current_users' is empty when no users are logged in" do
13
- expect(subject.current_users.size).to eq 0
14
- end
15
-
16
- it "logs login time on login" do
17
- now = Time.now.in_time_zone
18
- login_user
19
-
20
- expect(user.last_login_at).not_to be_nil
21
- expect(user.last_login_at.utc.to_s).to be >= now.utc.to_s
22
- expect(user.last_login_at.utc.to_s).to be <= (now.utc+2).to_s
23
- end
24
-
25
- it "logs logout time on logout" do
26
- login_user
27
- now = Time.now.in_time_zone
28
- logout_user
29
-
30
- expect(User.last.last_logout_at).not_to be_nil
31
-
32
- expect(User.last.last_logout_at.utc.to_s).to be >= now.utc.to_s
33
- expect(User.last.last_logout_at.utc.to_s).to be <= (now+2).utc.to_s
34
- end
35
-
36
- it "logs last activity time when logged in" do
37
- sorcery_controller_property_set(:register_last_activity_time, true)
38
-
39
- login_user
40
- now = Time.now.in_time_zone
41
- get :some_action
42
-
43
- last_activity_at = User.last.last_activity_at
44
-
45
- expect(last_activity_at).to be_present
46
- expect(last_activity_at.utc.to_s).to be >= now.utc.to_s
47
- expect(last_activity_at.utc.to_s).to be <= (now+2).utc.to_s
48
- end
49
-
50
- it "logs last IP address when logged in" do
51
- login_user
52
- get :some_action
53
-
54
- expect(User.last.last_login_from_ip_address).to eq "0.0.0.0"
55
- end
56
-
57
- it "updates nothing but activity fields" do
58
- original_user_name = User.last.username
59
- login_user
60
- get :some_action_making_a_non_persisted_change_to_the_user
61
-
62
- expect(User.last.username).to eq original_user_name
63
- end
64
-
65
- it "'current_users' holds the user object when 1 user is logged in" do
66
- login_user
67
- get :some_action
68
-
69
- expect(subject.current_users).to match([User.find(user.id)])
70
- end
71
-
72
- it "'current_users' shows all current_users, whether they have logged out before or not." do
73
- user1 = create_new_user({:username => 'gizmo1', :email => "bla1@bla.com", :password => 'secret1'})
74
- login_user(user1)
75
- get :some_action
76
- clear_user_without_logout
77
- user2 = create_new_user({:username => 'gizmo2', :email => "bla2@bla.com", :password => 'secret2'})
78
- login_user(user2)
79
- get :some_action
80
- clear_user_without_logout
81
- user3 = create_new_user({:username => 'gizmo3', :email => "bla3@bla.com", :password => 'secret3'})
82
- login_user(user3)
83
- get :some_action
84
-
85
- expect(subject.current_users.size).to eq 3
86
- expect(subject.current_users[0]).to eq User.find(user1.id)
87
- expect(subject.current_users[1]).to eq User.find(user2.id)
88
- expect(subject.current_users[2]).to eq User.find(user3.id)
89
- end
90
-
91
- it "does not register login time if configured so" do
92
- sorcery_controller_property_set(:register_login_time, false)
93
- now = Time.now.in_time_zone
94
- login_user
95
-
96
- expect(user.last_login_at).to be_nil
97
- end
98
-
99
- it "does not register logout time if configured so" do
100
- sorcery_controller_property_set(:register_logout_time, false)
101
- now = Time.now.in_time_zone
102
- login_user
103
- logout_user
104
-
105
- expect(user.last_logout_at).to be_nil
106
- end
107
-
108
- it "does not register last activity time if configured so" do
109
- sorcery_controller_property_set(:register_last_activity_time, false)
110
- now = Time.now.in_time_zone
111
- login_user
112
- get :some_action
113
-
114
- expect(user.last_activity_at).to be_nil
115
- end
116
-
117
- it "does not register last IP address if configured so" do
118
- sorcery_controller_property_set(:register_last_ip_address, false)
119
- ip_address = "127.0.0.1"
120
- login_user
121
- get :some_action
122
-
123
- expect(user.last_activity_at).to be_nil
124
- end
125
- end
@@ -1,52 +0,0 @@
1
- shared_examples_for 'oauth2_controller' do
2
- describe 'using create_from' do
3
- before(:each) do
4
- stub_all_oauth2_requests!
5
- User.delete_all
6
- Authentication.delete_all
7
- end
8
-
9
- it 'creates a new user' do
10
- sorcery_model_property_set(:authentications_class, Authentication)
11
- sorcery_controller_external_property_set(:facebook, :user_info_mapping, { username: 'name' })
12
-
13
- expect { get :test_create_from_provider, provider: 'facebook' }.to change { User.count }.by 1
14
- expect(User.first.username).to eq 'Noam Ben Ari'
15
- end
16
-
17
- it 'supports nested attributes' do
18
- sorcery_model_property_set(:authentications_class, Authentication)
19
- sorcery_controller_external_property_set(:facebook, :user_info_mapping, { username: 'hometown/name' })
20
-
21
- expect { get :test_create_from_provider, provider: 'facebook' }.to change { User.count }.by(1)
22
- expect(User.first.username).to eq 'Haifa, Israel'
23
- end
24
-
25
- it 'does not crash on missing nested attributes' do
26
- sorcery_model_property_set(:authentications_class, Authentication)
27
- sorcery_controller_external_property_set(:facebook, :user_info_mapping, { username: 'name', created_at: 'does/not/exist' })
28
-
29
- expect { get :test_create_from_provider, provider: 'facebook' }.to change { User.count }.by 1
30
- expect(User.first.username).to eq 'Noam Ben Ari'
31
- expect(User.first.created_at).not_to be_nil
32
- end
33
-
34
- describe 'with a block' do
35
-
36
- before(:each) do
37
- user = User.new(username: 'Noam Ben Ari')
38
- user.save!(validate: false)
39
- user.authentications.create(provider: 'twitter', uid: '456')
40
- end
41
-
42
- it 'does not create user' do
43
- sorcery_model_property_set(:authentications_class, Authentication)
44
- sorcery_controller_external_property_set(:facebook, :user_info_mapping, { username: 'name' })
45
-
46
- # test_create_from_provider_with_block in controller will check for uniqueness of username
47
- expect { get :test_create_from_provider_with_block, provider: 'facebook' }.not_to change { User.count }
48
- end
49
-
50
- end
51
- end
52
- end
@@ -1,62 +0,0 @@
1
- shared_examples_for "oauth_controller" do
2
- describe "using 'create_from'" do
3
- before(:each) do
4
- stub_all_oauth_requests!
5
- User.delete_all
6
- Authentication.delete_all
7
- end
8
-
9
- it "creates a new user" do
10
- sorcery_model_property_set(:authentications_class, Authentication)
11
- sorcery_controller_external_property_set(:twitter, :user_info_mapping, {:username => "screen_name"})
12
-
13
- expect { get :test_create_from_provider, :provider => "twitter" }.to change { User.count }.by 1
14
- expect(User.first.username).to eq "nbenari"
15
- end
16
-
17
- it "supports nested attributes" do
18
- sorcery_model_property_set(:authentications_class, Authentication)
19
- sorcery_controller_external_property_set(:twitter, :user_info_mapping, {:username => "status/text"})
20
-
21
- expect { get :test_create_from_provider, :provider => "twitter" }.to change { User.count }.by 1
22
- expect(User.first.username).to eq "coming soon to sorcery gem: twitter and facebook authentication support."
23
- end
24
-
25
- it "does not crash on missing nested attributes" do
26
- sorcery_model_property_set(:authentications_class, Authentication)
27
- sorcery_controller_external_property_set(:twitter, :user_info_mapping, {:username => "status/text", :created_at => "does/not/exist"})
28
-
29
- expect { get :test_create_from_provider, :provider => "twitter" }.to change { User.count }.by 1
30
- expect(User.first.username).to eq "coming soon to sorcery gem: twitter and facebook authentication support."
31
- expect(User.first.created_at).not_to be_nil
32
- end
33
-
34
- it "binds new provider" do
35
- sorcery_model_property_set(:authentications_class, UserProvider)
36
-
37
- current_user = custom_create_new_external_user(:facebook, UserProvider)
38
- login_user(current_user)
39
-
40
- expect { get :test_add_second_provider, :provider => "twitter" }.to change { UserProvider.count }.by 1
41
- expect(UserProvider.where(:user_id => current_user.id).size).to eq 2
42
- expect(User.count).to eq 1
43
- end
44
-
45
- describe "with a block" do
46
-
47
- before(:each) do
48
- user = User.new(:username => 'nbenari')
49
- user.save!(:validate => false)
50
- user.authentications.create(:provider => 'github', :uid => '456')
51
- end
52
-
53
- it "does not create user" do
54
- sorcery_model_property_set(:authentications_class, Authentication)
55
- sorcery_controller_external_property_set(:twitter, :user_info_mapping, {:username => "screen_name"})
56
-
57
- expect { get :test_create_from_provider_with_block, :provider => "twitter" }.not_to change { User.count }
58
- end
59
-
60
- end
61
- end
62
- end