social_auth 0.0.9

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.
Files changed (85) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +39 -0
  4. data/app/models/social_auth/facebook_service.rb +60 -0
  5. data/app/models/social_auth/google_plus_service.rb +106 -0
  6. data/app/models/social_auth/service.rb +152 -0
  7. data/app/models/social_auth/twitter_service.rb +67 -0
  8. data/config/routes.rb +2 -0
  9. data/lib/generators/social_auth/install/install_generator.rb +37 -0
  10. data/lib/generators/social_auth/install/templates/create_social_auth_services.rb +12 -0
  11. data/lib/generators/social_auth/install/templates/initializer.rb +15 -0
  12. data/lib/social_auth.rb +73 -0
  13. data/lib/social_auth/acts_as_social_user.rb +23 -0
  14. data/lib/social_auth/engine.rb +12 -0
  15. data/lib/social_auth/railtie.rb +7 -0
  16. data/lib/social_auth/version.rb +3 -0
  17. data/lib/tasks/social_auth_tasks.rake +4 -0
  18. data/spec/dummy/rails-4.2.0/Gemfile +42 -0
  19. data/spec/dummy/rails-4.2.0/README.rdoc +28 -0
  20. data/spec/dummy/rails-4.2.0/Rakefile +6 -0
  21. data/spec/dummy/rails-4.2.0/app/assets/javascripts/application.js +16 -0
  22. data/spec/dummy/rails-4.2.0/app/assets/stylesheets/application.css +15 -0
  23. data/spec/dummy/rails-4.2.0/app/controllers/application_controller.rb +5 -0
  24. data/spec/dummy/rails-4.2.0/app/helpers/application_helper.rb +2 -0
  25. data/spec/dummy/rails-4.2.0/app/models/user.rb +2 -0
  26. data/spec/dummy/rails-4.2.0/app/views/layouts/application.html.erb +14 -0
  27. data/spec/dummy/rails-4.2.0/bin/bundle +3 -0
  28. data/spec/dummy/rails-4.2.0/bin/rails +4 -0
  29. data/spec/dummy/rails-4.2.0/bin/rake +4 -0
  30. data/spec/dummy/rails-4.2.0/bin/setup +29 -0
  31. data/spec/dummy/rails-4.2.0/config.ru +4 -0
  32. data/spec/dummy/rails-4.2.0/config/application.rb +26 -0
  33. data/spec/dummy/rails-4.2.0/config/boot.rb +3 -0
  34. data/spec/dummy/rails-4.2.0/config/database.yml +15 -0
  35. data/spec/dummy/rails-4.2.0/config/environment.rb +5 -0
  36. data/spec/dummy/rails-4.2.0/config/environments/development.rb +41 -0
  37. data/spec/dummy/rails-4.2.0/config/environments/production.rb +79 -0
  38. data/spec/dummy/rails-4.2.0/config/environments/test.rb +42 -0
  39. data/spec/dummy/rails-4.2.0/config/initializers/assets.rb +11 -0
  40. data/spec/dummy/rails-4.2.0/config/initializers/backtrace_silencers.rb +7 -0
  41. data/spec/dummy/rails-4.2.0/config/initializers/cookies_serializer.rb +3 -0
  42. data/spec/dummy/rails-4.2.0/config/initializers/filter_parameter_logging.rb +4 -0
  43. data/spec/dummy/rails-4.2.0/config/initializers/inflections.rb +16 -0
  44. data/spec/dummy/rails-4.2.0/config/initializers/mime_types.rb +4 -0
  45. data/spec/dummy/rails-4.2.0/config/initializers/session_store.rb +3 -0
  46. data/spec/dummy/rails-4.2.0/config/initializers/social_auth.rb +15 -0
  47. data/spec/dummy/rails-4.2.0/config/initializers/wrap_parameters.rb +14 -0
  48. data/spec/dummy/rails-4.2.0/config/locales/en.yml +23 -0
  49. data/spec/dummy/rails-4.2.0/config/routes.rb +56 -0
  50. data/spec/dummy/rails-4.2.0/config/secrets.yml +22 -0
  51. data/spec/dummy/rails-4.2.0/db/migrate/20150504044515878_create_social_auth_services.rb +12 -0
  52. data/spec/dummy/rails-4.2.0/db/migrate/20150504044519_create_users.rb +9 -0
  53. data/spec/dummy/rails-4.2.0/db/schema.rb +35 -0
  54. data/spec/dummy/rails-4.2.0/db/seeds.rb +7 -0
  55. data/spec/dummy/rails-4.2.0/log/test.log +2570 -0
  56. data/spec/dummy/rails-4.2.0/public/404.html +67 -0
  57. data/spec/dummy/rails-4.2.0/public/422.html +67 -0
  58. data/spec/dummy/rails-4.2.0/public/500.html +66 -0
  59. data/spec/dummy/rails-4.2.0/public/favicon.ico +0 -0
  60. data/spec/dummy/rails-4.2.0/public/robots.txt +5 -0
  61. data/spec/dummy/rails-4.2.0/test/fixtures/users.yml +7 -0
  62. data/spec/dummy/rails-4.2.0/test/models/user_test.rb +7 -0
  63. data/spec/dummy/rails-4.2.0/test/test_helper.rb +10 -0
  64. data/spec/fixtures/vcr_cassettes/facebook_service/invalid_friends_request.yml +54 -0
  65. data/spec/fixtures/vcr_cassettes/facebook_service/invalid_request.yml +50 -0
  66. data/spec/fixtures/vcr_cassettes/facebook_service/invalid_token.yml +50 -0
  67. data/spec/fixtures/vcr_cassettes/facebook_service/valid_friends_request.yml +110 -0
  68. data/spec/fixtures/vcr_cassettes/facebook_service/valid_request.yml +55 -0
  69. data/spec/fixtures/vcr_cassettes/google_plus_service/invalid_authorization.yml +51 -0
  70. data/spec/fixtures/vcr_cassettes/google_plus_service/invalid_friends_request.yml +53 -0
  71. data/spec/fixtures/vcr_cassettes/google_plus_service/invalid_token.yml +51 -0
  72. data/spec/fixtures/vcr_cassettes/google_plus_service/valid_authorization.yml +56 -0
  73. data/spec/fixtures/vcr_cassettes/google_plus_service/valid_friends_request.yml +132 -0
  74. data/spec/fixtures/vcr_cassettes/google_plus_service/valid_request.yml +110 -0
  75. data/spec/fixtures/vcr_cassettes/twitter_service/invalid_friends_request.yml +47 -0
  76. data/spec/fixtures/vcr_cassettes/twitter_service/valid_friends_request.yml +159 -0
  77. data/spec/fixtures/vcr_cassettes/twitter_service/valid_request.yml +337 -0
  78. data/spec/models/facebook_service_spec.rb +127 -0
  79. data/spec/models/google_plus_service_spec.rb +133 -0
  80. data/spec/models/service_spec.rb +236 -0
  81. data/spec/models/twitter_service_spec.rb +100 -0
  82. data/spec/spec_helper.rb +74 -0
  83. data/spec/support/database.yml +15 -0
  84. data/spec/support/rails_template.rb +15 -0
  85. metadata +376 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7b73fa75b8a6c8200dfa7dd06516c356ed7bc6fa
