playlyfe 0.2.2 → 0.4.5

Sign up to get free protection for your applications and to get access to all the features.
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