playlyfe 0.2.2 → 0.4.5

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.
data/lib/playlyfe.rb CHANGED
@@ -1,86 +1,202 @@
1
+ require 'uri'
1
2
  require 'json'
2
- require 'error'
3
- require 'client'
4
- require 'base'
5
- require 'strategy'
6
- require 'client_credentials'
7
- require 'access_token'
8
- require 'response'
3
+ require 'rest_client'
9
4
 
10
- class Playlyfe
11
- @@client = nil
12
- @@token = nil
13
- @@api = 'http://api.playlyfe.com/v1'
14
- @@player = ""
15
- @@debug = true
16
-
17
- def self.token
18
- @@token
5
+ class PlaylyfeError < StandardError
6
+ attr_accessor :name, :message
7
+ def initialize(res)
8
+ message = []
9
+ begin
10
+ res = JSON.parse(res)
11
+ @name = res['error']
12
+ @message = res['error_description']
13
+ message << "#{@code}: #{@description}"
14
+ rescue => e
15
+ end
16
+ super(message.join("\n"))
19
17
  end
18
+ end
19
+
20
+ class Playlyfe
21
+ @@api = 'https://api.playlyfe.com/v1'
20
22
 
21
23
  # You can initiate a client by giving the client_id and client_secret params
22
24
  # This will authorize a client and get the token
25
+ #
26
+ # @param [Hash] options the options to make the request with
27
+ # @param options
28
+ # [Boolean] :type where where type can be 'code', 'client' for
29
+ # for the oauth2 auth code flow and client credentials flow
30
+ # [lambda] :store a method that persists the access_token into
31
+ # a database
32
+ # [lambda] :retrieve a method that is used by the sdk internally
33
+ # to read the access_token and use it in all your requests
23
34
  def self.init(options = {})
24
35
  puts 'Playlyfe Initializing...............................................'
36
+ if options[:type].nil?
37
+ err = PlaylyfeError.new("")
38
+ err.name = 'init_failed'
39
+ err.message = "You must pass in a type whether 'client' for client credentials flow or 'code' for auth code flow"
40
+ raise err
41
+ end
42
+ @@type = options[:type]
43
+ @@id = options[:client_id]
44
+ @@secret = options[:client_secret]
45
+ @@store = options[:store]
46
+ @@retrieve = options[:retrieve]
47
+ if @@store.nil?
48
+ @@store = lambda { |token| puts 'Storing Token' }
49
+ end
50
+ if @@type == 'client'
51
+ self.get_access_token()
52
+ else
53
+ if options[:redirect_uri].nil?
54
+ err = PlaylyfeError.new("")
55
+ err.name = 'init_failed'
56
+ err.message = 'You must pass in a redirect_uri for the auth code flow'
57
+ raise err
58
+ else
59
+ puts 'CLIENT'
60
+ #RestClient.get("https://playlyfe.com/auth?redirect_uri=#{options[:redirect_uri]}&response_type=code&client_id=#{@@id}")
61
+ #:authorize_url =>
62
+ #'response_type' => 'code', 'client_id' => @@id
63
+ #auth_url = @@client.auth_code.authorize_url(:redirect_uri => 'http://localhost:8080/oauth2/callback')
64
+ #@@client.auth_code.get_token('code_value', :redirect_uri => 'http://localhost:8080/oauth2/callback') #check query.code then make post request
65
+ end
66
+ end
67
+ #RestClient.log = Logger.new(STDOUT)
68
+ end
69
+
70
+ def self.get_access_token
71
+ puts 'Getting Access Token'
25
72
  begin
