pusher-platform-tmp 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/pusher-platform.rb +1 -0
- data/lib/pusher-platform/authenticator.rb +139 -0
- data/lib/pusher-platform/base_client.rb +66 -0
- data/lib/pusher-platform/common.rb +4 -0
- data/lib/pusher-platform/error_response.rb +15 -0
- data/lib/pusher-platform/instance.rb +63 -0
- metadata +97 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9d3bddf26ceca3fe9dfbe5a75173d0fc98e4ede0
|
4
|
+
data.tar.gz: a7f8bfc36444f85e4656e628ed41adbf45b59860
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 66e44e532c9ad2c6eceb4125f935f5b32b7654b985310a66834d82f4687d3353e68c3eb7a18042581768c787c5e6e2c19a08f8c5d57865018a421b1b3c401f5e
|
7
|
+
data.tar.gz: d3e4403be970c9c721c07eeebe0c117a641a61d6396fbab703ef89b738028d45e0596aabdb6be5f984455a53c8220ab6ec8251ce801ddcba8b5ff5b90cce6395
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'pusher-platform/instance'
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'jwt'
|
2
|
+
require 'rack'
|
3
|
+
|
4
|
+
module PusherPlatform
|
5
|
+
TOKEN_EXPIRY = 24*60*60
|
6
|
+
|
7
|
+
class Authenticator
|
8
|
+
def initialize(instance_id, key_id, key_secret)
|
9
|
+
@instance_id = instance_id
|
10
|
+
@key_id = key_id
|
11
|
+
@key_secret = key_secret
|
12
|
+
end
|
13
|
+
|
14
|
+
# Takes a Rack request to the authorization endpoint and and handles it
|
15
|
+
# either returning a new access/refresh token pair, or an error.
|
16
|
+
#
|
17
|
+
# @param request [Rack::Request] the request to authenticate
|
18
|
+
# @return the response object
|
19
|
+
def authenticate(request, options)
|
20
|
+
form_data = Rack::Utils.parse_nested_query request.body.read
|
21
|
+
grant_type = form_data['grant_type']
|
22
|
+
|
23
|
+
if grant_type == "client_credentials"
|
24
|
+
return authenticate_with_client_credentials(options)
|
25
|
+
elsif grant_type == "refresh_token"
|
26
|
+
old_refresh_jwt = form_data['refresh_token']
|
27
|
+
return authenticate_with_refresh_token(old_refresh_jwt, options)
|
28
|
+
else
|
29
|
+
return response(401, {
|
30
|
+
error: "unsupported_grant_type"
|
31
|
+
})
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def generate_access_token(options)
|
36
|
+
now = Time.now.utc.to_i
|
37
|
+
|
38
|
+
claims = {
|
39
|
+
instance: @instance_id,
|
40
|
+
iss: "api_keys/#{@key_id}",
|
41
|
+
iat: now,
|
42
|
+
exp: now + TOKEN_EXPIRY
|
43
|
+
}
|
44
|
+
|
45
|
+
claims.merge!({ sub: options[:user_id] }) unless options[:user_id].nil?
|
46
|
+
claims.merge!({ su: true }) if options[:su]
|
47
|
+
|
48
|
+
{
|
49
|
+
token: JWT.encode(claims, @key_secret, 'HS256'),
|
50
|
+
expires_in: TOKEN_EXPIRY
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def authenticate_with_client_credentials(options)
|
57
|
+
return respond_with_new_token_pair(options)
|
58
|
+
end
|
59
|
+
|
60
|
+
def authenticate_with_refresh_token(old_refresh_jwt, options)
|
61
|
+
old_refresh_token = begin
|
62
|
+
JWT.decode(old_refresh_jwt, @key_secret, true, {
|
63
|
+
iss: "api_keys/#{@key_id}",
|
64
|
+
verify_iss: true,
|
65
|
+
}).first
|
66
|
+
rescue => e
|
67
|
+
error_description = if e.is_a?(JWT::InvalidIssuerError)
|
68
|
+
"refresh token issuer is invalid"
|
69
|
+
elsif e.is_a?(JWT::ImmatureSignature)
|
70
|
+
"refresh token is not valid yet"
|
71
|
+
elsif e.is_a?(JWT::ExpiredSignature)
|
72
|
+
"refresh tokan has expired"
|
73
|
+
else
|
74
|
+
"refresh token is invalid"
|
75
|
+
end
|
76
|
+
|
77
|
+
return response(401, {
|
78
|
+
error: "invalid_grant",
|
79
|
+
error_description: error_description,
|
80
|
+
# TODO error_uri
|
81
|
+
})
|
82
|
+
end
|
83
|
+
|
84
|
+
if old_refresh_token["refresh"] != true
|
85
|
+
return response(401, {
|
86
|
+
error: "invalid_grant",
|
87
|
+
error_description: "refresh token does not have a refresh claim",
|
88
|
+
# TODO error_uri
|
89
|
+
})
|
90
|
+
end
|
91
|
+
|
92
|
+
if options[:user_id] != old_refresh_token["sub"]
|
93
|
+
return response(401, {
|
94
|
+
error: "invalid_grant",
|
95
|
+
error_description: "refresh token has an invalid user id",
|
96
|
+
# TODO error_uri
|
97
|
+
})
|
98
|
+
end
|
99
|
+
|
100
|
+
return respond_with_new_token_pair(options)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Creates a payload dictionary made out of access and refresh token pair and TTL for the access token.
|
104
|
+
#
|
105
|
+
# @param user_id [String] optional id of the user, ignore for anonymous users
|
106
|
+
# @return [Hash] Payload as a hash
|
107
|
+
def respond_with_new_token_pair(options)
|
108
|
+
access_token = generate_access_token(options)[:token]
|
109
|
+
refresh_token = generate_refresh_token(options)[:token]
|
110
|
+
return response(200, {
|
111
|
+
access_token: access_token,
|
112
|
+
token_type: "bearer",
|
113
|
+
expires_in: TOKEN_EXPIRY,
|
114
|
+
refresh_token: refresh_token,
|
115
|
+
})
|
116
|
+
end
|
117
|
+
|
118
|
+
def generate_refresh_token(options)
|
119
|
+
now = Time.now.utc.to_i
|
120
|
+
|
121
|
+
claims = {
|
122
|
+
instance: @instance_id,
|
123
|
+
iss: "api_keys/#{@key_id}",
|
124
|
+
iat: now,
|
125
|
+
refresh: true,
|
126
|
+
sub: options[:user_id],
|
127
|
+
}
|
128
|
+
|
129
|
+
{ token: JWT.encode(claims, @key_secret, 'HS256') }
|
130
|
+
end
|
131
|
+
|
132
|
+
def response(status, body)
|
133
|
+
return {
|
134
|
+
status: status,
|
135
|
+
json: body,
|
136
|
+
}
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'excon'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module PusherPlatform
|
5
|
+
class BaseClient
|
6
|
+
def initialize(options)
|
7
|
+
raise "Unspecified host" if options[:host].nil?
|
8
|
+
port_string = options[:port] || ''
|
9
|
+
host_string = "https://#{options[:host]}#{port_string}"
|
10
|
+
@connection = Excon.new(host_string)
|
11
|
+
|
12
|
+
@instance_id = options[:instance_id]
|
13
|
+
@service_name = options[:service_name]
|
14
|
+
@service_version = options[:service_version]
|
15
|
+
end
|
16
|
+
|
17
|
+
def request(options)
|
18
|
+
raise "Unspecified request method" if options[:method].nil?
|
19
|
+
raise "Unspecified request path" if options[:path].nil?
|
20
|
+
|
21
|
+
headers = if options[:headers]
|
22
|
+
options[:headers].dup
|
23
|
+
else
|
24
|
+
{}
|
25
|
+
end
|
26
|
+
|
27
|
+
if options[:jwt]
|
28
|
+
headers["Authorization"] = "Bearer #{options[:jwt]}"
|
29
|
+
end
|
30
|
+
|
31
|
+
path = "services/#{@service_name}/#{@service_version}/#{@instance_id}/#{options[:path]}"
|
32
|
+
body = unless options[:body].nil?
|
33
|
+
options[:body].to_json
|
34
|
+
end
|
35
|
+
|
36
|
+
response = @connection.request(
|
37
|
+
method: options[:method],
|
38
|
+
path: sanitise_path(path),
|
39
|
+
headers: headers,
|
40
|
+
body: body,
|
41
|
+
query: options[:query]
|
42
|
+
)
|
43
|
+
|
44
|
+
if response.status >= 200 && response.status <= 299
|
45
|
+
return response
|
46
|
+
elsif response.status >= 300 && response.status <= 399
|
47
|
+
raise "unsupported redirect response: #{response.status}"
|
48
|
+
elsif response.status >= 400 && response.status <= 599
|
49
|
+
error_description = begin
|
50
|
+
JSON.parse(response.body)
|
51
|
+
rescue
|
52
|
+
response.body
|
53
|
+
end
|
54
|
+
raise ErrorResponse.new(response.status, response.headers, error_description)
|
55
|
+
else
|
56
|
+
raise "unsupported response code: #{response.status}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def sanitise_path(path)
|
63
|
+
path.gsub(/\/+/, "/").gsub(/\/+$/, "")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module PusherPlatform
|
2
|
+
class ErrorResponse < Error
|
3
|
+
attr_accessor :status, :headers, :description
|
4
|
+
|
5
|
+
def initialize(status, headers, description)
|
6
|
+
@status = status
|
7
|
+
@headers = headers
|
8
|
+
@description = description
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
"Pusher::ErrorResponse: #{status} #{description}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require_relative './authenticator'
|
2
|
+
require_relative './base_client'
|
3
|
+
require_relative './common'
|
4
|
+
require_relative './error_response'
|
5
|
+
|
6
|
+
module PusherPlatform
|
7
|
+
|
8
|
+
HOST_BASE = 'pusherplatform.io'
|
9
|
+
|
10
|
+
class Instance
|
11
|
+
def initialize(options)
|
12
|
+
raise "No instance locator provided" if options[:locator].nil?
|
13
|
+
raise "No service name provided" if options[:service_name].nil?
|
14
|
+
raise "No service version provided" if options[:service_version].nil?
|
15
|
+
locator = options[:locator]
|
16
|
+
@service_name = options[:service_name]
|
17
|
+
@service_version = options[:service_version]
|
18
|
+
|
19
|
+
key_parts = options[:key].match(/^([^:]+):(.+)$/)
|
20
|
+
raise "Invalid key" if key_parts.nil?
|
21
|
+
|
22
|
+
@key_id = key_parts[1]
|
23
|
+
@key_secret = key_parts[2]
|
24
|
+
|
25
|
+
split_locator = locator.split(':')
|
26
|
+
|
27
|
+
@platform_version = split_locator[0]
|
28
|
+
@cluster = split_locator[1]
|
29
|
+
@instance_id = split_locator[2]
|
30
|
+
|
31
|
+
@client = if options[:client]
|
32
|
+
options[:client]
|
33
|
+
else
|
34
|
+
BaseClient.new(
|
35
|
+
host: options[:host] || "#{@cluster}.#{HOST_BASE}",
|
36
|
+
port: options[:port],
|
37
|
+
instance_id: @instance_id,
|
38
|
+
service_name: @service_name,
|
39
|
+
service_version: @service_version
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
@authenticator = Authenticator.new(@instance_id, @key_id, @key_secret)
|
44
|
+
end
|
45
|
+
|
46
|
+
def request(options)
|
47
|
+
if options[:jwt].nil?
|
48
|
+
options = options.merge(
|
49
|
+
{ jwt: @authenticator.generate_access_token({ su: true })[:token] }
|
50
|
+
)
|
51
|
+
end
|
52
|
+
@client.request(options)
|
53
|
+
end
|
54
|
+
|
55
|
+
def authenticate(request, options)
|
56
|
+
@authenticator.authenticate(request, options)
|
57
|
+
end
|
58
|
+
|
59
|
+
def generate_access_token(options)
|
60
|
+
@authenticator.generate_access_token(options)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
metadata
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pusher-platform-tmp
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.6.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Pusher
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-03-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: excon
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.54.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.54.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: jwt
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.5'
|
34
|
+
- - ">="
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: 1.5.6
|
37
|
+
type: :runtime
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - "~>"
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '1.5'
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 1.5.6
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rack
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '1.0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '1.0'
|
61
|
+
description:
|
62
|
+
email: dev@playbycourt.com
|
63
|
+
executables: []
|
64
|
+
extensions: []
|
65
|
+
extra_rdoc_files: []
|
66
|
+
files:
|
67
|
+
- lib/pusher-platform.rb
|
68
|
+
- lib/pusher-platform/authenticator.rb
|
69
|
+
- lib/pusher-platform/base_client.rb
|
70
|
+
- lib/pusher-platform/common.rb
|
71
|
+
- lib/pusher-platform/error_response.rb
|
72
|
+
- lib/pusher-platform/instance.rb
|
73
|
+
homepage:
|
74
|
+
licenses:
|
75
|
+
- MIT
|
76
|
+
metadata: {}
|
77
|
+
post_install_message:
|
78
|
+
rdoc_options: []
|
79
|
+
require_paths:
|
80
|
+
- lib
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
requirements: []
|
92
|
+
rubyforge_project:
|
93
|
+
rubygems_version: 2.5.1
|
94
|
+
signing_key:
|
95
|
+
specification_version: 4
|
96
|
+
summary: Pusher Platform Ruby SDK
|
97
|
+
test_files: []
|