spark_api 1.0.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.
Files changed (257) hide show
  1. data/History.txt +139 -0
  2. data/LICENSE +14 -0
  3. data/README.md +153 -0
  4. data/Rakefile +18 -0
  5. data/VERSION +1 -0
  6. data/bin/spark_api +8 -0
  7. data/bin/spark_api~ +8 -0
  8. data/lib/spark_api.rb +46 -0
  9. data/lib/spark_api/authentication.rb +55 -0
  10. data/lib/spark_api/authentication/api_auth.rb +104 -0
  11. data/lib/spark_api/authentication/api_auth.rb~ +104 -0
  12. data/lib/spark_api/authentication/base_auth.rb +47 -0
  13. data/lib/spark_api/authentication/base_auth.rb~ +47 -0
  14. data/lib/spark_api/authentication/oauth2.rb +198 -0
  15. data/lib/spark_api/authentication/oauth2.rb~ +199 -0
  16. data/lib/spark_api/authentication/oauth2_impl/grant_type_base.rb +87 -0
  17. data/lib/spark_api/authentication/oauth2_impl/grant_type_base.rb~ +87 -0
  18. data/lib/spark_api/authentication/oauth2_impl/grant_type_code.rb +48 -0
  19. data/lib/spark_api/authentication/oauth2_impl/grant_type_code.rb~ +49 -0
  20. data/lib/spark_api/authentication/oauth2_impl/grant_type_password.rb +44 -0
  21. data/lib/spark_api/authentication/oauth2_impl/grant_type_password.rb~ +45 -0
  22. data/lib/spark_api/authentication/oauth2_impl/grant_type_refresh.rb +35 -0
  23. data/lib/spark_api/authentication/oauth2_impl/grant_type_refresh.rb~ +36 -0
  24. data/lib/spark_api/authentication/oauth2_impl/middleware.rb +38 -0
  25. data/lib/spark_api/authentication/oauth2_impl/middleware.rb~ +39 -0
  26. data/lib/spark_api/authentication/oauth2_impl/password_provider.rb +24 -0
  27. data/lib/spark_api/authentication/oauth2_impl/password_provider.rb~ +25 -0
  28. data/lib/spark_api/cli.rb +158 -0
  29. data/lib/spark_api/cli.rb~ +158 -0
  30. data/lib/spark_api/cli/api_auth.rb +8 -0
  31. data/lib/spark_api/cli/api_auth.rb~ +8 -0
  32. data/lib/spark_api/cli/oauth2.rb +14 -0
  33. data/lib/spark_api/cli/oauth2.rb~ +14 -0
  34. data/lib/spark_api/cli/setup.rb +47 -0
  35. data/lib/spark_api/cli/setup.rb~ +47 -0
  36. data/lib/spark_api/client.rb +27 -0
  37. data/lib/spark_api/configuration.rb +54 -0
  38. data/lib/spark_api/configuration.rb~ +54 -0
  39. data/lib/spark_api/configuration/yaml.rb +101 -0
  40. data/lib/spark_api/configuration/yaml.rb~ +101 -0
  41. data/lib/spark_api/connection.rb +42 -0
  42. data/lib/spark_api/faraday.rb +64 -0
  43. data/lib/spark_api/faraday.rb~ +64 -0
  44. data/lib/spark_api/models.rb +33 -0
  45. data/lib/spark_api/models.rb~ +33 -0
  46. data/lib/spark_api/models/account.rb +115 -0
  47. data/lib/spark_api/models/account.rb~ +115 -0
  48. data/lib/spark_api/models/base.rb +118 -0
  49. data/lib/spark_api/models/base.rb~ +118 -0
  50. data/lib/spark_api/models/connect_prefs.rb +10 -0
  51. data/lib/spark_api/models/connect_prefs.rb~ +10 -0
  52. data/lib/spark_api/models/constraint.rb +16 -0
  53. data/lib/spark_api/models/constraint.rb~ +16 -0
  54. data/lib/spark_api/models/contact.rb +49 -0
  55. data/lib/spark_api/models/contact.rb~ +49 -0
  56. data/lib/spark_api/models/custom_fields.rb +12 -0
  57. data/lib/spark_api/models/custom_fields.rb~ +12 -0
  58. data/lib/spark_api/models/document.rb +11 -0
  59. data/lib/spark_api/models/document.rb~ +11 -0
  60. data/lib/spark_api/models/finders.rb +45 -0
  61. data/lib/spark_api/models/finders.rb~ +45 -0
  62. data/lib/spark_api/models/idx_link.rb +47 -0
  63. data/lib/spark_api/models/idx_link.rb~ +47 -0
  64. data/lib/spark_api/models/listing.rb +197 -0
  65. data/lib/spark_api/models/listing.rb~ +197 -0
  66. data/lib/spark_api/models/listing_cart.rb +72 -0
  67. data/lib/spark_api/models/listing_cart.rb~ +72 -0
  68. data/lib/spark_api/models/market_statistics.rb +33 -0
  69. data/lib/spark_api/models/market_statistics.rb~ +33 -0
  70. data/lib/spark_api/models/message.rb +21 -0
  71. data/lib/spark_api/models/message.rb~ +21 -0
  72. data/lib/spark_api/models/note.rb +41 -0
  73. data/lib/spark_api/models/note.rb~ +41 -0
  74. data/lib/spark_api/models/notification.rb +42 -0
  75. data/lib/spark_api/models/notification.rb~ +42 -0
  76. data/lib/spark_api/models/open_house.rb +24 -0
  77. data/lib/spark_api/models/open_house.rb~ +24 -0
  78. data/lib/spark_api/models/photo.rb +70 -0
  79. data/lib/spark_api/models/photo.rb~ +70 -0
  80. data/lib/spark_api/models/property_types.rb +7 -0
  81. data/lib/spark_api/models/property_types.rb~ +7 -0
  82. data/lib/spark_api/models/saved_search.rb +16 -0
  83. data/lib/spark_api/models/saved_search.rb~ +16 -0
  84. data/lib/spark_api/models/shared_listing.rb +35 -0
  85. data/lib/spark_api/models/shared_listing.rb~ +35 -0
  86. data/lib/spark_api/models/standard_fields.rb +50 -0
  87. data/lib/spark_api/models/standard_fields.rb~ +50 -0
  88. data/lib/spark_api/models/subresource.rb +19 -0
  89. data/lib/spark_api/models/subresource.rb~ +19 -0
  90. data/lib/spark_api/models/system_info.rb +14 -0
  91. data/lib/spark_api/models/system_info.rb~ +14 -0
  92. data/lib/spark_api/models/tour_of_home.rb +24 -0
  93. data/lib/spark_api/models/tour_of_home.rb~ +24 -0
  94. data/lib/spark_api/models/video.rb +16 -0
  95. data/lib/spark_api/models/video.rb~ +16 -0
  96. data/lib/spark_api/models/virtual_tour.rb +18 -0
  97. data/lib/spark_api/models/virtual_tour.rb~ +18 -0
  98. data/lib/spark_api/multi_client.rb +59 -0
  99. data/lib/spark_api/multi_client.rb~ +59 -0
  100. data/lib/spark_api/paginate.rb +109 -0
  101. data/lib/spark_api/paginate.rb~ +109 -0
  102. data/lib/spark_api/primary_array.rb +29 -0
  103. data/lib/spark_api/primary_array.rb~ +29 -0
  104. data/lib/spark_api/request.rb +96 -0
  105. data/lib/spark_api/request.rb~ +96 -0
  106. data/lib/spark_api/response.rb +70 -0
  107. data/lib/spark_api/response.rb~ +70 -0
  108. data/lib/spark_api/version.rb +4 -0
  109. data/lib/spark_api/version.rb~ +4 -0
  110. data/script/console +6 -0
  111. data/script/console~ +6 -0
  112. data/script/example.rb +27 -0
  113. data/script/example.rb~ +27 -0
  114. data/spec/fixtures/accounts/all.json +160 -0
  115. data/spec/fixtures/accounts/my.json +74 -0
  116. data/spec/fixtures/accounts/my_portal.json +20 -0
  117. data/spec/fixtures/accounts/my_put.json +5 -0
  118. data/spec/fixtures/accounts/my_save.json +5 -0
  119. data/spec/fixtures/accounts/office.json +142 -0
  120. data/spec/fixtures/accounts/password_save.json +6 -0
  121. data/spec/fixtures/authentication_failure.json +7 -0
  122. data/spec/fixtures/base.json +13 -0
  123. data/spec/fixtures/contacts/contacts.json +28 -0
  124. data/spec/fixtures/contacts/my.json +19 -0
  125. data/spec/fixtures/contacts/new.json +11 -0
  126. data/spec/fixtures/contacts/new_empty.json +8 -0
  127. data/spec/fixtures/contacts/new_notify.json +11 -0
  128. data/spec/fixtures/contacts/post.json +10 -0
  129. data/spec/fixtures/contacts/tags.json +11 -0
  130. data/spec/fixtures/count.json +10 -0
  131. data/spec/fixtures/empty.json +3 -0
  132. data/spec/fixtures/errors/expired.json +7 -0
  133. data/spec/fixtures/errors/failure.json +5 -0
  134. data/spec/fixtures/errors/failure_with_constraint.json +17 -0
  135. data/spec/fixtures/errors/failure_with_msg.json +7 -0
  136. data/spec/fixtures/generic_delete.json +1 -0
  137. data/spec/fixtures/generic_failure.json +5 -0
  138. data/spec/fixtures/listing_carts/add_listing.json +13 -0
  139. data/spec/fixtures/listing_carts/add_listing_post.json +5 -0
  140. data/spec/fixtures/listing_carts/empty.json +5 -0
  141. data/spec/fixtures/listing_carts/listing_cart.json +19 -0
  142. data/spec/fixtures/listing_carts/new.json +12 -0
  143. data/spec/fixtures/listing_carts/post.json +10 -0
  144. data/spec/fixtures/listing_carts/remove_listing.json +13 -0
  145. data/spec/fixtures/listings/constraints.json +18 -0
  146. data/spec/fixtures/listings/constraints_with_pagination.json +24 -0
  147. data/spec/fixtures/listings/document_index.json +19 -0
  148. data/spec/fixtures/listings/multiple.json +69 -0
  149. data/spec/fixtures/listings/no_subresources.json +38 -0
  150. data/spec/fixtures/listings/open_houses.json +21 -0
  151. data/spec/fixtures/listings/photos/index.json +469 -0
  152. data/spec/fixtures/listings/photos/new.json +12 -0
  153. data/spec/fixtures/listings/photos/post.json +20 -0
  154. data/spec/fixtures/listings/put.json +5 -0
  155. data/spec/fixtures/listings/put_expiration_date.json +5 -0
  156. data/spec/fixtures/listings/saved_search.json +17 -0
  157. data/spec/fixtures/listings/shared_listing_get.json +14 -0
  158. data/spec/fixtures/listings/shared_listing_new.json +9 -0
  159. data/spec/fixtures/listings/shared_listing_post.json +10 -0
  160. data/spec/fixtures/listings/tour_of_homes.json +23 -0
  161. data/spec/fixtures/listings/videos_index.json +18 -0
  162. data/spec/fixtures/listings/virtual_tours_index.json +42 -0
  163. data/spec/fixtures/listings/with_documents.json +52 -0
  164. data/spec/fixtures/listings/with_permissions.json +44 -0
  165. data/spec/fixtures/listings/with_photos.json +110 -0
  166. data/spec/fixtures/listings/with_supplement.json +39 -0
  167. data/spec/fixtures/listings/with_videos.json +54 -0
  168. data/spec/fixtures/listings/with_vtour.json +48 -0
  169. data/spec/fixtures/logo_fbs.png +0 -0
  170. data/spec/fixtures/messages/new.json +14 -0
  171. data/spec/fixtures/messages/new_empty.json +7 -0
  172. data/spec/fixtures/messages/new_with_recipients.json +15 -0
  173. data/spec/fixtures/messages/post.json +5 -0
  174. data/spec/fixtures/notes/add.json +11 -0
  175. data/spec/fixtures/notes/agent_shared.json +11 -0
  176. data/spec/fixtures/notes/agent_shared_empty.json +7 -0
  177. data/spec/fixtures/notes/new.json +5 -0
  178. data/spec/fixtures/notifications/mark_read.json +1 -0
  179. data/spec/fixtures/notifications/new.json +8 -0
  180. data/spec/fixtures/notifications/new_empty.json +7 -0
  181. data/spec/fixtures/notifications/notifications.json +43 -0
  182. data/spec/fixtures/notifications/post.json +10 -0
  183. data/spec/fixtures/notifications/unread.json +10 -0
  184. data/spec/fixtures/oauth2/access.json +3 -0
  185. data/spec/fixtures/oauth2/access_with_old_refresh.json +5 -0
  186. data/spec/fixtures/oauth2/access_with_refresh.json +5 -0
  187. data/spec/fixtures/oauth2/authorization_code_body.json +7 -0
  188. data/spec/fixtures/oauth2/error.json +3 -0
  189. data/spec/fixtures/oauth2/password_body.json +7 -0
  190. data/spec/fixtures/oauth2/refresh_body.json +7 -0
  191. data/spec/fixtures/oauth2_error.json +3 -0
  192. data/spec/fixtures/property_types/property_types.json +31 -0
  193. data/spec/fixtures/session.json +10 -0
  194. data/spec/fixtures/standardfields/city.json +1031 -0
  195. data/spec/fixtures/standardfields/nearby.json +53 -0
  196. data/spec/fixtures/standardfields/standardfields.json +188 -0
  197. data/spec/fixtures/standardfields/stateorprovince.json +36 -0
  198. data/spec/fixtures/success.json +5 -0
  199. data/spec/json_helper.rb +76 -0
  200. data/spec/mock_helper.rb +124 -0
  201. data/spec/oauth2_helper.rb +68 -0
  202. data/spec/spec_helper.rb +48 -0
  203. data/spec/unit/flexmls_api_spec.rb~ +23 -0
  204. data/spec/unit/spark_api/authentication/api_auth_spec.rb +169 -0
  205. data/spec/unit/spark_api/authentication/api_auth_spec.rb~ +169 -0
  206. data/spec/unit/spark_api/authentication/base_auth_spec.rb +10 -0
  207. data/spec/unit/spark_api/authentication/base_auth_spec.rb~ +10 -0
  208. data/spec/unit/spark_api/authentication/oauth2_impl/grant_type_base_spec.rb +10 -0
  209. data/spec/unit/spark_api/authentication/oauth2_impl/grant_type_base_spec.rb~ +10 -0
  210. data/spec/unit/spark_api/authentication/oauth2_spec.rb +205 -0
  211. data/spec/unit/spark_api/authentication/oauth2_spec.rb~ +205 -0
  212. data/spec/unit/spark_api/authentication_spec.rb +38 -0
  213. data/spec/unit/spark_api/authentication_spec.rb~ +38 -0
  214. data/spec/unit/spark_api/configuration/yaml_spec.rb +72 -0
  215. data/spec/unit/spark_api/configuration/yaml_spec.rb~ +72 -0
  216. data/spec/unit/spark_api/configuration_spec.rb +122 -0
  217. data/spec/unit/spark_api/configuration_spec.rb~ +122 -0
  218. data/spec/unit/spark_api/faraday_spec.rb +90 -0
  219. data/spec/unit/spark_api/faraday_spec.rb~ +90 -0
  220. data/spec/unit/spark_api/models/account_spec.rb +176 -0
  221. data/spec/unit/spark_api/models/base_spec.rb +106 -0
  222. data/spec/unit/spark_api/models/connect_prefs_spec.rb +9 -0
  223. data/spec/unit/spark_api/models/constraint_spec.rb +19 -0
  224. data/spec/unit/spark_api/models/contact_spec.rb +108 -0
  225. data/spec/unit/spark_api/models/contact_spec.rb~ +108 -0
  226. data/spec/unit/spark_api/models/document_spec.rb +32 -0
  227. data/spec/unit/spark_api/models/listing_cart_spec.rb +127 -0
  228. data/spec/unit/spark_api/models/listing_cart_spec.rb~ +127 -0
  229. data/spec/unit/spark_api/models/listing_spec.rb +320 -0
  230. data/spec/unit/spark_api/models/listing_spec.rb~ +320 -0
  231. data/spec/unit/spark_api/models/message_spec.rb +47 -0
  232. data/spec/unit/spark_api/models/message_spec.rb~ +47 -0
  233. data/spec/unit/spark_api/models/note_spec.rb +63 -0
  234. data/spec/unit/spark_api/models/note_spec.rb~ +63 -0
  235. data/spec/unit/spark_api/models/notification_spec.rb +62 -0
  236. data/spec/unit/spark_api/models/notification_spec.rb~ +62 -0
  237. data/spec/unit/spark_api/models/open_house_spec.rb +39 -0
  238. data/spec/unit/spark_api/models/photo_spec.rb +92 -0
  239. data/spec/unit/spark_api/models/property_types_spec.rb +33 -0
  240. data/spec/unit/spark_api/models/saved_search_spec.rb +40 -0
  241. data/spec/unit/spark_api/models/shared_listing_spec.rb +45 -0
  242. data/spec/unit/spark_api/models/shared_listing_spec.rb~ +45 -0
  243. data/spec/unit/spark_api/models/standard_fields_spec.rb +60 -0
  244. data/spec/unit/spark_api/models/system_info_spec.rb +83 -0
  245. data/spec/unit/spark_api/models/tour_of_home_spec.rb +44 -0
  246. data/spec/unit/spark_api/models/video_spec.rb +36 -0
  247. data/spec/unit/spark_api/models/virtual_tour_spec.rb +44 -0
  248. data/spec/unit/spark_api/multi_client_spec.rb +56 -0
  249. data/spec/unit/spark_api/multi_client_spec.rb~ +56 -0
  250. data/spec/unit/spark_api/paginate_spec.rb +224 -0
  251. data/spec/unit/spark_api/paginate_spec.rb~ +224 -0
  252. data/spec/unit/spark_api/primary_array_spec.rb +41 -0
  253. data/spec/unit/spark_api/primary_array_spec.rb~ +41 -0
  254. data/spec/unit/spark_api/request_spec.rb +344 -0
  255. data/spec/unit/spark_api/request_spec.rb~ +344 -0
  256. data/spec/unit/spark_api_spec.rb +23 -0
  257. metadata +725 -0