26
- @@client = OAuth2::Client.new(
27
- options[:client_id],
28
- options[:client_secret],
29
- :authorize_url => "/auth",
30
- :token_url => "/auth/token",
31
- :site => "http://playlyfe.com"
73
+ access_token = RestClient.post('https://playlyfe.com/auth/token',
74
+ {
75
+ :client_id => @@id,
76
+ :client_secret => @@secret,
77
+ :grant_type => 'client_credentials'
78
+ }.to_json,
79
+ :content_type => :json,
80
+ :accept => :json
32
81
  )
33
- rescue OAuth2::Error => e
34
- puts 'OAuth2 Error---------------------------------------'
35
- puts
36
- puts e
82
+ access_token = JSON.parse(access_token)
83
+ expires_at ||= Time.now.to_i + access_token['expires_in']
84
+ access_token.delete('expires_in')
85
+ access_token['expires_at'] = expires_at
86
+ @@store.call access_token
87
+ if @@retrieve.nil?
88
+ @@retrieve = lambda { return access_token }
89
+ end
90
+ rescue => e
91
+ raise PlaylyfeError.new(e.response)
92
+ end
93
+ end
94
+
95
+ def self.check_expired(access_token)
96
+ if access_token['expires_at'] < Time.now.to_i
97
+ puts 'Access Token Expired'
98
+ self.get_access_token()
37
99
  end
38
- create_token()
39
100
  end
40
101
 
41
- def self.create_token
42
- @@token = @@client.client_credentials.get_token({}, {'auth_scheme' => 'request_body', 'mode' => 'query'})
102
+ # def refresh!(params = {})
103
+ # params.merge!(:client_id => @client.id,
104
+ # :client_secret => @client.secret,
105
+ # :grant_type => 'refresh_token',
106
+ # :refresh_token => refresh_token)
107
+ # new_token = @client.get_token(params)
108
+ # new_token.options = options
109
+ # new_token.refresh_token = refresh_token unless new_token.refresh_token
110
+ # new_token
111
+ # end
112
+
113
+ def self.login
43
114
  end
44
115
 
45
116
  def self.get(options = {})
46
- options[:player] ||= ''
47
- options[:query] ||= ''
117
+ options[:route] ||= ''
118
+ options[:query] ||= {}
48
119
  options[:raw] ||= false
120
+
121
+ access_token = @@retrieve.call
122
+ self.check_expired(access_token)
123
+ options[:query][:access_token] = access_token['access_token']
124
+
49
125
  begin
50
- response = @@token.get("#{@@api}#{options[:url]}?debug=true&player_id=#{options[:player]}&#{options[:query]}")
126
+ res = RestClient.get("#{@@api}#{options[:route]}",
127
+ {:params => options[:query] }
128
+ )
51
129
  if options[:raw] == true
52
- return response
53
- end
54
- json = JSON.parse(response.body)
55
- if @@debug
56
- puts "Playlyfe: GET #{@@api}#{options[:url]}?debug=true&player_id=#{options[:player]}&#{options[:query]}"
57
- puts
58
- puts json
59
- end
60
- return json
61
- rescue OAuth2::Error => e
62
- puts 'OAuth2 Error'
63
- puts e.code
64
- puts e.description
65
- if e.code == 'invalid_token'
66
- create_token()
67
- get()
130
+ return res.body
131
+ else
132
+ return JSON.parse(res.body)
68
133
  end
134
+ rescue => e
135
+ raise PlaylyfeError.new(e.response)
69
136
  end
70
137
  end
71
138
 
72
139
  def self.post(options = {})
73
- options[:player] ||= ''
74
- opts = {}
75
- opts[:headers] ||= {'Content-Type' => 'application/json'}
76
- opts[:body] = JSON.generate(options[:body])
77
- response = @@token.post("#{@@api}#{options[:url]}?debug=true&player_id=#{options[:player]}", opts)
78
- json = JSON.parse(response.body)
79
- if @@debug
80
- puts "Playlyfe: POST #{@@api}#{options[:url]}?debug=true&player_id=#{options[:player]}"
81
- puts
82
- puts json
140
+ options[:route] ||= ''
141
+ options[:query] ||= {}
142
+ options[:body] ||= {}
143
+
144
+ access_token = @@retrieve.call
145
+ self.check_expired(access_token)
146
+ options[:query][:access_token] = access_token['access_token']
147
+
148
+ begin
149
+ res = RestClient.post("#{@@api}#{options[:route]}?#{self.hash_to_query(options[:query])}",
150
+ options[:body].to_json,
151
+ :content_type => :json,
152
+ :accept => :json
153
+ )
154
+ return JSON.parse(res.body)
155
+ rescue => e
156
+ raise PlaylyfeError.new(e.response)
83
157
  end
84
- return json
158
+ end
159
+
160
+ def self.patch(options = {})
161
+ options[:route] ||= ''
162
+ options[:query] ||= {}
163
+ options[:body] ||= {}
164
+
165
+ access_token = @@retrieve.call
166
+ self.check_expired(access_token)
167
+ options[:query][:access_token] = access_token['access_token']
168
+
169
+ begin
170
+ res = RestClient.patch("#{@@api}#{options[:route]}?#{self.hash_to_query(options[:query])}",
171
+ options[:body].to_json,
172
+ :content_type => :json,
173
+ :accept => :json
174
+ )
175
+ return JSON.parse(res.body)
176
+ rescue => e
177
+ raise PlaylyfeError.new(e.response)
178
+ end
179
+ end
180
+
181
+ def self.delete(options = {})
182
+ options[:route] ||= ''
183
+ options[:query] ||= {}
184
+
185
+ access_token = @@retrieve.call
186
+ self.check_expired(access_token)
187
+ options[:query][:access_token] = access_token['access_token']
188
+
189
+ begin
190
+ res = RestClient.delete("#{@@api}#{options[:route]}",
191
+ {:params => options[:query] }
192
+ )
193
+ JSON.parse(res.body)
194
+ rescue => e
195
+ raise PlaylyfeError.new(e.response)
196
+ end
197
+ end
198
+
199
+ def self.hash_to_query(hash)
200
+ return URI.encode(hash.map{|k,v| "#{k}=#{v}"}.join("&"))
85
201
  end
86
202
  end
data/test/test.rb CHANGED
@@ -1,31 +1,137 @@
1
1
  require 'test/unit'
2
+ require 'redis'
2
3
  require 'playlyfe'
3
4
 
4
5
  class PlaylyfeTest < Test::Unit::TestCase
5
6
 
6
- def test_start
7
+ def test_invalid_client
8
+ begin
9
+ Playlyfe.init(
10
+ client_id: "wrong_id",
11
+ client_secret: "wrong_secret",
12
+ type: 'client'
13
+ )
14
+ rescue PlaylyfeError => e
15
+ assert_equal e.name,'client_auth_fail'
16
+ assert_equal e.message, 'Client authentication failed'
17
+ end
18
+ end
19
+
20
+ def test_wrong_init
21
+ begin
22
+ Playlyfe.init(
23
+ client_id: "Zjc0MWU0N2MtODkzNS00ZWNmLWEwNmYtY2M1MGMxNGQ1YmQ4",
24
+ client_secret: "YzllYTE5NDQtNDMwMC00YTdkLWFiM2MtNTg0Y2ZkOThjYTZkMGIyNWVlNDAtNGJiMC0xMWU0LWI2NGEtYjlmMmFkYTdjOTI3"
25
+ )
26
+ rescue PlaylyfeError => e
27
+ assert_equal e.name, 'init_failed'
28
+ end
29
+ end
30
+
31
+ def test_wrong_api
32
+ begin
33
+ Playlyfe.init(
34
+ client_id: "Zjc0MWU0N2MtODkzNS00ZWNmLWEwNmYtY2M1MGMxNGQ1YmQ4",
35
+ client_secret: "YzllYTE5NDQtNDMwMC00YTdkLWFiM2MtNTg0Y2ZkOThjYTZkMGIyNWVlNDAtNGJiMC0xMWU0LWI2NGEtYjlmMmFkYTdjOTI3",
36
+ type: 'client'
37
+ )
38
+ Playlyfe.get(route: '/gege', query: { player_id: 'student1' })
39
+ rescue PlaylyfeError => e
40
+ assert_equal e.name,'route_not_found'
41
+ assert_equal e.message, 'This route does not exist'
42
+ end
43
+ end
44
+
45
+ def test_init_staging
7
46
  Playlyfe.init(
8
- client_id: "NjZlZWFiMWEtN2ZiOC00YmVmLTk2YWMtNDEyYTNmMjQ5NDBl",
9
- client_secret: "NGRhMWJlYzUtODJiNS00ZTdkLTgzNTctMTQyMGEzNTljN2MwMTU2MGM3NTAtZjZiZS0xMWUzLTgxZjYtNmZhMGYyMzRkZGU3"
47
+ client_id: "Zjc0MWU0N2MtODkzNS00ZWNmLWEwNmYtY2M1MGMxNGQ1YmQ4",
48
+ client_secret: "YzllYTE5NDQtNDMwMC00YTdkLWFiM2MtNTg0Y2ZkOThjYTZkMGIyNWVlNDAtNGJiMC0xMWU0LWI2NGEtYjlmMmFkYTdjOTI3",
49
+ type: 'client'
10
50
  )
11
- puts Playlyfe.token.to_hash
12
-
13
- players = Playlyfe.get(url: '/players')
51
+ players = Playlyfe.get(route: '/players', query: { player_id: 'student1', limit: 1 })
14
52
  assert_not_nil players["data"]
15
53
  assert_not_nil players["data"][0]
16
54
 
55
+ begin
56
+ assert_nil Playlyfe.get(route: '/player')
57
+ rescue PlaylyfeError => e
58
+ assert_equal e.message, "The 'player_id' parameter should be specified in the query"
59
+ end
60
+
17
61
  player_id = 'student1'
18
- player = Playlyfe.get(url: '/player', player: player_id)
62
+ player = Playlyfe.get(route: '/player', query: { player_id: player_id } )
19
63
  assert_equal player["id"], "student1"
20
64
  assert_equal player["alias"], "Student1"
21
65
  assert_equal player["enabled"], true
22
66
 
23
- no_player = Playlyfe.get(url: '/player')
24
- assert_nil no_player
67
+ Playlyfe.get(route: '/definitions/processes', query: { player_id: player_id } )
68
+ Playlyfe.get(route:'/definitions/teams', query: { player_id: player_id } )
69
+ Playlyfe.get(route: '/processes', query: { player_id: player_id } )
70
+ Playlyfe.get(route: '/teams', query: { player_id: player_id } )
71
+
72
+ processes = Playlyfe.get(route: '/processes', query: { player_id: 'student1', limit: 1, skip: 4 })
73
+ assert_equal processes["data"][0]["definition"], "module1"
74
+ assert_equal processes["data"].size, 1
75
+
76
+ new_process = Playlyfe.post(route: '/definitions/processes/module1', query: { player_id: player_id })
77
+ assert_equal new_process["definition"], "module1"
78
+ assert_equal new_process["state"], "ACTIVE"
79
+
80
+ patched_process = Playlyfe.patch(
81
+ route: "/processes/#{new_process['id']}",
82
+ query: { player_id: player_id },
83
+ body: { name: 'patched_process', access: 'PUBLIC' }
84
+ )
85
+
86
+ assert_equal patched_process['name'], 'patched_process'
87
+ assert_equal patched_process['access'], 'PUBLIC'
88
+
89
+ deleted_process = Playlyfe.delete(route: "/processes/#{new_process['id']}", query: { player_id: player_id })
90
+ assert_not_nil deleted_process['message']
91
+ end
25
92
 
26
- Playlyfe.get(url: "/definitions/processes", player: player_id)
27
- Playlyfe.get(url: "/definitions/teams", player: player_id)
28
- Playlyfe.get(url: "/processes", player: player_id)
29
- Playlyfe.get(url: "/teams", player: player_id)
93
+ def test_init_production
94
+ Playlyfe.init(
95
+ client_id: "N2Y4NjNlYTItODQzZi00YTQ0LTkzZWEtYTBiNTA2ODg3MDU4",
96
+ client_secret: "NDc3NTA0NmItMjBkZi00MjI2LWFhMjUtOTI0N2I1YTkxYjc2M2U3ZGI0MDAtNGQ1Mi0xMWU0LWJmZmUtMzkyZTdiOTYxYmMx",
97
+ type: 'client'
98
+ )
99
+ #player = Playlyfe.get(route: '/players', query: { player_id: 'l54328754bddc332e0021a847', limit: 1 })
100
+ #assert_equal player["data"][0]["email"], "peter@playlyfe.com"
101
+ end
102
+
103
+ def test_store
104
+ redis = Redis.new
105
+ Playlyfe.init(
106
+ client_id: "Zjc0MWU0N2MtODkzNS00ZWNmLWEwNmYtY2M1MGMxNGQ1YmQ4",
107
+ client_secret: "YzllYTE5NDQtNDMwMC00YTdkLWFiM2MtNTg0Y2ZkOThjYTZkMGIyNWVlNDAtNGJiMC0xMWU0LWI2NGEtYjlmMmFkYTdjOTI3",
108
+ type: 'client',
109
+ store: lambda { |token| redis.set('token', JSON.generate(token)) },
110
+ retrieve: lambda { return JSON.parse(redis.get('token')) }
111
+ )
112
+ players = Playlyfe.get(route: '/players', query: { player_id: 'student1', limit: 1 })
113
+ assert_not_nil players["data"]
114
+ assert_not_nil players["data"][0]
115
+ end
116
+
117
+ def test_auth_code_error
118
+ begin
119
+ Playlyfe.init(
120
+ client_id: "NGM2ZmYyNGQtNjViMy00YjQ0LWI0YTgtZTdmYWFlNDRkMmUx",
121
+ client_secret: "ZTQ0OWI4YTItYzE4ZC00MWQ5LTg3YjktMDI5ZjAxYTBkZmRiZGQ0NzI4OTAtNGQ1My0xMWU0LWJmZmUtMzkyZTdiOTYxYmMx",
122
+ type: 'code'
123
+ )
124
+ rescue PlaylyfeError => e
125
+ assert_equal e.name, 'init_failed'
126
+ end
127
+ end
128
+
129
+ def test_auth_code
130
+ Playlyfe.init(
131
+ client_id: "NGM2ZmYyNGQtNjViMy00YjQ0LWI0YTgtZTdmYWFlNDRkMmUx",
132
+ client_secret: "ZTQ0OWI4YTItYzE4ZC00MWQ5LTg3YjktMDI5ZjAxYTBkZmRiZGQ0NzI4OTAtNGQ1My0xMWU0LWJmZmUtMzkyZTdiOTYxYmMx",
133
+ type: 'code',
134
+ redirect_uri: 'https://playlyfe.com/v1/api'
135
+ )
30
136
  end
31
137
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: playlyfe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.4.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,16 +9,16 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-06-19 00:00:00.000000000 Z
12
+ date: 2014-10-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: faraday
15
+ name: rest_client
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - '='
20
20
  - !ruby/object:Gem::Version
21
- version: '0.8'
21
+ version: 1.7.2
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -26,15 +26,15 @@ dependencies:
26
26
  requirements:
27
27
  - - '='
28
28
  - !ruby/object:Gem::Version
29
- version: '0.8'
29
+ version: 1.7.2
30
30
  - !ruby/object:Gem::Dependency
31
- name: jwt
31
+ name: json
32
32
  requirement: !ruby/object:Gem::Requirement
33
33
  none: false
34
34
  requirements:
35
35
  - - '='
36
36
  - !ruby/object:Gem::Version
37
- version: '1.0'
37
+ version: 1.8.1
38
38
  type: :runtime
39
39
  prerelease: false
40
40
  version_requirements: !ruby/object:Gem::Requirement
@@ -42,23 +42,39 @@ dependencies:
42
42
  requirements:
43
43
  - - '='
44
44
  - !ruby/object:Gem::Version
45
- version: '1.0'
45
+ version: 1.8.1
46
46
  - !ruby/object:Gem::Dependency
47
- name: json
47
+ name: rake
48
48
  requirement: !ruby/object:Gem::Requirement
49
49
  none: false
50
50
  requirements:
51
- - - '='
51
+ - - ! '>='
52
52
  - !ruby/object:Gem::Version
53
- version: 1.8.1
54
- type: :runtime
53
+ version: '0'
54
+ type: :development
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  none: false
58
58
  requirements:
59
- - - '='
59
+ - - ! '>='
60
60
  - !ruby/object:Gem::Version
61
- version: 1.8.1
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: redis
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
62
78
  description: This gem can be used to interact with the playlyfe gamification platform
63
79
  using oauth 2.0
64
80
  email: peter@playlyfe.com
@@ -68,17 +84,9 @@ extra_rdoc_files: []
68
84
  files:
69
85
  - Rakefile
70
86
  - lib/playlyfe.rb
71
- - lib/access_token.rb
72
- - lib/base.rb
73
- - lib/client.rb
74
- - lib/client_credentials.rb
75
- - lib/error.rb
76
- - lib/response.rb
77
- - lib/strategy.rb
78
87
  - test/test.rb
79
88
  homepage: https://github.com/pyros2097/playlyfe-ruby-sdk
80
- licenses:
81
- - ApacheV2
89
+ licenses: []
82
90
  post_install_message:
83
91
  rdoc_options: []
84
92
  require_paths:
@@ -100,7 +108,7 @@ rubyforge_project:
100
108
  rubygems_version: 1.8.23
101
109
  signing_key:
102
110
  specification_version: 3
103
- summary: The playlyfe ruby sdk
111
+ summary: The Playlyfe Ruby SDK
104
112
  test_files:
105
113
  - test/test.rb
106
114
  has_rdoc:
data/lib/access_token.rb DELETED
@@ -1,177 +0,0 @@
1
- module OAuth2
2
- class AccessToken
3
- attr_reader :client, :token, :expires_in, :expires_at, :params
4
- attr_accessor :options, :refresh_token
5
-
6
- def self.from_hash(client, hash)
7
- new(client, hash.delete('access_token') || hash.delete(:access_token), hash)
8
- end
9
-
10
- class << self
11
- # Initializes an AccessToken from a Hash
12
- #
13
- # @param [Client] the OAuth2::Client instance
14
- # @param [Hash] a hash of AccessToken property values
15
- # @return [AccessToken] the initalized AccessToken
16
- def from_hash(client, hash)
17
- new(client, hash.delete('access_token') || hash.delete(:access_token), hash)
18
- end
19
-
20
- # Initializes an AccessToken from a key/value application/x-www-form-urlencoded string
21
- #
22
- # @param [Client] client the OAuth2::Client instance
23
- # @param [String] kvform the application/x-www-form-urlencoded string
24
- # @return [AccessToken] the initalized AccessToken
25
- def from_kvform(client, kvform)
26
- from_hash(client, Rack::Utils.parse_query(kvform))
27
- end
28
- end
29
-
30
- # Initalize an AccessToken
31
- #
32
- # @param [Client] client the OAuth2::Client instance
33
- # @param [String] token the Access Token value
34
- # @param [Hash] opts the options to create the Access Token with
35
- # @option opts [String] :refresh_token (nil) the refresh_token value
36
- # @option opts [FixNum, String] :expires_in (nil) the number of seconds in which the AccessToken will expire
37
- # @option opts [FixNum, String] :expires_at (nil) the epoch time in seconds in which AccessToken will expire
38
- # @option opts [Symbol] :mode (:header) the transmission mode of the Access Token parameter value
39
- # one of :header, :body or :query
40
- # @option opts [String] :header_format ('Bearer %s') the string format to use for the Authorization header
41
- # @option opts [String] :param_name ('access_token') the parameter name to use for transmission of the
42
- # Access Token value in :body or :query transmission mode
43
- def initialize(client, token, opts = {})
44
- @client = client
45
- @token = token.to_s
46
- [:refresh_token, :expires_in, :expires_at].each do |arg|
47
- instance_variable_set("@#{arg}", opts.delete(arg) || opts.delete(arg.to_s))
48
- end
49
- @expires_in ||= opts.delete('expires')
50
- @expires_in &&= @expires_in.to_i
51
- @expires_at &&= @expires_at.to_i
52
- @expires_at ||= Time.now.to_i + @expires_in if @expires_in
53
- @options = {:mode => opts.delete(:mode) || :header,
54
- :header_format => opts.delete(:header_format) || 'Bearer %s',
55
- :param_name => opts.delete(:param_name) || 'access_token'}
56
- @params = opts
57
- end
58
-
59
- # Indexer to additional params present in token response
60
- #
61
- # @param [String] key entry key to Hash
62
- def [](key)
63
- @params[key]
64
- end
65
-
66
- # Whether or not the token expires
67
- #
68
- # @return [Boolean]
69
- def expires?
70
- !!@expires_at # rubocop:disable DoubleNegation
71
- end
72
-
73
- # Whether or not the token is expired
74
- #
75
- # @return [Boolean]
76
- def expired?
77
- expires? && (expires_at < Time.now.to_i)
78
- end
79
-
80
- # Refreshes the current Access Token
81
- #
82
- # @return [AccessToken] a new AccessToken
83
- # @note options should be carried over to the new AccessToken
84
- def refresh!(params = {})
85
- fail('A refresh_token is not available') unless refresh_token
86
- params.merge!(:client_id => @client.id,
87
- :client_secret => @client.secret,
88
- :grant_type => 'refresh_token',
89
- :refresh_token => refresh_token)
90
- new_token = @client.get_token(params)
91
- new_token.options = options
92
- new_token.refresh_token = refresh_token unless new_token.refresh_token
93
- new_token
94
- end
95
-
96
- # Convert AccessToken to a hash which can be used to rebuild itself with AccessToken.from_hash
97
- #
98
- # @return [Hash] a hash of AccessToken property values
99
- def to_hash
100
- params.merge(:access_token => token, :refresh_token => refresh_token, :expires_at => expires_at)
101
- end
102
-
103
- # Make a request with the Access Token
104
- #
105
- # @param [Symbol] verb the HTTP request method
106
- # @param [String] path the HTTP URL path of the request
107
- # @param [Hash] opts the options to make the request with
108
- # @see Client#request
109
- def request(verb, path, opts = {}, &block)
110
- self.token = opts
111
- @client.request(verb, path, opts, &block)
112
- end
113
-
114
- # Make a GET request with the Access Token
115
- #
116
- # @see AccessToken#request
117
- def get(path, opts = {}, &block)
118
- request(:get, path, opts, &block)
119
- end
120
-
121
- # Make a POST request with the Access Token
122
- #
123
- # @see AccessToken#request
124
- def post(path, opts = {}, &block)
125
- request(:post, path, opts, &block)
126
- end
127
-
128
- # Make a PUT request with the Access Token
129
- #
130
- # @see AccessToken#request
131
- def put(path, opts = {}, &block)
132
- request(:put, path, opts, &block)
133
- end
134
-
135
- # Make a PATCH request with the Access Token
136
- #
137
- # @see AccessToken#request
138
- def patch(path, opts = {}, &block)
139
- request(:patch, path, opts, &block)
140
- end
141
-
142
- # Make a DELETE request with the Access Token
143
- #
144
- # @see AccessToken#request
145
- def delete(path, opts = {}, &block)
146
- request(:delete, path, opts, &block)
147
- end
148
-
149
- # Get the headers hash (includes Authorization token)
150
- def headers
151
- {'Authorization' => options[:header_format] % token}
152
- end
153
-
154
- private
155
-
156
- def token=(opts) # rubocop:disable MethodLength
157
- case options[:mode]
158
- when :header
159
- opts[:headers] ||= {}
160
- opts[:headers].merge!(headers)
161
- when :query
162
- opts[:params] ||= {}
163
- opts[:params][options[:param_name]] = token
164
- when :body
165
- opts[:body] ||= {}
166
- if opts[:body].is_a?(Hash)
167
- opts[:body][options[:param_name]] = token
168
- else
169
- opts[:body] << "&#{options[:param_name]}=#{token}"
170
- end
171
- # @todo support for multi-part (file uploads)
172
- else
173
- fail("invalid :mode option of #{options[:mode]}")
174
- end
175
- end
176
- end
177
- end
data/lib/base.rb DELETED
@@ -1,16 +0,0 @@
1
- module OAuth2
2
- module Strategy
3
- class Base
4
- def initialize(client)
5
- @client = client
6
- end
7
-
8
- # The OAuth client_id and client_secret
9
- #
10
- # @return [Hash]
11
- def client_params
12
- {'client_id' => @client.id, 'client_secret' => @client.secret}
13
- end
14
- end
15
- end
16
- end
data/lib/client.rb DELETED
@@ -1,155 +0,0 @@
1
- require 'faraday'
2
- require 'logger'
3
- require 'access_token'
4
-
5
- module OAuth2
6
- # The OAuth2::Client class
7
- class Client
8
- attr_reader :id, :secret, :site
9
- attr_accessor :options
10
- attr_writer :connection
11
-
12
- # Instantiate a new OAuth 2.0 client using the
13
- # Client ID and Client Secret registered to your
14
- # application.
15
- #
16
- # @param [String] client_id the client_id value
17
- # @param [String] client_secret the client_secret value
18
- # @param [Hash] opts the options to create the client with
19
- # @option opts [String] :site the OAuth2 provider site host
20
- # @option opts [String] :authorize_url ('/oauth/authorize') absolute or relative URL path to the Authorization endpoint
21
- # @option opts [String] :token_url ('/oauth/token') absolute or relative URL path to the Token endpoint
22
- # @option opts [Symbol] :token_method (:post) HTTP method to use to request token (:get or :post)
23
- # @option opts [Hash] :connection_opts ({}) Hash of connection options to pass to initialize Faraday with
24
- # @option opts [FixNum] :max_redirects (5) maximum number of redirects to follow
25
- # @option opts [Boolean] :raise_errors (true) whether or not to raise an OAuth2::Error
26
- # on responses with 400+ status codes
27
- # @yield [builder] The Faraday connection builder
28
- def initialize(client_id, client_secret, options = {}, &block)
29
- opts = options.dup
30
- @id = client_id
31
- @secret = client_secret
32
- @site = opts.delete(:site)
33
- ssl = opts.delete(:ssl)
34
- @options = {:authorize_url => '/oauth/authorize',
35
- :token_url => '/oauth/token',
36
- :token_method => :post,
37
- :connection_opts => {},
38
- :connection_build => block,
39
- :max_redirects => 5,
40
- :raise_errors => true}.merge(opts)
41
- @options[:connection_opts][:ssl] = ssl if ssl
42
- end
43
-
44
- # Set the site host
45
- #
46
- # @param [String] the OAuth2 provider site host
47
- def site=(value)
48
- @connection = nil
49
- @site = value
50
- end
51
-
52
- # The Faraday connection object
53
- def connection
54
- @connection ||= begin
55
- conn = Faraday.new(site, options[:connection_opts])
56
- conn.build do |b|
57
- options[:connection_build].call(b)
58
- end if options[:connection_build]
59
- conn
60
- end
61
- end
62
-
63
- # The authorize endpoint URL of the OAuth2 provider
64
- #
65
- # @param [Hash] params additional query parameters
66
- def authorize_url(params = nil)
67
- connection.build_url(options[:authorize_url], params).to_s
68
- end
69
-
70
- # The token endpoint URL of the OAuth2 provider
71
- #
72
- # @param [Hash] params additional query parameters
73
- def token_url(params = nil)
74
- connection.build_url(options[:token_url], params).to_s
75
- end
76
-
77
- # Makes a request relative to the specified site root.
78
- #
79
- # @param [Symbol] verb one of :get, :post, :put, :delete
80
- # @param [String] url URL path of request
81
- # @param [Hash] opts the options to make the request with
82
- # @option opts [Hash] :params additional query parameters for the URL of the request
83
- # @option opts [Hash, String] :body the body of the request
84
- # @option opts [Hash] :headers http request headers
85
- # @option opts [Boolean] :raise_errors whether or not to raise an OAuth2::Error on 400+ status
86
- # code response for this request. Will default to client option
87
- # @option opts [Symbol] :parse @see Response::initialize
88
- # @yield [req] The Faraday request
89
- def request(verb, url, opts = {}) # rubocop:disable CyclomaticComplexity, MethodLength
90
- connection.response :logger, ::Logger.new($stdout) if ENV['OAUTH_DEBUG'] == 'true'
91
-
92
- url = connection.build_url(url, opts[:params]).to_s
93
-
94
- response = connection.run_request(verb, url, opts[:body], opts[:headers]) do |req|
95
- yield(req) if block_given?
96
- end
97
- response = Response.new(response, :parse => opts[:parse])
98
-
99
- case response.status
100
- when 301, 302, 303, 307
101
- opts[:redirect_count] ||= 0
102
- opts[:redirect_count] += 1
103
- return response if opts[:redirect_count] > options[:max_redirects]
104
- if response.status == 303
105
- verb = :get
106
- opts.delete(:body)
107
- end
108
- request(verb, response.headers['location'], opts)
109
- when 200..299, 300..399
110
- # on non-redirecting 3xx statuses, just return the response
111
- response
112
- when 400..599
113
- error = Error.new(response)
114
- fail(error) if opts.fetch(:raise_errors, options[:raise_errors])
115
- response.error = error
116
- response
117
- else
118
- error = Error.new(response)
119
- fail(error, "Unhandled status code value of #{response.status}")
120
- end
121
- end
122
-
123
- # Initializes an AccessToken by making a request to the token endpoint
124
- #
125
- # @param [Hash] params a Hash of params for the token endpoint
126
- # @param [Hash] access token options, to pass to the AccessToken object
127
- # @param [Class] class of access token for easier subclassing OAuth2::AccessToken
128
- # @return [AccessToken] the initalized AccessToken
129
- def get_token(params, access_token_opts = {})
130
- opts = {:raise_errors => options[:raise_errors], :parse => params.delete(:parse)}
131
- if options[:token_method] == :post
132
- headers = params.delete(:headers)
133
- opts[:body] = params
134
- opts[:headers] = {'Content-Type' => 'application/x-www-form-urlencoded'}
135
- opts[:headers].merge!(headers) if headers
136
- else
137
- opts[:params] = params
138
- end
139
- response = request(options[:token_method], token_url, opts)
140
- error = Error.new(response)
141
- fail(error) if options[:raise_errors] && !(response.parsed.is_a?(Hash) && response.parsed['access_token'])
142
- hash = response.parsed.merge(access_token_opts)
143
- AccessToken.new(self, hash.delete('access_token') || hash.delete(:access_token), hash)
144
- #AccessToken.from_hash(self, response.parsed.merge(access_token_opts))
145
- #access_token_class.from_hash(self, response.parsed.merge(access_token_opts))
146
- end
147
-
148
- # The Client Credentials strategy
149
- #
150
- # @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.4
151
- def client_credentials
152
- @client_credentials ||= OAuth2::Strategy::ClientCredentials.new(self)
153
- end
154
- end
155
- end
@@ -1,37 +0,0 @@
1
- require 'base64'
2
-
3
- module OAuth2
4
- module Strategy
5
- # The Client Credentials Strategy
6
- #
7
- # @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.4
8
- class ClientCredentials < Base
9
- # Not used for this strategy
10
- #
11
- # @raise [NotImplementedError]
12
- def authorize_url
13
- fail(NotImplementedError, 'The authorization endpoint is not used in this strategy')
14
- end
15
-
16
- # Retrieve an access token given the specified client.
17
- #
18
- # @param [Hash] params additional params
19
- # @param [Hash] opts options
20
- def get_token(params = {}, opts = {})
21
- request_body = opts.delete('auth_scheme') == 'request_body'
22
- params.merge!('grant_type' => 'client_credentials')
23
- params.merge!(request_body ? client_params : {:headers => {'Authorization' => authorization(client_params['client_id'], client_params['client_secret'])}})
24
- @client.get_token(params, opts.merge('refresh_token' => nil))
25
- end
26
-
27
- # Returns the Authorization header value for Basic Authentication
28
- #
29
- # @param [String] The client ID
30
- # @param [String] the client secret
31
- def authorization(client_id, client_secret)
32
- 'Basic ' + Base64.encode64(client_id + ':' + client_secret).gsub("\n", '')
33
- end
34
- end
35
- end
36
- end
37
-
data/lib/error.rb DELETED
@@ -1,24 +0,0 @@
1
- module OAuth2
2
- class Error < StandardError
3
- attr_reader :response, :code, :description
4
-
5
- # standard error values include:
6
- # :invalid_request, :invalid_client, :invalid_token, :invalid_grant, :unsupported_grant_type, :invalid_scope
7
- def initialize(response)
8
- response.error = self
9
- @response = response
10
-
11
- message = []
12
-
13
- if response.parsed.is_a?(Hash)
14
- @code = response.parsed['error']
15
- @description = response.parsed['error_description']
16
- message << "#{@code}: #{@description}"
17
- end
18
-
19
- message << response.body
20
-
21
- super(message.join("\n"))
22
- end
23
- end
24
- end
data/lib/response.rb DELETED
@@ -1,81 +0,0 @@
1
- module OAuth2
2
- # OAuth2::Response class
3
- class Response
4
- attr_reader :response
5
- attr_accessor :error, :options
6
-
7
- # Adds a new content type parser.
8
- #
9
- # @param [Symbol] key A descriptive symbol key such as :json or :query.
10
- # @param [Array] One or more mime types to which this parser applies.
11
- # @yield [String] A block returning parsed content.
12
- def self.register_parser(key, mime_types, &block)
13
- key = key.to_sym
14
- PARSERS[key] = block
15
- Array(mime_types).each do |mime_type|
16
- CONTENT_TYPES[mime_type] = key
17
- end
18
- end
19
-
20
- # Initializes a Response instance
21
- #
22
- # @param [Faraday::Response] response The Faraday response instance
23
- # @param [Hash] opts options in which to initialize the instance
24
- # @option opts [Symbol] :parse (:automatic) how to parse the response body. one of :query (for x-www-form-urlencoded),
25
- # :json, or :automatic (determined by Content-Type response header)
26
- def initialize(response, opts = {})
27
- @response = response
28
- @options = {:parse => :automatic}.merge(opts)
29
- end
30
-
31
- # The HTTP response headers
32
- def headers
33
- response.headers
34
- end
35
-
36
- # The HTTP response status code
37
- def status
38
- response.status
39
- end
40
-
41
- # The HTTP response body
42
- def body
43
- response.body || ''
44
- end
45
-
46
- # Procs that, when called, will parse a response body according
47
- # to the specified format.
48
- PARSERS = {
49
- :json => lambda { |body| JSON.parse(body) rescue body }, # rubocop:disable RescueModifier
50
- #:query => lambda { |body| Rack::Utils.parse_query(body) },
51
- :text => lambda { |body| body }
52
- }
53
-
54
- # Content type assignments for various potential HTTP content types.
55
- CONTENT_TYPES = {
56
- 'application/json' => :json,
57
- 'text/javascript' => :json,
58
- 'application/x-www-form-urlencoded' => :query,
59
- 'text/plain' => :text
60
- }
61
-
62
- # The parsed response body.
63
- # Will attempt to parse application/x-www-form-urlencoded and
64
- # application/json Content-Type response bodies
65
- def parsed
66
- return nil unless PARSERS.key?(parser)
67
- @parsed ||= PARSERS[parser].call(body)
68
- end
69
-
70
- # Attempts to determine the content type of the response.
71
- def content_type
72
- ((response.headers.values_at('content-type', 'Content-Type').compact.first || '').split(';').first || '').strip
73
- end
74
-
75
- # Determines the parser that will be used to supply the content of #parsed
76
- def parser
77
- return options[:parse].to_sym if PARSERS.key?(options[:parse])
78
- CONTENT_TYPES[content_type]
79
- end
80
- end
81
- end
data/lib/strategy.rb DELETED
@@ -1,73 +0,0 @@
1
- require 'jwt'
2
-
3
- module OAuth2
4
- module Strategy
5
- # The Client Assertion Strategy
6
- #
7
- # @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.1.3
8
- #
9
- # Sample usage:
10
- # client = OAuth2::Client.new(client_id, client_secret,
11
- # :site => 'http://localhost:8080')
12
- #
13
- # params = {:hmac_secret => "some secret",
14
- # # or :private_key => "private key string",
15
- # :iss => "http://localhost:3001",
16
- # :prn => "me@here.com",
17
- # :exp => Time.now.utc.to_i + 3600}
18
- #
19
- # access = client.assertion.get_token(params)
20
- # access.token # actual access_token string
21
- # access.get("/api/stuff") # making api calls with access token in header
22
- #
23
- class Assertion < Base
24
- # Not used for this strategy
25
- #
26
- # @raise [NotImplementedError]
27
- def authorize_url
28
- fail(NotImplementedError, 'The authorization endpoint is not used in this strategy')
29
- end
30
-
31
- # Retrieve an access token given the specified client.
32
- #
33
- # @param [Hash] params assertion params
34
- # pass either :hmac_secret or :private_key, but not both.
35
- #
36
- # params :hmac_secret, secret string.
37
- # params :private_key, private key string.
38
- #
39
- # params :iss, issuer
40
- # params :aud, audience, optional
41
- # params :prn, principal, current user
42
- # params :exp, expired at, in seconds, like Time.now.utc.to_i + 3600
43
- #
44
- # @param [Hash] opts options
45
- def get_token(params = {}, opts = {})
46
- hash = build_request(params)
47
- @client.get_token(hash, opts.merge('refresh_token' => nil))
48
- end
49
-
50
- def build_request(params)
51
- assertion = build_assertion(params)
52
- {:grant_type => 'assertion',
53
- :assertion_type => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
54
- :assertion => assertion,
55
- :scope => params[:scope]
56
- }.merge(client_params)
57
- end
58
-
59
- def build_assertion(params)
60
- claims = {:iss => params[:iss],
61
- :aud => params[:aud],
62
- :prn => params[:prn],
63
- :exp => params[:exp]
64
- }
65
- if params[:hmac_secret]
66
- JWT.encode(claims, params[:hmac_secret], 'HS256')
67
- elsif params[:private_key]
68
- JWT.encode(claims, params[:private_key], 'RS256')
69
- end
70
- end
71
- end
72
- end
73
- end