pusher-platform-tmp 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
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,4 @@
1
+ module PusherPlatform
2
+ class Error < ::StandardError
3
+ end
4
+ 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: []