@@ -0,0 +1,199 @@
1
+ require 'uri'
2
+
3
+
4
+ module FlexmlsApi
5
+
6
+ module Authentication
7
+
8
+ #=OAuth2 Authentication
9
+ # Auth implementation to the API using the OAuth2 service endpoint. Current adheres to the 10
10
+ # draft of the OAuth2 specification. With OAuth2, the application supplies credentials for the
11
+ # application, and a separate a user authentication flow dictactes the active user for
12
+ # requests.
13
+ #
14
+ #===Setup
15
+ # When using this authentication method, there is a bit more setup involved to make the client
16
+ # work. All applications need to extend the BaseOAuth2Provider class to supply the application
17
+ # specific configuration. Also depending on the application type (command line, native, or web
18
+ # based), the user authentication step will be handled differently.
19
+
20
+ #==OAuth2
21
+ # Implementation the BaseAuth interface for API style authentication
22
+ class OAuth2 < BaseAuth
23
+
24
+ def initialize(client)
25
+ super(client)
26
+ @provider = client.oauth2_provider
27
+ end
28
+
29
+ def session
30
+ @provider.load_session()
31
+ end
32
+ def session=(s)
33
+ @provider.save_session(s)
34
+ end
35
+
36
+ def authenticate
37
+ granter = OAuth2Impl::GrantTypeBase.create(@client, @provider, session)
38
+ self.session = granter.authenticate
39
+ session
40
+ end
41
+
42
+ # Perform an HTTP request (no data)
43
+ def request(method, path, body, options={})
44
+ escaped_path = URI.escape(path)
45
+ connection = @client.connection(true) # SSL Only!
46
+ connection.headers.merge!(self.auth_header)
47
+ parameter_string = options.size > 0 ? "?#{build_url_parameters(options)}" : ""
48
+ request_path = "#{escaped_path}#{parameter_string}"
49
+ FlexmlsApi.logger.debug("Request: #{request_path}")
50
+ if body.nil?
51
+ response = connection.send(method, request_path)
52
+ else
53
+ FlexmlsApi.logger.debug("Data: #{body}")
54
+ response = connection.send(method, request_path, body)
55
+ end
56
+ response
57
+ end
58
+
59
+ def logout
60
+ @provider.save_session(nil)
61
+ end
62
+
63
+ def authorization_url()
64
+ params = {
65
+ "client_id" => @provider.client_id,
66
+ "response_type" => "code",
67
+ "redirect_uri" => @provider.redirect_uri
68
+ }
69
+ "#{@provider.authorization_uri}?#{build_url_parameters(params)}"
70
+ end
71
+
72
+
73
+ protected
74
+
75
+ def auth_header
76
+ {"Authorization"=> "OAuth #{session.access_token}"}
77
+ end
78
+
79
+ def provider
80
+ @provider
81
+ end
82
+ def client
83
+ @client
84
+ end
85
+
86
+ end
87
+
88
+ # Representation of a session with the api using oauth2
89
+ class OAuthSession
90
+ SESSION_ATTRIBUTES = [:access_token, :expires_in, :scope, :refresh_token, :refresh_timeout, :start_time]
91
+ attr_accessor *SESSION_ATTRIBUTES
92
+ def initialize(options={})
93
+ @access_token = options["access_token"]
94
+ @expires_in = options["expires_in"]
95
+ @scope = options["scope"]
96
+ @refresh_token = options["refresh_token"]
97
+ @start_time = options.fetch("start_time", DateTime.now)
98
+ @refresh_timeout = options.fetch("refresh_timeout",3600)
99
+ if @start_time.is_a? String
100
+ @start_time = DateTime.parse(@start_time)
101
+ end
102
+ end
103
+ # Is the user session token expired?
104
+ def expired?
105
+ @start_time + Rational(@expires_in - @refresh_timeout, 86400) < DateTime.now
106
+ end
107
+
108
+ def to_json(*a)
109
+ hash = {}
110
+ SESSION_ATTRIBUTES.each do |k|
111
+ value = self.send(k)
112
+ hash[k.to_s] = value unless value.nil?
113
+ end
114
+ hash.to_json(*a)
115
+ end
116
+ end
117
+
118
+ #=OAuth2 configuration provider for applications
119
+ # Applications planning to use OAuth2 authentication with the API must extend this class as
120
+ # part of the client configuration, providing values for the following attributes:
121
+ # @authorization_uri - User oauth2 login page for flexmls
122
+ # @access_uri - Location of the OAuth2 access token resource for the api. OAuth2 code and
123
+ # credentials will be sent to this uri to generate an access token.
124
+ # @redirect_uri - Application uri to redirect to
125
+ # @client_id - OAuth2 provided application identifier
126
+ # @client_secret - OAuth2 provided password for the client id
127
+ class BaseOAuth2Provider
128
+ attr_accessor *Configuration::OAUTH2_KEYS
129
+ # Requirements for authorization_code grant type
130
+ attr_accessor :code
131
+ attr_accessor :grant_type
132
+
133
+ def initialize(opts={})
134
+ Configuration::OAUTH2_KEYS.each do |key|
135
+ send("#{key}=", opts[key]) if opts.include? key
136
+ end
137
+ @grant_type = :authorization_code
138
+ end
139
+
140
+ def grant_type
141
+ # backwards compatibility check
142
+ @grant_type.nil? ? :authorization_code : @grant_type
143
+ end
144
+
145
+ # Application using the client must handle user redirect for user authentication. For
146
+ # command line applications, this method is called prior to initial client requests so that
147
+ # the process can notify the user to go to the url and retrieve the access_code for the app.
148
+ # In a web based web application, this method can be mostly ignored. However, the web based
149
+ # application is then responsible for ensuring the code is saved to the the provider instance
150
+ # prior to any client requests are performed (or the error below will be thrown).
151
+ def redirect(url)
152
+ raise "To be implemented by client application"
153
+ end
154
+
155
+ #==For any persistence to be supported outside application process, the application shall
156
+ # implement the following methods for storing and retrieving the user OAuth2 session
157
+ # (e.g. to and from memcached).
158
+
159
+ # Load the current OAuth session
160
+ # returns - active OAuthSession or nil
161
+ def load_session
162
+ nil
163
+ end
164
+
165
+ # Save current session
166
+ # session - active OAuthSession
167
+ def save_session(session)
168
+
169
+ end
170
+
171
+ # Provides a default session time out
172
+ # returns - the session timeout length (in seconds)
173
+ def session_timeout
174
+ 86400 # 1.day
175
+ end
176
+
177
+ end
178
+
179
+ module OAuth2Impl
180
+ require 'flexmls_api/authentication/oauth2_impl/middleware'
181
+ require 'flexmls_api/authentication/oauth2_impl/grant_type_base'
182
+ require 'flexmls_api/authentication/oauth2_impl/grant_type_refresh'
183
+ require 'flexmls_api/authentication/oauth2_impl/grant_type_code'
184
+ require 'flexmls_api/authentication/oauth2_impl/grant_type_password'
185
+ require 'flexmls_api/authentication/oauth2_impl/password_provider'
186
+
187
+ # Loads a provider class from a string
188
+ def self.load_provider(string, args={})
189
+ constant = Object
190
+ string.split("::").compact.each { |name| constant = constant.const_get(name) unless name == ""}
191
+ constant.new(args)
192
+ rescue => e
193
+ raise ArgumentError, "The value '#{string}' is an invalid class name for an oauth2 provider: #{e.message}"
194
+ end
195
+ end
196
+
197
+ end
198
+
199
+ end
@@ -0,0 +1,87 @@
1
+ module SparkApi
2
+ module Authentication
3
+ module OAuth2Impl
4
+ class GrantTypeBase
5
+ GRANT_TYPES = [:authorization_code, :password, :refresh_token]
6
+
7
+ def self.create(client, provider, session=nil)
8
+ granter = nil
9
+ case provider.grant_type
10
+ when :authorization_code
11
+ granter = GrantTypeCode.new(client, provider, session)
12
+ when :password
13
+ granter = GrantTypePassword.new(client, provider, session)
14
+ # This method should only be used internally to the library
15
+ when :refresh_token
16
+ granter = GrantTypeRefresh.new(client, provider, session)
17
+ else
18
+ raise ClientError, "Unsupported grant type [#{provider.grant_type}]"
19
+ end
20
+ SparkApi.logger.debug("[oauth2] setup #{granter.class.name}")
21
+ granter
22
+ end
23
+
24
+ attr_reader :provider, :client, :session
25
+ def initialize(client, provider, session)
26
+ @client = client
27
+ @provider = provider
28
+ @session = session
29
+ end
30
+ def authenticate
31
+
32
+ end
33
+
34
+ def refresh
35
+
36
+ end
37
+
38
+ protected
39
+
40
+ def create_session(token_params)
41
+ SparkApi.logger.debug("[oauth2] create_session to #{provider.access_uri} params #{token_params}")
42
+ uri = URI.parse(provider.access_uri)
43
+ request_path = "#{uri.path}"
44
+ response = oauth_access_connection("#{uri.scheme}://#{uri.host}").post(request_path, "#{token_params}").body
45
+ response.expires_in = provider.session_timeout if response.expires_in.nil?
46
+ SparkApi.logger.debug("[oauth2] New session created #{response}")
47
+ response
48
+ end
49
+
50
+ def needs_refreshing?
51
+ !@session.nil? && !@session.refresh_token.nil? && @session.expired?
52
+ end
53
+
54
+ # Generate the appropriate request uri for authorizing this application for current user.
55
+ def authorization_url()
56
+ params = {
57
+ "client_id" => @provider.client_id,
58
+ "response_type" => "code",
59
+ "redirect_uri" => @provider.redirect_uri
60
+ }
61
+ "#{@provider.authorization_uri}?#{build_url_parameters(params)}"
62
+ end
63
+
64
+ # Setup a faraday connection for dealing with an OAuth2 endpoint
65
+ def oauth_access_connection(endpoint)
66
+ opts = {
67
+ :headers => @client.headers
68
+ }
69
+ opts[:ssl] = {:verify => false }
70
+ opts[:url] = endpoint
71
+ conn = Faraday::Connection.new(opts) do |builder|
72
+ builder.adapter Faraday.default_adapter
73
+ builder.use SparkApi::Authentication::OAuth2Impl::Middleware
74
+ end
75
+ end
76
+ def build_url_parameters(parameters={})
77
+ array = parameters.map do |key,value|
78
+ escaped_value = CGI.escape("#{value}")
79
+ "#{key}=#{escaped_value}"
80
+ end
81
+ array.join "&"
82
+ end
83
+ end
84
+
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,87 @@
1
+ module FlexmlsApi
2
+ module Authentication
3
+ module OAuth2Impl
4
+ class GrantTypeBase
5
+ GRANT_TYPES = [:authorization_code, :password, :refresh_token]
6
+
7
+ def self.create(client, provider, session=nil)
8
+ granter = nil
9
+ case provider.grant_type
10
+ when :authorization_code
11
+ granter = GrantTypeCode.new(client, provider, session)
12
+ when :password
13
+ granter = GrantTypePassword.new(client, provider, session)
14
+ # This method should only be used internally to the library
15
+ when :refresh_token
16
+ granter = GrantTypeRefresh.new(client, provider, session)
17
+ else
18
+ raise ClientError, "Unsupported grant type [#{provider.grant_type}]"
19
+ end
20
+ FlexmlsApi.logger.debug("[oauth2] setup #{granter.class.name}")
21
+ granter
22
+ end
23
+
24
+ attr_reader :provider, :client, :session
25
+ def initialize(client, provider, session)
26
+ @client = client
27
+ @provider = provider
28
+ @session = session
29
+ end
30
+ def authenticate
31
+
32
+ end
33
+
34
+ def refresh
35
+
36
+ end
37
+
38
+ protected
39
+
40
+ def create_session(token_params)
41
+ FlexmlsApi.logger.debug("[oauth2] create_session to #{provider.access_uri} params #{token_params}")
42
+ uri = URI.parse(provider.access_uri)
43
+ request_path = "#{uri.path}"
44
+ response = oauth_access_connection("#{uri.scheme}://#{uri.host}").post(request_path, "#{token_params}").body
45
+ response.expires_in = provider.session_timeout if response.expires_in.nil?
46
+ FlexmlsApi.logger.debug("[oauth2] New session created #{response}")
47
+ response
48
+ end
49
+
50
+ def needs_refreshing?
51
+ !@session.nil? && !@session.refresh_token.nil? && @session.expired?
52
+ end
53
+
54
+ # Generate the appropriate request uri for authorizing this application for current user.
55
+ def authorization_url()
56
+ params = {
57
+ "client_id" => @provider.client_id,
58
+ "response_type" => "code",
59
+ "redirect_uri" => @provider.redirect_uri
60
+ }
61
+ "#{@provider.authorization_uri}?#{build_url_parameters(params)}"
62
+ end
63
+
64
+ # Setup a faraday connection for dealing with an OAuth2 endpoint
65
+ def oauth_access_connection(endpoint)
66
+ opts = {
67
+ :headers => @client.headers
68
+ }
69
+ opts[:ssl] = {:verify => false }
70
+ opts[:url] = endpoint
71
+ conn = Faraday::Connection.new(opts) do |builder|
72
+ builder.adapter Faraday.default_adapter
73
+ builder.use FlexmlsApi::Authentication::OAuth2Impl::Middleware
74
+ end
75
+ end
76
+ def build_url_parameters(parameters={})
77
+ array = parameters.map do |key,value|
78
+ escaped_value = CGI.escape("#{value}")
79
+ "#{key}=#{escaped_value}"
80
+ end
81
+ array.join "&"
82
+ end
83
+ end
84
+
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,48 @@
1
+ module SparkApi
2
+ module Authentication
3
+ module OAuth2Impl
4
+ # OAuth2 authentication flow using username and password parameters for the user in the
5
+ # request. This implementation is geared towards authentication styles for web applications
6
+ # that have a OAuth flow for redirects.
7
+ class GrantTypeCode < GrantTypeBase
8
+ def initialize(client, provider, session)
9
+ super(client, provider, session)
10
+ end
11
+ def authenticate
12
+ if(provider.code.nil?)
13
+ SparkApi.logger.debug("[oauth2] No authoriztion code present. Redirecting to #{authorization_url}.")
14
+ provider.redirect(authorization_url)
15
+ end
16
+ if needs_refreshing?
17
+ new_session = refresh
18
+ end
19
+ return new_session unless new_session.nil?
20
+ create_session(token_params)
21
+ end
22
+
23
+ def refresh()
24
+ SparkApi.logger.debug("[oauth2] Refresh oauth session.")
25
+ refresher = GrantTypeRefresh.new(client,provider,session)
26
+ refresher.params = {"redirect_uri" => @provider.redirect_uri}
27
+ refresher.authenticate
28
+ rescue ClientError => e
29
+ SparkApi.logger.info("[oauth2] Refreshing token failed, the library will try and authenticate from scratch: #{e.message}")
30
+ nil
31
+ end
32
+
33
+ private
34
+ def token_params
35
+ params = {
36
+ "client_id" => @provider.client_id,
37
+ "client_secret" => @provider.client_secret,
38
+ "grant_type" => "authorization_code",
39
+ "code" => @provider.code,
40
+ "redirect_uri" => @provider.redirect_uri
41
+ }.to_json
42
+ end
43
+
44
+ end
45
+
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,49 @@
1
+
2
+ module FlexmlsApi
3
+ module Authentication
4
+ module OAuth2Impl
5
+ # OAuth2 authentication flow using username and password parameters for the user in the
6
+ # request. This implementation is geared towards authentication styles for web applications
7
+ # that have a OAuth flow for redirects.
8
+ class GrantTypeCode < GrantTypeBase
9
+ def initialize(client, provider, session)
10
+ super(client, provider, session)
11
+ end
12
+ def authenticate
13
+ if(provider.code.nil?)
14
+ FlexmlsApi.logger.debug("[oauth2] No authoriztion code present. Redirecting to #{authorization_url}.")
15
+ provider.redirect(authorization_url)
16
+ end
17
+ if needs_refreshing?
18
+ new_session = refresh
19
+ end
20
+ return new_session unless new_session.nil?
21
+ create_session(token_params)
22
+ end
23
+
24
+ def refresh()
25
+ FlexmlsApi.logger.debug("[oauth2] Refresh oauth session.")
26
+ refresher = GrantTypeRefresh.new(client,provider,session)
27
+ refresher.params = {"redirect_uri" => @provider.redirect_uri}
28
+ refresher.authenticate
29
+ rescue ClientError => e
30
+ FlexmlsApi.logger.info("[oauth2] Refreshing token failed, the library will try and authenticate from scratch: #{e.message}")
31
+ nil
32
+ end
33
+
34
+ private
35
+ def token_params
36
+ params = {
37
+ "client_id" => @provider.client_id,
38
+ "client_secret" => @provider.client_secret,
39
+ "grant_type" => "authorization_code",
40
+ "code" => @provider.code,
41
+ "redirect_uri" => @provider.redirect_uri
42
+ }.to_json
43
+ end
44
+
45
+ end
46
+
47
+ end
48
+ end
49
+ end