4
+ data.tar.gz: 3df34c0d057b106e60658529905ad5b5d6a3258f
5
+ SHA512:
6
+ metadata.gz: 1806f1a18ece05d1522f280a6746b675384708be1933bc87ac940c20f2eefb129af1ae11bb4c8f98272cfb5afbd02cb3fe8bbe4ea20aa4b39fe4e1fc024cd765
7
+ data.tar.gz: 6348918e0a59e0aced64adbb948fc72ca3d86f8658e38a5fa28aa932aee3a1eedd3a1dda83b621c34958e9ef67bb77c2ee000ef21eff00ebc57b4c4572d756b9
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2015 William Porter
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,39 @@
1
+ require "bundler"
2
+ require 'rake'
3
+ Bundler.setup
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ def cmd(command)
7
+ puts command
8
+ raise unless system command
9
+ end
10
+
11
+ # Import all our rake tasks
12
+ FileList['tasks/**/*.rake'].each { |task| import task }
13
+
14
+ # begin
15
+ # require 'bundler/setup'
16
+ # rescue LoadError
17
+ # puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
18
+ # end
19
+
20
+ # require 'rdoc/task'
21
+
22
+ # RDoc::Task.new(:rdoc) do |rdoc|
23
+ # rdoc.rdoc_dir = 'rdoc'
24
+ # rdoc.title = 'SocialAuth'
25
+ # rdoc.options << '--line-numbers'
26
+ # rdoc.rdoc_files.include('README.rdoc')
27
+ # rdoc.rdoc_files.include('lib/**/*.rb')
28
+ # end
29
+
30
+ # APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
31
+ # load 'rails/tasks/engine.rake'
32
+
33
+
34
+ # load 'rails/tasks/statistics.rake'
35
+
36
+
37
+
38
+ # Bundler::GemHelper.install_tasks
39
+
@@ -0,0 +1,60 @@
1
+ require 'fb_graph2'
2
+
3
+ module SocialAuth
4
+ class FacebookService < Service
5
+ def name
6
+ "Facebook"
7
+ end
8
+
9
+ def self.init_with(auth_token={})
10
+ request = create_connection(auth_token)
11
+
12
+ return create_with_request(
13
+ request.id,
14
+ User.create_with_facebook_request(request),
15
+ "Authenticated",
16
+ {access_token: request.access_token}
17
+ )
18
+ end
19
+
20
+ def self.connect_with(user, auth_token={}, method="Connected")
21
+ request = create_connection(auth_token)
22
+
23
+ return create_with_request(
24
+ request.id,
25
+ user,
26
+ method,
27
+ {access_token: request.access_token}
28
+ )
29
+ end
30
+
31
+ def self.create_connection(auth_token={})
32
+ fb_user = FbGraph2::User.me(auth_token[:access_token])
33
+ fb_user.fetch
34
+
35
+ rescue FbGraph2::Exception::InvalidToken => e
36
+ raise InvalidToken.new(e.message)
37
+ rescue FbGraph2::Exception::BadRequest => e
38
+ raise BadRequest.new(e.message)
39
+ end
40
+
41
+ def friend_ids
42
+ if redis_instance.exists(redis_key(:friends))
43
+ friend_ids = redis_instance.smembers(redis_key(:friends))
44
+ else
45
+ friend_ids = self.class.create_connection(access_token).friends.map(&:id)
46
+ unless friend_ids.empty?
47
+ redis_instance.del(redis_key(:friends))
48
+ redis_instance.sadd(redis_key(:friends), friend_ids)
49
+ redis_instance.expire(redis_key(:friends), REDIS_CACHE)
50
+ end
51
+ end
52
+ friend_ids
53
+
54
+ rescue InvalidToken => e
55
+ disconnect(e)
56
+ return []
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,106 @@
1
+ require 'google_plus'
2
+ require 'typhoeus'
3
+
4
+ module SocialAuth
5
+ class GooglePlusService < Service
6
+
7
+ def name
8
+ "Google Plus"
9
+ end
10
+
11
+ def self.init_with(auth_token={})
12
+ access_token = fetch_access_token(auth_token)
13
+ request = create_connection(access_token).get('me')
14
+
15
+ return create_with_request(
16
+ request.id,
17
+ User.create_with_google_plus_request(request),
18
+ "Authenticated",
19
+ {refresh_token: access_token[:refresh_token]}
20
+ )
21
+ rescue GooglePlus::RequestError => e
22
+ raise InvalidToken.new(e.message)
23
+ end
24
+
25
+ def self.connect_with(user, auth_token={}, method="Connected")
26
+ access_token = fetch_access_token(auth_token)
27
+ request = create_connection(access_token).get('me')
28
+
29
+ return create_with_request(
30
+ request.id,
31
+ user,
32
+ method,
33
+ {refresh_token: access_token[:refresh_token]}
34
+ )
35
+ rescue GooglePlus::RequestError => e
36
+ raise InvalidToken.new(e.message)
37
+ end
38
+
39
+ def self.fetch_access_token(auth_token={})
40
+ params = {
41
+ client_id: SocialAuth.google_client_id,
42
+ client_secret: SocialAuth.google_client_secret,
43
+ redirect_uri: SocialAuth.google_redirect_uri
44
+ }
45
+ if auth_token[:auth_token].present?
46
+ params[:code] = auth_token[:auth_token]
47
+ params[:grant_type] = "authorization_code"
48
+ request = Typhoeus::Request.new(
49
+ "https://www.googleapis.com/oauth2/v3/token",
50
+ method: :post,
51
+ params: params
52
+ )
53
+ else
54
+ params[:refresh_token] = auth_token[:refresh_token]
55
+ params[:grant_type] = "refresh_token"
56
+ request = Typhoeus::Request.new(
57
+ "https://www.googleapis.com/oauth2/v3/token",
58
+ method: :post,
59
+ params: params
60
+ )
61
+ end
62
+
63
+ request.on_complete do |response|
64
+ body = JSON.parse(response.body).with_indifferent_access
65
+ if response.success?
66
+ return body
67
+ else
68
+ raise InvalidToken.new(body[:error_description])
69
+ end
70
+ end
71
+
72
+ request.run
73
+ end
74
+
75
+ def self.create_connection(auth_token={})
76
+ GooglePlus.api_key = SocialAuth.google_api_key
77
+ GooglePlus.access_token = auth_token[:access_token]
78
+ GooglePlus::Person
79
+ end
80
+
81
+ def google_items
82
+ self.class.create_connection(self.class.fetch_access_token(access_token)).list.items
83
+ end
84
+
85
+ def friend_ids
86
+ if redis_instance.exists(redis_key(:friends))
87
+ friend_ids = redis_instance.smembers(redis_key(:friends))
88
+ else
89
+ items = google_items
90
+ friend_ids = items.map(&:id) if items.present?
91
+ if friend_ids.present?
92
+ redis_instance.del(redis_key(:friends))
93
+ redis_instance.sadd(redis_key(:friends), friend_ids)
94
+ redis_instance.expire(redis_key(:friends), REDIS_CACHE)
95
+ else
96
+ return []
97
+ end
98
+ end
99
+ friend_ids
100
+
101
+ rescue InvalidToken => e
102
+ disconnect
103
+ return []
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,152 @@
1
+ module SocialAuth
2
+ class Service < ActiveRecord::Base
3
+ #validations
4
+ validates_presence_of :user, :access_token, :remote_id, :method
5
+ validates_uniqueness_of :remote_id, scope: [:type], :if => lambda { |service| service.method == 'Authenticated' }
6
+ validates_uniqueness_of :remote_id, scope: [:type, :user_id], :if => lambda { |service| service.method == 'Connected' }
7
+
8
+ before_validation :validate_methods
9
+
10
+ #relations
11
+ belongs_to :user
12
+
13
+ #settings
14
+ self.table_name = "social_auth_services"
15
+
16
+ #callbacks
17
+ after_create :append_to_associated_services
18
+
19
+ ACCEPTED_METHODS = %w(Authenticated Connected)
20
+ REDIS_CACHE = 2_592_000 # cache expiry in seconds
21
+
22
+ def name
23
+ raise "need to implement"
24
+ end
25
+
26
+ def access_token
27
+ if super.blank?
28
+ {}
29
+ else
30
+ super.with_indifferent_access
31
+ end
32
+ end
33
+
34
+ def self.init_with(auth_token)
35
+ raise "need to override"
36
+ end
37
+
38
+ def self.connect_with(user, auth_token)
39
+ raise "need to override"
40
+ end
41
+
42
+ def self.create_with_request(remote_id, user, method="Connected", access_token={})
43
+ remote_id = remote_id.to_s
44
+
45
+ unless (service = find_by_remote_id_and_method(remote_id, method)) && method == "Authenticated"
46
+ #attempts to look if some other user connected this same facebook account if its an authentication request
47
+ if count = where(remote_id: remote_id, method: "Connected").count == 1 and method == "Authenticated"
48
+ service = find_by_remote_id_and_method(remote_id, "Connected")
49
+ else
50
+ service = new
51
+ service.remote_id = remote_id
52
+
53
+ #gives the owner one last chance to perform some app level logic on the user before being created
54
+ user = user.validate_existing_user(remote_id, service.type) if user.respond_to?(:validate_existing_user)
55
+ service.user = user
56
+ service.method = method
57
+ end
58
+ end
59
+
60
+ service.access_token = access_token
61
+ service.save
62
+
63
+ return service
64
+ end
65
+
66
+ def services
67
+ self.class.where('remote_id IN (?)', friend_ids)
68
+ end
69
+
70
+ def self.append_to_associated_services(id)
71
+ service = find(id)
72
+ service.services.each do |s|
73
+ s.append_to_friends_list(service)
74
+ end
75
+ end
76
+
77
+ def append_to_friends_list(service)
78
+ if redis_instance.exists(self.redis_key(:friends))
79
+ redis_instance.sadd(self.redis_key(:friends), service.remote_id)
80
+ end
81
+ user.friend_joined_the_app_callback(service.user) if user.respond_to?(:friend_joined_the_app_callback)
82
+ end
83
+
84
+ def friend_ids
85
+ if redis_instance.exists(redis_key(:friends))
86
+ friend_ids = redis_instance.smembers(redis_key(:friends))
87
+ else
88
+ []
89
+ end
90
+ end
91
+
92
+ def self.disconnect_user(user)
93
+ service = find_by_user_id(user.id)
94
+ if service
95
+ raise Error.new("Cannot disconnect a service you used to authenticate with") if service.authenticated?
96
+
97
+ service.disconnect(nil, false)
98
+ else
99
+ raise ServiceDoesNotExist.new("Couldn't find service for this user")
100
+ end
101
+ end
102
+
103
+ def disconnect(e=nil, callback=true)
104
+ if connected?
105
+ #destroys service
106
+ self.destroy
107
+ #notifies the user that their service is about to be disconnected
108
+ user.service_disconnected_callback(self) if user.respond_to?(:service_disconnected_callback) and callback
109
+ else
110
+ #re_raises the exception
111
+ raise InvalidToken.new(e ? e.message : "Token has become invalid") #move to localization file
112
+ end
113
+ end
114
+
115
+ # helper method to generate redis keys
116
+ def redis_key(str)
117
+ "#{type}:#{id}:#{str}"
118
+ end
119
+
120
+ def redis_instance
121
+ $redis #need to change out for an configurable var
122
+ end
123
+
124
+ def authenticated?
125
+ return true if method == 'Authenticated'
126
+ false
127
+ end
128
+
129
+ def connected?
130
+ return true if method == 'Connected'
131
+ false
132
+ end
133
+
134
+ private
135
+
136
+ def validate_methods
137
+ errors.add(:method, 'not an accepted option') unless ACCEPTED_METHODS.include?(method)
138
+ end
139
+
140
+ def append_to_associated_services
141
+ self.class.delay.append_to_associated_services(self.id)
142
+ end
143
+
144
+ end
145
+
146
+ #exceptions
147
+ class InvalidToken < StandardError ; end
148
+ class BadRequest < StandardError ; end
149
+ class ServiceDoesNotExist < StandardError ; end
150
+ class Error < StandardError ; end
151
+
152
+ end
@@ -0,0 +1,67 @@
1
+ require 'twitter'
2
+
3
+ module SocialAuth
4
+ class TwitterService < Service
5
+
6
+ def name
7
+ "Twitter"
8
+ end
9
+
10
+ def self.init_with(auth_token={})
11
+ request = create_connection(auth_token)
12
+
13
+ return create_with_request(
14
+ request.user.id,
15
+ User.create_with_twitter_request(request.user),
16
+ "Authenticated",
17
+ {access_token: request.access_token, access_token_secret: request.access_token_secret}
18
+ )
19
+
20
+ rescue Twitter::Error::Unauthorized => e
21
+ raise InvalidToken.new(e.message)
22
+ end
23
+
24
+ def self.connect_with(user, auth_token={}, method="Connected")
25
+ request = create_connection(auth_token)
26
+
27
+ return create_with_request(
28
+ request.user.id,
29
+ user,
30
+ method,
31
+ {access_token: request.access_token, access_token_secret: request.access_token_secret}
32
+ )
33
+
34
+ rescue Twitter::Error::Unauthorized => e
35
+ raise InvalidToken.new(e.message)
36
+ end
37
+
38
+ def self.create_connection(auth_token={})
39
+ Twitter::REST::Client.new do |config|
40
+ config.consumer_key = SocialAuth.twitter_consumer_key
41
+ config.consumer_secret = SocialAuth.twitter_consumer_secret
42
+ config.access_token = auth_token[:access_token]
43
+ config.access_token_secret = auth_token[:access_token_secret]
44
+ end
45
+ #the reason why we don't catch any exceptions here is because it only initializes the connection no
46
+ #requests are actually made here
47
+ end
48
+
49
+ def friend_ids
50
+ if redis_instance.exists(redis_key(:friends))
51
+ friend_ids = redis_instance.smembers(redis_key(:friends))
52
+ else
53
+ friend_ids = self.class.create_connection(access_token).friend_ids.to_hash[:ids].map(&:to_s)
54
+ unless friend_ids.empty?
55
+ redis_instance.del(redis_key(:friends))
56
+ redis_instance.sadd(redis_key(:friends), friend_ids.to_s)
57
+ redis_instance.expire(redis_key(:friends), REDIS_CACHE)
58
+ end
59
+ end
60
+ friend_ids
61
+ rescue Twitter::Error::Unauthorized => e
62
+ disconnect
63
+ return []
64
+ end
65
+
66
+ end
67
+ end