fix twitter users from shapado <4.0
[shapado:cantonics-shapado.git] / lib / multiauth_support.rb
1 module MultiauthSupport
2   extend ActiveSupport::Concern
3
4   included do
5     field :using_openid, :type => Boolean, :default => false
6     field :openid_email
7
8     field :twitter_handle, :type => String
9     field :twitter_oauth_token, :type => String
10     field :twitter_oauth_secret, :type => String
11
12     field :facebook_id,               :type => String
13     field :facebook_token,            :type => String
14     field :facebook_profile,          :type => String
15
16     field :twitter_token,             :type => String
17     field :twitter_secret,            :type => String
18     field :twitter_login,             :type => String
19     field :twitter_id,                :type => String
20
21     field :identica_token,             :type => String
22     field :identica_secret,            :type => String
23     field :identica_login,             :type => String
24     field :identica_id,                :type => String
25
26     field :github_id, :type => String
27     field :github_login, :type => String
28
29     field :auth_keys, :type => Array, :default => []
30     field :user_info, :type => Hash, :default => {}
31   end
32
33   module ClassMethods
34     def authenticate(fields)
35       puts "FIELDS #{fields.inspect}"
36
37       provider = fields["provider"]
38
39       if fields["uid"] =~ %r{google\.com/accounts/o8/} && fields["user_info"]["email"]
40         fields["uid"] = "http://google_id_#{fields["user_info"]["email"]}" # normalize for subdomains
41       end
42
43       uid = fields["uid"] || fields["extra"]["user_hash"]["id"]
44       auth_key = "#{provider}_#{uid}"
45
46       user = User.where({:auth_keys => auth_key}).first
47       if user.nil?
48         user = User.new(:auth_keys => [auth_key])
49
50         puts ">>>>>>> #{provider} #{fields["user_info"].inspect}"
51
52         user.user_info[provider] = fields["user_info"]
53
54         if user.email.blank?
55           user.email = user.user_info[provider]["email"]
56         end
57
58         user.send("handle_#{provider}", fields) if user.respond_to?("handle_#{provider}", true)
59
60         if user.login.blank?
61           if user.email.blank?
62             user.login = user.user_info[provider]["nickname"] || user.user_info[provider]["login"] || user.user_info[provider]["name"] || "#{provider}_#{rand(100)}#{rand(100)}#{rand(100)}"
63           else
64             user.login = user.email.split("@").first.downcase.gsub(".","")
65           end
66         end
67
68         if !user.valid? && !user.errors.on(:login).empty?
69           user.login = user.login + "_#{rand(100)}#{rand(100)}#{rand(100)}"
70         end
71
72         return false if !user.save
73       end
74       if provider == 'twitter' && user.user_info["twitter"] && user.user_info["twitter"]["old"]
75         user.user_info["twitter"] = fields["user_info"]
76         user.save(:validate => false)
77       end
78       user
79     end
80   end # ClassMethods
81
82   module InstanceMethods
83     def connect(fields)
84       provider = fields["provider"]
85       if fields["uid"] =~ %r{google\.com/accounts/o8/} && fields["user_info"]["email"]
86         fields["uid"] = "http://google_id_#{fields["user_info"]["email"]}" # normalize for subdomains
87       end
88
89       auth_key = "#{provider}_#{fields["uid"]}"
90       user = User.only(:id).where({:auth_keys => auth_key}).first
91       if user.present? && user.id != self.id
92         self.push(:"user_info.#{provider}" => fields["user_info"])
93
94         user.destroy if merge_user(user)
95       end
96
97       user.send("handle_#{provider}", fields) if user.respond_to?("handle_#{provider}", true)
98
99       self.push_uniq(:auth_keys => auth_key)
100     end
101
102     def merge_user(user)
103       #TODO merge friendlist, facebook friend lists and maybe more
104       #TODO merging is broken
105       [Question, Answer, Badge, UserStat].each do |m|
106         m.override({:user_id => user.id}, {:user_id => self.id})
107       end
108       if !self.facebook_login? && user.facebook_login?
109         self.facebook_friend_list.destroy &&
110         FacebookFriendList.override({:user_id => user.id}, {:user_id => self.id})
111         #self.update({ :facebook_id => user.facebook_id, :facebook_token => user.facebook_token })
112       end
113       if !self.twitter_login? && user.twitter_login?
114         self.twitter_friend_list.destroy &&
115         TwitterFriendList.override({:user_id => user.id}, {:user_id => self.id})
116         #self.update({ :twitter_id => user.twitter_id, :twitter_token => user.twitter_token,
117         #              :twitter_secret => user.twitter_secret, :twitter_login => user.twitter_login})
118       end
119       if !self.identica_login? && user.identica_login?
120         self.identica_friend_list.destroy &&
121         IdenticaFriendList.override({:user_id => user.id}, {:user_id => self.id})
122         #self.update({ :twitter_id => user.twitter_id, :twitter_token => user.twitter_token,
123         #              :twitter_secret => user.twitter_secret, :twitter_login => user.twitter_login})
124       end
125       user
126     end
127
128     def password_required?
129       return false if self[:using_openid] || self[:facebook_id].present? || self[:github_id].present?
130
131       (encrypted_password.blank? || !password.blank?)
132     end
133
134     def twitter_client
135       if self.twitter_secret.present? && self.twitter_token.present? && (config = Multiauth.providers["Twitter"])
136         TwitterOAuth::Client.new(
137           :consumer_key => config["id"],
138           :consumer_secret => config["token"],
139           :token => self.twitter_token,
140           :secret => self.twitter_secret
141         )
142       end
143     end
144
145     def facebook_client(property = 'friends')
146       response = open(URI.encode("https://graph.facebook.com/#{self.facebook_id}/#{property}?access_token=#{self.facebook_token}")).read
147       JSON.parse(response)
148     end
149
150     def identica_client
151       config = Multiauth.providers["Identica"]
152       @consumer = OAuth::Consumer.new(config["id"], config["token"], {:site=>'http://identi.ca'})
153       @accesstoken = OAuth::AccessToken.new(@consumer, self.identica_token, self.identica_secret)
154     end
155
156     def get_identica_friends
157       JSON.parse(identica_client.get('/api/friends/ids.json').body)
158     end
159
160     private
161     # {"provider"=>"facebook", "uid"=>"4332432432432", "credentials"=>{"token"=>"432432432432432"},
162     # "user_info"=>{"nickname"=>"profile.php?id=4332432432432", "first_name"=>"My", "last_name"=>"Name", "name"=>"My Name", "urls"=>{"Facebook"=>"http://www.facebook.com/profile.php?id=4332432432432", "Website"=>nil}},
163     # "extra"=>{"user_hash"=>{"id"=>"4332432432432", "name"=>"My Name", "first_name"=>"My", "last_name"=>"Name", "link"=>"http://www.facebook.com/profile.php?id=4332432432432", "birthday"=>"06/15/1980", "gender"=>"male", "email"=>"my email", "timezone"=>-5, "locale"=>"en_US", "updated_time"=>"2010-04-01T07:27:28+0000"}}}
164     def handle_facebook(fields)
165       uinfo = fields["extra"]["user_hash"]
166       self.facebook_id = fields["uid"]
167       self.facebook_token = fields["credentials"]["token"]
168       self.facebook_profile = fields["user_info"]["urls"]["Facebook"]
169
170       if self.email.blank?
171         self.email = uinfo["email"]
172       end
173     end
174
175     # {"provider"=>"twitter", "uid"=>"user id", "credentials"=>{"token"=>"token", "secret"=>"secret"},
176     # "extra"=>{"access_token"=>token_object, "user_hash"=>{"description"=>"desc", "screen_name"=>"nick", "geo_enabled"=>false, "profile_sidebar_border_color"=>"87bc44", "status"=>{}}},
177     # "user_info"=>{"nickname"=>"nick", "name"=>"My Name", "location"=>"Here", "image"=>"http://a0.twimg.com/profile_images/path.png", "description"=>"desc", "urls"=>{"Website"=>nil}}}
178     def handle_twitter(fields)
179       self.twitter_token = fields["credentials"]["token"]
180       self.twitter_secret = fields["credentials"]["secret"]
181       self.twitter_login = fields["user_info"]["nickname"]
182       self.twitter_id = fields["uid"]
183
184       self.login.blank? && self.login = fields["user_info"]["nickname"]
185     end
186
187     def handle_identica(fields)
188       self.identica_token = fields["credentials"]["token"]
189       self.identica_secret = fields["credentials"]["secret"]
190       self.identica_login = fields["user_info"]["nickname"]
191       self.identica_id = fields["extra"]["user_hash"]["id"]
192
193       self.login.blank? && self.login = fields["user_info"]["nickname"]
194     end
195   end # InstanceMethods
196 end