login_radius 0.0.1

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 (71) hide show
  1. data/.DS_Store +0 -0
  2. data/.gitignore +18 -0
  3. data/Gemfile +25 -0
  4. data/LICENSE +22 -0
  5. data/LoginRadiusTestApp/.gitignore +15 -0
  6. data/LoginRadiusTestApp/Gemfile +42 -0
  7. data/LoginRadiusTestApp/Gemfile.lock +136 -0
  8. data/LoginRadiusTestApp/README.rdoc +261 -0
  9. data/LoginRadiusTestApp/Rakefile +7 -0
  10. data/LoginRadiusTestApp/app/assets/images/rails.png +0 -0
  11. data/LoginRadiusTestApp/app/assets/javascripts/application.js +15 -0
  12. data/LoginRadiusTestApp/app/assets/stylesheets/application.css +13 -0
  13. data/LoginRadiusTestApp/app/controllers/application_controller.rb +17 -0
  14. data/LoginRadiusTestApp/app/helpers/application_helper.rb +2 -0
  15. data/LoginRadiusTestApp/app/mailers/.gitkeep +0 -0
  16. data/LoginRadiusTestApp/app/models/.gitkeep +0 -0
  17. data/LoginRadiusTestApp/app/views/application/callback.html.erb +1 -0
  18. data/LoginRadiusTestApp/app/views/application/index.html.erb +7 -0
  19. data/LoginRadiusTestApp/app/views/layouts/application.html.erb +14 -0
  20. data/LoginRadiusTestApp/config/application.rb +62 -0
  21. data/LoginRadiusTestApp/config/boot.rb +6 -0
  22. data/LoginRadiusTestApp/config/database.yml +25 -0
  23. data/LoginRadiusTestApp/config/environment.rb +5 -0
  24. data/LoginRadiusTestApp/config/environments/development.rb +37 -0
  25. data/LoginRadiusTestApp/config/environments/production.rb +67 -0
  26. data/LoginRadiusTestApp/config/environments/test.rb +37 -0
  27. data/LoginRadiusTestApp/config/initializers/backtrace_silencers.rb +7 -0
  28. data/LoginRadiusTestApp/config/initializers/inflections.rb +15 -0
  29. data/LoginRadiusTestApp/config/initializers/mime_types.rb +5 -0
  30. data/LoginRadiusTestApp/config/initializers/secret_token.rb +7 -0
  31. data/LoginRadiusTestApp/config/initializers/session_store.rb +8 -0
  32. data/LoginRadiusTestApp/config/initializers/wrap_parameters.rb +14 -0
  33. data/LoginRadiusTestApp/config/locales/en.yml +5 -0
  34. data/LoginRadiusTestApp/config/routes.rb +61 -0
  35. data/LoginRadiusTestApp/config.ru +4 -0
  36. data/LoginRadiusTestApp/db/seeds.rb +7 -0
  37. data/LoginRadiusTestApp/lib/assets/.gitkeep +0 -0
  38. data/LoginRadiusTestApp/lib/tasks/.gitkeep +0 -0
  39. data/LoginRadiusTestApp/log/.gitkeep +0 -0
  40. data/LoginRadiusTestApp/public/404.html +26 -0
  41. data/LoginRadiusTestApp/public/422.html +26 -0
  42. data/LoginRadiusTestApp/public/500.html +25 -0
  43. data/LoginRadiusTestApp/public/favicon.ico +0 -0
  44. data/LoginRadiusTestApp/public/robots.txt +5 -0
  45. data/LoginRadiusTestApp/script/rails +6 -0
  46. data/LoginRadiusTestApp/test/fixtures/.gitkeep +0 -0
  47. data/LoginRadiusTestApp/test/functional/.gitkeep +0 -0
  48. data/LoginRadiusTestApp/test/integration/.gitkeep +0 -0
  49. data/LoginRadiusTestApp/test/performance/browsing_test.rb +12 -0
  50. data/LoginRadiusTestApp/test/test_helper.rb +13 -0
  51. data/LoginRadiusTestApp/test/unit/.gitkeep +0 -0
  52. data/LoginRadiusTestApp/vendor/assets/javascripts/.gitkeep +0 -0
  53. data/LoginRadiusTestApp/vendor/assets/stylesheets/.gitkeep +0 -0
  54. data/LoginRadiusTestApp/vendor/plugins/.gitkeep +0 -0
  55. data/README.md +417 -0
  56. data/Rakefile +2 -0
  57. data/lib/hash.rb +13 -0
  58. data/lib/login_radius/exception.rb +4 -0
  59. data/lib/login_radius/messages.rb +102 -0
  60. data/lib/login_radius/user_profile.rb +102 -0
  61. data/lib/login_radius/user_profile_getters.rb +106 -0
  62. data/lib/login_radius/version.rb +3 -0
  63. data/lib/login_radius.rb +11 -0
  64. data/lib/string.rb +9 -0
  65. data/login_radius.gemspec +20 -0
  66. data/test/.DS_Store +0 -0
  67. data/test/basic_async_test.rb +29 -0
  68. data/test/unit/.DS_Store +0 -0
  69. data/test/unit/base_test.rb +11 -0
  70. data/test/unit/user_profile_test.rb +68 -0
  71. metadata +152 -0
data/README.md ADDED
@@ -0,0 +1,417 @@
1
+ # LoginRadius
2
+
3
+ Ruby wrapper for the LoginRadius API. Get social graph information and send messages using LoginRadius'
4
+ many social network clients!
5
+
6
+ Optional Asynchronous EventMachine friendly option!
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ gem 'login_radius'
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install login_radius
21
+
22
+ ## Usage
23
+
24
+ Take a peek:
25
+
26
+ @user_profile = LoginRadius::UserProfile.new({
27
+ :token => "TOKEN",
28
+ :secret => "SECRET",
29
+ :async => false #Set to true if using EventMachine driven frameworks(must use Em Synchrony)
30
+ })
31
+ @user_profile.login
32
+ my_contacts = @user_profile.contacts
33
+
34
+ That's right, folks. It's that simple. In order to get access to LoginRadius API, you only
35
+ need to create a UserProfile object with a user's login token and your apps secret, and login.
36
+
37
+ ### How to get a token and actually make a client
38
+
39
+ On the LoginRadius website, you are able to enter your own callback URL for your app. You will need
40
+ to setup a route on whatever framework you use, maybe "/callback." Then, get the gem forward(www.forwardhq.com)
41
+ and use it to set up a public web address for your localhost server. Then, you can enter the callback on LoginRadius
42
+ as https://jordanmatthew.fwd.wf/callback, for instance.
43
+
44
+ Paste their example login stuff onto your index page, and then enter your FB/Twitter credentials on their site.
45
+
46
+ Now you're ready to go with testing!
47
+
48
+ When LoginRadius hits your callback after a user logs in, you'll see parameters like this:
49
+
50
+ Parameters: {"token"=>"yourtokenhere"}
51
+
52
+ This token is the token used in the above example. This is what you'll need to get your user's profile.
53
+ Just paste it into the code above, and you can immediately grab their user profile on the callback,
54
+ login, and grab their contacts.
55
+
56
+ ### Some examples
57
+
58
+ Below is just code exemplifying some of the more interesting methods and what they return.
59
+ Note if you have a user authed via facebook, and not twitter, methods like
60
+
61
+ @user_profile.twitter_timeline
62
+ => false
63
+
64
+ Will return false, indicating you don't have access to that stream.
65
+
66
+ #### Facebook Stuff
67
+
68
+ @user_profile.facebook_groups
69
+ => [{:id=>"11111", :name=>"Name"},
70
+ {:id=>"11111", :name=>"Name"},
71
+ {:id=>"11111", :name=>"Name"},
72
+ {:id=>"11111", :name=>"Name"},
73
+ {:id=>"11111", :name=>"Name"},
74
+ {:id=>"11111", :name=>"Name"},
75
+ {:id=>"11111", :name=>"Name"},
76
+ {:id=>"11111", :name=>"Name"},
77
+ {:id=>"11111", :name=>"Name"}]
78
+
79
+ @user_profile.facebook_groups
80
+ => [{:id=>"11111", :name=>"Name"},
81
+ {:id=>"11111", :name=>"Name"}]
82
+
83
+
84
+ @user_profile.facebook_events
85
+ => [{:id=>"11111",
86
+ :name=>"Name",
87
+ :start_time=>"3/14/2015 9:00:00 AM",
88
+ :rsvp_status=>"attending",
89
+ :location=>"Across the world!"}]
90
+
91
+ And, finally, you can also make facebook posts:
92
+
93
+ params = {
94
+ :title => "Testing",
95
+ :url => "www.loginradius.com",
96
+ :status => "Wizzup",
97
+ :caption => "Testly",
98
+ :description => "Testing"
99
+ }
100
+ @user_profile.make_facebook_post(params)
101
+ => true
102
+
103
+ Note that as of publishing, the :to option on facebook params does not work. You can't post to
104
+ other people's walls.
105
+
106
+ #### LinkedIn Stuff
107
+
108
+ To send messages over linked in:
109
+
110
+ @user_profile.send_linked_in_message(@user_profile.contacts.first[:id], "Testing", "This is a test.")
111
+ => true
112
+
113
+ Notice how I grabbed my first contact and used his :id property.
114
+
115
+ Also, you can grab a user's linked in companies:
116
+
117
+ @user_profile.linked_in_companies
118
+ => [{:id=>11111, :name=>"Name"}]
119
+
120
+ #### Twitter Stuff
121
+
122
+ To send messages over twitter:
123
+
124
+ @user_profile.send_linked_in_message(@user_profile.contacts.first[:id], "Testing", "This is a test.")
125
+ => true
126
+
127
+ Notice how I grabbed my first contact and used his :id property.
128
+
129
+ Grab your timelines and mentions:
130
+
131
+ @user_profile.twitter_timeline
132
+ => [{:id=>"11111",
133
+ :text=>"Stuff",
134
+ :date_time=>"5/20/2013 4:53:52 PM",
135
+ :likes=>0,
136
+ :place=>"",
137
+ :source=>
138
+ "<a href=\"http://twitterfeed.com\" rel=\"nofollow\">twitterfeed</a>",
139
+ :image_url=>nil,
140
+ :link_url=>nil,
141
+ :name=>"Name"}]
142
+
143
+ @user_profile.twitter_mentions
144
+ => [{:id=>"11111",
145
+ :text=>"Stuff",
146
+ :date_time=>"3/31/2013 7:54:10 PM",
147
+ :likes=>0,
148
+ :place=>"",
149
+ :source=>"web",
150
+ :image_url=>nil,
151
+ :link_url=>nil,
152
+ :name=>"Name"}]
153
+
154
+ #### Social Network Contacts
155
+
156
+ Note this is all the contacts from every social network the user is connected to.
157
+
158
+ @user_profile.contacts
159
+ => [{:name=>"Name",
160
+ :email_id=>"",
161
+ :phone_number=>"",
162
+ :id=>"11111",
163
+ :profile_url=>nil,
164
+ :image_url=>nil,
165
+ :status=>nil,
166
+ :industry=>nil,
167
+ :country=>nil,
168
+ :gender=>nil}]
169
+
170
+ #### User Profile Stuff
171
+
172
+ When you login, the API returns a hash of user profile information, which is then turned into methods
173
+ on the LoginRadius::UserProfile object for your accessing pleasure. A quick call to methods will show this.
174
+
175
+ @user_profile.methods
176
+ =>[:id,
177
+ :provider,
178
+ :prefix,
179
+ :first_name,
180
+ :middle_name,
181
+ :last_name,
182
+ :suffix,
183
+ :full_name,
184
+ :nick_name,
185
+ :profile_name,
186
+ :birth_date,
187
+ :gender,
188
+ :website,
189
+ :email,
190
+ :country,
191
+ ...]
192
+
193
+ Calling .methods on a user profile right after a successful login call will reveal these.
194
+ For more information, please read the section in caveats on the matter, as it's a bit esoteric.
195
+
196
+ ## Caveats
197
+
198
+ This section is for the nitpickers. It's got notes about the finer pickings of this API for those
199
+ who are wondering, but is not strictly necessary reading.
200
+
201
+ ### The Bang Methods
202
+
203
+ When authenticated, a method like contacts will return
204
+
205
+ @user_profile.authenticated?
206
+ => true
207
+ @user_profile.contacts
208
+ => [{:name=>"Name",
209
+ :email_id=>"",
210
+ :phone_number=>"",
211
+ :id=>"11111",
212
+ :profile_url=>nil,
213
+ :image_url=>nil,
214
+ :status=>nil,
215
+ :industry=>nil,
216
+ :country=>nil,
217
+ :gender=>nil},
218
+ ...
219
+
220
+ When unauthenticated, a method like contacts will return false.
221
+
222
+ @user_profile.authenticated?
223
+ => false
224
+ @user_profile.contacts
225
+ => false
226
+
227
+ However, if you want to be a bit more flammable, try using contacts!, the bang version. It still returns
228
+ the same results when authed, but when unauthed, will actually raise a LoginRadius::Exception.
229
+
230
+ ### The LoginRadius::UserProfile object before and after login
231
+
232
+ Once you've got a valid user_profile, you have many methods available to you. Let's quickly take a look
233
+ at the methods in a LoginRadius::UserProfile object that hasn't yet been authenticated:
234
+
235
+ @user_profile.authenticated?
236
+ => false
237
+
238
+ @user_profile.methods - Object.new.methods
239
+ => [:secret,
240
+ :secret=,
241
+ :token,
242
+ :token=,
243
+ :async,
244
+ :async=,
245
+ :guid_valid?,
246
+ :authenticated?,
247
+ :call_api,
248
+ :send_twitter_message,
249
+ :send_linkedin_message,
250
+ :make_facebook_post,
251
+ :login!,
252
+ :login,
253
+ :twitter_mentions!,
254
+ :twitter_mentions,
255
+ :twitter_timeline!,
256
+ :twitter_timeline,
257
+ :linked_in_companies!,
258
+ :linked_in_companies,
259
+ :contacts!,
260
+ :contacts,
261
+ :facebook_groups!,
262
+ :facebook_groups,
263
+ :facebook_posts!,
264
+ :facebook_posts,
265
+ :facebook_events!,
266
+ :facebook_events]
267
+
268
+ Notice we've got a lot of stuff we can access. But we're not yet authenticated. Running login and then checking
269
+ the new methods created, we see we have much more than before!
270
+
271
+ @user_profile.login
272
+ => true
273
+ @user_profile.authenticated?
274
+ => true
275
+ @user_profile.methods - Object.new.methods
276
+ => [:id,
277
+ :provider,
278
+ :prefix,
279
+ :first_name,
280
+ :middle_name,
281
+ :last_name,
282
+ :suffix,
283
+ :full_name,
284
+ :nick_name,
285
+ :profile_name,
286
+ :birth_date,
287
+ :gender,
288
+ :website,
289
+ :email,
290
+ :country,
291
+ :thumbnail_image_url,
292
+ :image_url,
293
+ :favicon,
294
+ :profile_url,
295
+ :home_town,
296
+ :state,
297
+ :city,
298
+ :industry,
299
+ :about,
300
+ :time_zone,
301
+ :local_language,
302
+ :language,
303
+ :verified,
304
+ :updated_time,
305
+ :positions,
306
+ :educations,
307
+ :phone_numbers,
308
+ :im_accounts,
309
+ :addresses,
310
+ :main_address,
311
+ :created,
312
+ :local_city,
313
+ :profile_city,
314
+ :local_country,
315
+ :profile_country,
316
+ :relationship_status,
317
+ :quota,
318
+ :interested_in,
319
+ :interests,
320
+ :religion,
321
+ :political,
322
+ :sports,
323
+ :inspirational_people,
324
+ :https_image_url,
325
+ :followers_count,
326
+ :friends_count,
327
+ :is_geo_enabled,
328
+ :total_statuses_count,
329
+ :associations,
330
+ :num_recommenders,
331
+ :honors,
332
+ :skills,
333
+ :current_status,
334
+ :certifications,
335
+ :courses,
336
+ :volunteer,
337
+ :recommendations_received,
338
+ :languages,
339
+ :public_repository,
340
+ :hireable,
341
+ :repository_url,
342
+ :age,
343
+ :patents,
344
+ :favorite_things,
345
+ :professional_headline,
346
+ :provider_access_credential,
347
+ :secret,
348
+ :secret=,
349
+ :token,
350
+ :token=,
351
+ :async,
352
+ :async=,
353
+ :guid_valid?,
354
+ :authenticated?,
355
+ :call_api,
356
+ :send_twitter_message,
357
+ :send_linkedin_message,
358
+ :make_facebook_post,
359
+ :login!,
360
+ :login,
361
+ :twitter_mentions!,
362
+ :twitter_mentions,
363
+ :twitter_timeline!,
364
+ :twitter_timeline,
365
+ :linked_in_companies!,
366
+ :linked_in_companies,
367
+ :contacts!,
368
+ :contacts,
369
+ :facebook_groups!,
370
+ :facebook_groups,
371
+ :facebook_posts!,
372
+ :facebook_posts,
373
+ :facebook_events!,
374
+ :facebook_events]
375
+
376
+ Holy moly we got a whole bunch of new methods! That's because on login, all the user profile information
377
+ returned by the API is turned into dynamically generated GETTERs on the object that you can then use later.
378
+
379
+ ### Running the tests in test/unit
380
+
381
+ The base_test.rb file isn't meant to be run, it is inherited by user_profile_test.rb. In that file
382
+ you'll notice the lines at the top of the file:
383
+
384
+ TOKEN = "yourtokenhere"
385
+ SECRET = "yoursecrethere"
386
+
387
+ These are meant to be changed to your API secret and some user's token. What I do is I run a dummy
388
+ rails app locally and use the forward gem so that it can receive requests from the internet. Then I setup
389
+ a callback route and login page on the app and use it to have LoginRadius ping me with tokens that I can
390
+ paste into this test file.
391
+
392
+ *Beware, LoginRadius invalidates any token after 15 minutes, so during testing, you'll have to repeat the token
393
+ creation A LOT*
394
+
395
+ To run the tests, do:
396
+
397
+ bundle exec ruby -Itest test/unit/user_profile_test.rb
398
+
399
+ If you login with a facebook user, for instance, you'll notice all the twitter/linked in tests failing.
400
+ This is to be expected, you don't have access to those streams!
401
+
402
+ ### Evented Frameworks
403
+
404
+ I've built this gem to work with frameworks like Cramp. I've done a little bit of testing by creating
405
+ a basic event loop script in basic_async_test.rb in the test folder, which you can run to see that
406
+ things work there.
407
+
408
+ I've also done some AB testing using a Cramp framework elsewhere, but you should take caution in using
409
+ this gem in evented frameworks and make sure to use apache bench to test it for nonblocking yourself.
410
+
411
+ ## Contributing
412
+
413
+ 1. Fork it
414
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
415
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
416
+ 4. Push to the branch (`git push origin my-new-feature`)
417
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
data/lib/hash.rb ADDED
@@ -0,0 +1,13 @@
1
+ class Hash
2
+ def self.lr_convert_hash_keys(value) #lr_ appended so no method naming conflicts with other gems
3
+ case value
4
+ when Array
5
+ value.map { |v| lr_convert_hash_keys(v) }
6
+ # or `value.map(&method(:convert_hash_keys))`
7
+ when Hash
8
+ Hash[value.map { |k, v| [k.to_s.lr_underscore, lr_convert_hash_keys(v)] }]
9
+ else
10
+ value
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,4 @@
1
+ module LoginRadius
2
+ class Exception < Exception
3
+ end
4
+ end
@@ -0,0 +1,102 @@
1
+ module LoginRadius
2
+ module Messages
3
+
4
+ # Sends a message to a person using twitter
5
+ #
6
+ # @param sendto [String] Social ID of person you're contacting from contact list
7
+ # @param subject [String]
8
+ # @param message [String]
9
+ # @return [Boolean] Whether or not message succeeded
10
+ def send_twitter_message(sendto, subject, message)
11
+ send_message({:sendto => sendto, :subject => subject, :message => message})
12
+ end
13
+
14
+ # Sends a message to a person using twitter
15
+ #
16
+ # @param sendto [String] Social ID of person you're contacting from contact list
17
+ # @param subject [String]
18
+ # @param message [String]
19
+ # @return [Boolean] Whether or not message succeeded
20
+ def send_twitter_message!(sendto, subject, message)
21
+ send_message({:sendto => sendto, :subject => subject, :message => message})
22
+ end
23
+
24
+ # Sends a message to a person using linkedin
25
+ #
26
+ # @param sendto [String] Social ID of person you're contacting from contact list
27
+ # @param subject [String]
28
+ # @param message [String]
29
+ # @return [Boolean] Whether or not message succeeded
30
+ def send_linked_in_message(sendto, subject, message)
31
+ send_message({:sendto => sendto, :subject => subject, :message => message})
32
+ end
33
+
34
+ # Sends a message to a person using linkedin
35
+ #
36
+ # @param sendto [String] Social ID of person you're contacting from contact list
37
+ # @param subject [String]
38
+ # @param message [String]
39
+ # @return [Boolean] Whether or not message succeeded
40
+ def send_linked_in_message!(sendto, subject, message)
41
+ send_message!({:sendto => sendto, :subject => subject, :message => message})
42
+ end
43
+
44
+ # Sends a message to a person using facebook. Params hash takes following keys:
45
+ # to(optional) :- Person's wall it's going to, if blank, is your own
46
+ # title :- [optional parameter] status message title
47
+ # url:- [optional parameter] any url that you want post in status message
48
+ # imageurl :- [optional parameter] any image url that you want post in status message
49
+ # status :- your status message
50
+ # caption : [optional parameter] caption that you want post in status message
51
+ # description :- [optional parameter] description that you want post in status message
52
+ #
53
+ # @param params [Hash]
54
+ # @return [Boolean] Whether or not message succeeded
55
+ def make_facebook_post!(params = {})
56
+ call_api("status/update/#{secret}/#{token}", params)
57
+ end
58
+
59
+ # Sends a message to a person using facebook. Params hash takes following keys:
60
+ # to(optional) :- Person's wall it's going to, if blank, is your own
61
+ # title :- [optional parameter] status message title
62
+ # url:- [optional parameter] any url that you want post in status message
63
+ # imageurl :- [optional parameter] any image url that you want post in status message
64
+ # status :- your status message
65
+ # caption : [optional parameter] caption that you want post in status message
66
+ # description :- [optional parameter] description that you want post in status message
67
+ #
68
+ # @param params [Hash]
69
+ # @return [Boolean] Whether or not message succeeded
70
+ def make_facebook_post(params = {})
71
+ make_facebook_post!(params)
72
+ rescue LoginRadius::Exception => e
73
+ false
74
+ end
75
+
76
+ private
77
+
78
+ # Sends a message to a person. Will use either twitter, facebook or linked in, depending
79
+ # on where the person you're sending it to came from, and the keys present in the hash.
80
+ # Other methods in this module describe the keys they use, they all call this method
81
+ # with the proper keys for their service type.
82
+ #
83
+ # @param params [Hash] Params hash describing what you'd like to send.
84
+ # @return [Boolean] Whether or not message succeeded
85
+ def send_message!(params = {})
86
+ call_api("directmessage/#{secret}/#{token}",params)
87
+ end
88
+
89
+ # Sends a message to a person. Will use either twitter, facebook or linked in, depending
90
+ # on where the person you're sending it to came from, and the keys present in the hash.
91
+ # Other methods in this module describe the keys they use, they all call this method
92
+ # with the proper keys for their service type.
93
+ #
94
+ # @param params [Hash] Params hash describing what you'd like to send.
95
+ # @return [Boolean] Whether or not message succeeded
96
+ def send_message(params = {})
97
+ send_message!(params)
98
+ rescue LoginRadius::Exception => e
99
+ false
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,102 @@
1
+ require 'net/http'
2
+ module LoginRadius
3
+ class UserProfile
4
+ include UserProfileGetters
5
+ include Messages
6
+
7
+ attr_accessor :secret, :token, :async
8
+
9
+ API_ROOT = "https://hub.loginradius.com/"
10
+
11
+ # Takes a hash of account secret, token, and connection type(net_http or em_http)
12
+ # and uses it to auth against the LoginRadius API. Then it returns the Account object. The
13
+ # async key is optional, if set to true, will use Em::HTTP instead of Net::HTTP.
14
+ #
15
+ # @param opts [Hash] Must have keys :token, :secret, and :async(optional)
16
+ # @return [LoginRadius::Account]
17
+ def initialize(opts = {})
18
+ self.token = opts[:token]
19
+ self.secret = opts[:secret]
20
+ self.async = opts[:async]
21
+ raise LoginRadius::Exception.new("Invalid Request") unless token
22
+ raise LoginRadius::Exception.new("Invalid Token") unless guid_valid?(token)
23
+ raise LoginRadius::Exception.new("Invalid Secret") unless guid_valid?(secret)
24
+ end
25
+
26
+ # Takes a guid and returns whether or not it is valid.
27
+ #
28
+ # @param guid [String]
29
+ # @return [Boolean]
30
+ def guid_valid?(guid)
31
+ guid.match(/^\{?[A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{12}\}?$/i)
32
+ end
33
+
34
+ # Returns whether or not this object is authed.
35
+ #
36
+ # @return [Boolean]
37
+ def authenticated?
38
+ respond_to?(:id)
39
+ end
40
+
41
+ # Generic GET call function that other submodules can use to hit the API.
42
+ #
43
+ # @param url [String] Target URL to fetch data from.
44
+ # @param params [Hash] Parameters to send
45
+ # @return data [Hash] Parsed JSON data from the call
46
+ def call_api(url, params = {})
47
+ url = API_ROOT+url unless url.match(/^#{API_ROOT}/) #in case api root is included,
48
+ #as would happen in a recursive redirect call.
49
+
50
+ if async
51
+ #UNTESTED
52
+ #if async is true, we expect you to be using EM::Synchrony submodule and to be in an eventloop,
53
+ #like with a thin server using the Cramp framework. Otherwise, this method blows up.
54
+ response = EM::Synchrony.sync EventMachine::HttpRequest.new(url).aget :redirects => 2, :query => params
55
+ response = response.response
56
+ else
57
+ #synchronous version of the call.
58
+ url_obj = URI.parse(url)
59
+ url_obj.query = URI.encode_www_form(params)
60
+
61
+ http = Net::HTTP.new(url_obj.host, url_obj.port)
62
+ http.use_ssl = true
63
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
64
+ response = http.get(url_obj.request_uri)
65
+
66
+ if response.is_a?(Net::HTTPTemporaryRedirect)
67
+ #for some reason, we always get redirected when calling server first time.
68
+ #so if we do, we scan body for the redirect url, and the scan returns
69
+ #an array of arrays. So we grab the array we know has what we need,
70
+ #and grab the first element.
71
+ redirect_url_array = response.body.scan(/<a href=\"([^>]+)\">/i)[1]
72
+ redirect_url = redirect_url_array.first
73
+ return call_api(redirect_url, params)
74
+ end
75
+
76
+ response = response.body
77
+ end
78
+
79
+ # For some reason, this API returns true/false instead of JSON responses for certain calls.
80
+ # We catch this here.
81
+ return true if response.match(/^true/i)
82
+ return false if response.match(/^false/i)
83
+
84
+ #We rescue this because sometimes the API returns HTML pages(which can't be JSON parsed)
85
+ #This mostly happens when people use expired tokens. So we go ahead and raise an exception
86
+ #About it if it gets caught.
87
+ begin
88
+ unconverted_response = JSON.parse(response)
89
+ #it's all String keys in CamelCase above, so...
90
+ # IF we got a hash back, convert it directly, if its an array, convert each item which is a hash
91
+ # into snake case
92
+ converted_response = unconverted_response.is_a?(Hash) ?
93
+ Hash.lr_convert_hash_keys(unconverted_response).symbolize_keys! :
94
+ unconverted_response.map { |item| Hash.lr_convert_hash_keys(item).symbolize_keys! }
95
+
96
+ return converted_response
97
+ rescue JSON::ParserError => e
98
+ raise LoginRadius::Exception.new("A JSON parsing error occured because the API returned an HTML page instead of JSON. This happens mostly when you're using an expired Token. Specifics: #{e.message}")
99
+ end
100
+ end
101
+ end
102
+ end