messagebird-rest 3.0.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9dc78fcfe4e496c197244a6e62ad347bc439c52334d69a453f25977f7981c23b
4
- data.tar.gz: 6cef4d63e95ddd4455dd4a003b60bdfc3dfb1b10cfc1049cee9aecc7d8d5fb41
3
+ metadata.gz: 7f7b235894410103a5ae0cddb620fb84e1e713c022a8f974ab54d114d69fe1d0
4
+ data.tar.gz: 95411be3e97854eb9696c0b2f24092fdbd0f41deab6949be4656566312520e01
5
5
  SHA512:
6
- metadata.gz: f235d92c2c49b14c7529c3aa2ad5e8fb6e03adf66a74f4d55229b1b262c66d8beca1a9137ff1d24da439a8d9051915765c7371e17fc1eae63bc0e56e8b96f715
7
- data.tar.gz: 4b264d192ab011f784aedd9f336e750a1d20b9413d6cb9fd2e67d37cef6b36f7ceed25b80d6dbb74e90b4ffb452f90939b3343a732d2043138071a2b15f6260e
6
+ metadata.gz: 13e721d025a6e85b5f503770fe04a09a60048358ca9c76556a42903a7d59134f9290b2b736a6504bbfab559997bd29d53c8fd76b74b861544dd80d7de3d502d3
7
+ data.tar.gz: 26762318e8db116c1ddff86d49b64434328b0569815ab84f247c255978d7e60773e0d87f6ab589441bbcd1ab4460815734b1c42944b4425dbc998a7d11da6f23
data/README.md CHANGED
@@ -2,7 +2,7 @@ MessageBird's REST API for Ruby
2
2
  ===============================
3
3
  This repository contains the open source Ruby client for MessageBird's REST API. Documentation can be found at: https://developers.messagebird.com/
4
4
 
5
- [![Build Status](https://travis-ci.org/messagebird/ruby-rest-api.svg?branch=master)](https://travis-ci.org/messagebird/ruby-rest-api)
5
+ [![Build Status](https://github.com/messagebird/ruby-rest-api/actions/workflows/ruby_ci.yml/badge.svg)](https://github.com/messagebird/ruby-rest-api/actions)
6
6
 
7
7
  Requirements
8
8
  ------------
@@ -7,6 +7,8 @@ module MessageBird
7
7
  class Base
8
8
  # takes each element from the given hash and apply it to ourselves through an assignment method
9
9
  def map_hash_elements_to_self(hash)
10
+ return if hash.nil?
11
+
10
12
  hash.each do |key, value|
11
13
  method_name = key.gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase # convert came case to snake case
12
14
  method_name += '='
@@ -26,7 +26,8 @@ module MessageBird
26
26
  end
27
27
 
28
28
  class CallFlowList < List
29
- attr_accessor :perPage, :currentPage, :pageCount, :totalCount
29
+ attr_accessor :per_page, :current_page, :page_count, :total_count
30
+
30
31
  PER_PAGE = 20
31
32
  CURRENT_PAGE = 1
32
33
 
@@ -53,8 +54,8 @@ module MessageBird
53
54
 
54
55
  class CallFlowStepOption < MessageBird::Base
55
56
  attr_accessor :destination, :payload, :language, :voice, :repeat,
56
- :media, :length, :maxLength, :timeout, :finishOnKey, :transcribe,
57
- :transcribeLanguage, :record, :url, :ifMachine, :machineTimeout,
58
- :onFinish, :mask
57
+ :media, :length, :max_length, :timeout, :finish_on_key, :transcribe,
58
+ :transcribe_language, :record, :url, :if_machine, :machine_timeout,
59
+ :on_finish, :mask
59
60
  end
60
61
  end
@@ -30,14 +30,16 @@ require 'messagebird/voice/call'
30
30
  require 'messagebird/voice/call_leg'
31
31
  require 'messagebird/voice/call_leg_recording'
32
32
  require 'messagebird/voice/transcription'
33
- require 'messagebird/voice/list'
34
33
 
35
34
  module MessageBird
36
35
  class ErrorException < StandardError
37
36
  attr_reader :errors
38
37
 
39
38
  def initialize(errors)
39
+ super()
40
40
  @errors = errors
41
+ message = errors.map(&:message).join(', ')
42
+ super(message)
41
43
  end
42
44
  end
43
45
 
@@ -47,7 +49,8 @@ module MessageBird
47
49
  class Client
48
50
  attr_reader :access_key, :http_client, :conversation_client, :voice_client
49
51
 
50
- def initialize(access_key = nil, http_client = nil, conversation_client = nil, voice_client = nil)
52
+ def initialize(access_key = nil, http_client = nil, conversation_client = nil, voice_client = nil) # rubocop:disable Metrics/ParameterLists
53
+ super()
51
54
  @access_key = access_key || ENV['MESSAGEBIRD_ACCESS_KEY']
52
55
  @http_client = http_client || HttpClient.new(@access_key)
53
56
  @conversation_client = conversation_client || ConversationClient.new(@access_key)
@@ -490,7 +493,7 @@ module MessageBird
490
493
  # JSON by default. See also:
491
494
  # https://developers.messagebird.com/docs/alternatives.
492
495
 
493
- '_method=PUT&' + contact_ids.map { |id| "ids[]=#{id}" }.join('&')
496
+ contact_ids.map { |id| "ids[]=#{id}" }.join('&').prepend('_method=PUT&')
494
497
  end
495
498
 
496
499
  def add_querystring(path, params)
@@ -14,9 +14,9 @@ module MessageBird
14
14
  request.body = params.to_json
15
15
  request
16
16
  end
17
- end
18
17
 
19
- def endpoint
20
- ENDPOINT
18
+ def endpoint
19
+ ENDPOINT
20
+ end
21
21
  end
22
22
  end
@@ -5,5 +5,13 @@ require 'messagebird/base'
5
5
  module MessageBird
6
6
  class Error < MessageBird::Base
7
7
  attr_accessor :code, :description, :parameter
8
+
9
+ def message
10
+ if parameter
11
+ "#{description} (error code: #{code}, parameter: #{parameter})"
12
+ else
13
+ "#{description} (error code: #{code})"
14
+ end
15
+ end
8
16
  end
9
17
  end
@@ -33,7 +33,7 @@ module MessageBird
33
33
  http
34
34
  end
35
35
 
36
- def request(method, path, params = {}, check_json = true)
36
+ def request(method, path, params = {}, check_json: true)
37
37
  uri = URI.join(endpoint, path)
38
38
  http = build_http_client(uri)
39
39
  request = build_request(method, uri, params)
@@ -2,23 +2,25 @@
2
2
 
3
3
  require 'messagebird/base'
4
4
 
5
- class List < MessageBird::Base
6
- attr_accessor :offset, :limit, :count, :total_count, :links
7
- attr_reader :items
5
+ module MessageBird
6
+ class List < MessageBird::Base
7
+ attr_accessor :offset, :limit, :count, :total_count, :links
8
+ attr_reader :items
8
9
 
9
- # type will be used to create objects for the items, e.g.
10
- # List.new(Contact, {}).
11
- def initialize(type, json)
12
- @type = type
10
+ # type will be used to create objects for the items, e.g.
11
+ # List.new(Contact, {}).
12
+ def initialize(type, json)
13
+ @type = type
13
14
 
14
- super(json)
15
- end
15
+ super(json)
16
+ end
16
17
 
17
- def items=(value)
18
- @items = value.map { |i| @type.new i }
19
- end
18
+ def items=(value)
19
+ @items = value.map { |i| @type.new i }
20
+ end
20
21
 
21
- def [](index)
22
- @items[index]
22
+ def [](index)
23
+ @items[index]
24
+ end
23
25
  end
24
26
  end
@@ -6,7 +6,7 @@ require 'messagebird/recipient'
6
6
  module MessageBird
7
7
  class Message < MessageBird::Base
8
8
  attr_accessor :id, :href, :direction, :type, :originator, :body, :reference,
9
- :validity, :gateway, :typeDetails, :datacoding, :mclass
9
+ :validity, :gateway, :type_details, :datacoding, :mclass
10
10
  attr_reader :scheduled_date_time, :created_datetime, :recipients
11
11
 
12
12
  def scheduled_date_time=(value)
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'base64'
4
+ require 'digest'
5
+ require 'time'
6
+ require 'jwt'
7
+
8
+ module MessageBird
9
+ class ValidationError < StandardError
10
+ end
11
+
12
+ ##
13
+ # RequestValidator validates request signature signed by MessageBird services.
14
+ #
15
+ # @see https://developers.messagebird.com/docs/verify-http-requests
16
+ class RequestValidator
17
+ ALLOWED_ALGOS = %w[HS256 HS384 HS512].freeze
18
+
19
+ ##
20
+ #
21
+ # @param [string] signature_key customer signature key. Can be retrieved through <a href="https://dashboard.messagebird.com/developers/settings">Developer Settings</a>. This is NOT your API key.
22
+ # @param [bool] skip_url_validation whether url_hash claim validation should be skipped. Note that when true, no query parameters should be trusted.
23
+ def initialize(signature_key, skip_url_validation: false)
24
+ @signature_key = signature_key
25
+ @skip_url_validation = skip_url_validation
26
+ end
27
+
28
+ ##
29
+ # This method validates provided request signature, which is a JWT token.
30
+ # This JWT is signed with a MessageBird account unique secret key, ensuring the request is from MessageBird and a specific account.
31
+ # The JWT contains the following claims:
32
+ # * "url_hash" - the raw URL hashed with SHA256 ensuring the URL wasn't altered.
33
+ # * "payload_hash" - the raw payload hashed with SHA256 ensuring the payload wasn't altered.
34
+ # * "jti" - a unique token ID to implement an optional non-replay check (NOT validated by default).
35
+ # * "nbf" - the not before timestamp.
36
+ # * "exp" - the expiration timestamp is ensuring that a request isn't captured and used at a later time.
37
+ # * "iss" - the issuer name, always MessageBird.
38
+ # @param [String] signature the actual signature taken from request header "MessageBird-Signature-JWT".
39
+ # @param [String] url the raw url including the protocol, hostname and query string, e.g. "https://example.com/?example=42".
40
+ # @param [Array] request_body the raw request body.
41
+ # @return [Array] raw signature payload
42
+ # @raise [ValidationError] if signature is invalid
43
+ # @see https://developers.messagebird.com/docs/verify-http-requests
44
+ def validate_signature(signature, url, request_body)
45
+ raise ValidationError, 'Signature can not be empty' if signature.to_s.empty?
46
+ raise ValidationError, 'URL can not be empty' if !@skip_url_validation && url.to_s.empty?
47
+
48
+ claims = decode_signature signature
49
+ validate_url(url, claims['url_hash']) unless @skip_url_validation
50
+ validate_payload(request_body, claims['payload_hash'])
51
+
52
+ claims
53
+ end
54
+
55
+ private # Applies to every method below this line
56
+
57
+ def decode_signature(signature)
58
+ begin
59
+ claims, * = JWT.decode signature, @signature_key, true,
60
+ algorithm: ALLOWED_ALGOS,
61
+ iss: 'MessageBird',
62
+ required_claims: %w[iss nbf exp],
63
+ verify_iss: true,
64
+ leeway: 1
65
+ rescue JWT::DecodeError => e
66
+ raise ValidationError, e
67
+ end
68
+
69
+ claims
70
+ end
71
+
72
+ def validate_url(url, url_hash)
73
+ expected_url_hash = Digest::SHA256.hexdigest url
74
+ unless JWT::SecurityUtils.secure_compare(expected_url_hash, url_hash)
75
+ raise ValidationError, 'invalid jwt: claim url_hash is invalid'
76
+ end
77
+ end
78
+
79
+ def validate_payload(body, payload_hash)
80
+ if !body.to_s.empty? && !payload_hash.to_s.empty?
81
+ unless JWT::SecurityUtils.secure_compare(Digest::SHA256.hexdigest(body), payload_hash)
82
+ raise ValidationError, 'invalid jwt: claim payload_hash is invalid'
83
+ end
84
+ elsif !body.to_s.empty?
85
+ raise ValidationError, 'invalid jwt: claim payload_hash is not set but payload is present'
86
+ elsif !payload_hash.to_s.empty?
87
+ raise ValidationError, 'invalid jwt: claim payload_hash is set but actual payload is missing'
88
+ end
89
+ end
90
+ end
91
+ end
@@ -5,10 +5,16 @@ require 'digest'
5
5
  require 'time'
6
6
 
7
7
  module MessageBird
8
+ ##
9
+ # @deprecated Use {MessageBird::RequestValidator::ValidationError} instead.
8
10
  class ValidationException < TypeError
9
11
  end
10
12
 
13
+ ##
14
+ # @deprecated Use {MessageBird::RequestValidator} instead.
11
15
  class SignedRequest
16
+ ##
17
+ # @deprecated Use {MessageBird::RequestValidator} instead.
12
18
  def initialize(query_parameters, signature, request_timestamp, body)
13
19
  unless query_parameters.is_a? Hash
14
20
  raise ValidationException, 'The "query_parameters" value is invalid.'
@@ -29,12 +35,16 @@ module MessageBird
29
35
  @body = body
30
36
  end
31
37
 
38
+ ##
39
+ # @deprecated Use {MessageBird::RequestValidator::validateSignature} instead.
32
40
  def verify(signing_key)
33
41
  calculated_signature = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), signing_key, build_payload)
34
42
  expected_signature = Base64.decode64(@signature)
35
43
  calculated_signature.bytes == expected_signature.bytes
36
44
  end
37
45
 
46
+ ##
47
+ # @deprecated Use {MessageBird::RequestValidator} instead.
38
48
  def build_payload
39
49
  parts = []
40
50
  parts.push(@request_timestamp)
@@ -43,6 +53,8 @@ module MessageBird
43
53
  parts.join("\n")
44
54
  end
45
55
 
56
+ ##
57
+ # @deprecated Use {MessageBird::RequestValidator} instead.
46
58
  def recent?(offset = 10)
47
59
  (Time.now.getutc.to_i - @request_timestamp) < offset
48
60
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module MessageBird
4
4
  module Version
5
- STRING = '3.0.0'
5
+ STRING = '4.0.0'
6
6
  end
7
7
  end
@@ -11,6 +11,7 @@ module MessageBird
11
11
 
12
12
  # further processed attributes for convenience
13
13
  attr_accessor :_links, :uri
14
+
14
15
  # Grab the URI to the downloadable file and provide it as a direct attribute
15
16
  def handle_links(links_object)
16
17
  @uri = links_object['file']
@@ -6,6 +6,7 @@ module MessageBird
6
6
  module Voice
7
7
  class List < List
8
8
  attr_accessor :per_page, :current_page, :page_count, :total_count
9
+
9
10
  PER_PAGE = 20
10
11
  CURRENT_PAGE = 1
11
12
  def data=(value)
data/lib/messagebird.rb CHANGED
@@ -12,7 +12,8 @@ require 'messagebird/group_reference'
12
12
  require 'messagebird/hlr'
13
13
  require 'messagebird/http_client'
14
14
  require 'messagebird/message_reference'
15
- require 'messagebird/signed_request'
15
+ require 'messagebird/signed_request' # @deprecated
16
+ require 'messagebird/request_validator'
16
17
  require 'messagebird/verify'
17
18
  require 'messagebird/message'
18
19
  require 'messagebird/voicemessage'
metadata CHANGED
@@ -1,57 +1,71 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: messagebird-rest
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maurice Nonnekes
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-25 00:00:00.000000000 Z
11
+ date: 2022-04-11 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: jwt
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 2.3.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 2.3.0
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: rspec
15
29
  requirement: !ruby/object:Gem::Requirement
16
30
  requirements:
17
31
  - - "~>"
18
32
  - !ruby/object:Gem::Version
19
- version: 3.10.0
33
+ version: 3.11.0
20
34
  type: :development
21
35
  prerelease: false
22
36
  version_requirements: !ruby/object:Gem::Requirement
23
37
  requirements:
24
38
  - - "~>"
25
39
  - !ruby/object:Gem::Version
26
- version: 3.10.0
40
+ version: 3.11.0
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rubocop
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
45
  - - "~>"
32
46
  - !ruby/object:Gem::Version
33
- version: 0.77.0
47
+ version: 1.26.1
34
48
  type: :development
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
52
  - - "~>"
39
53
  - !ruby/object:Gem::Version
40
- version: 0.77.0
54
+ version: 1.26.1
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: webmock
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
59
  - - "~>"
46
60
  - !ruby/object:Gem::Version
47
- version: 3.7.5
61
+ version: 3.14.0
48
62
  type: :development
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
66
  - - "~>"
53
67
  - !ruby/object:Gem::Version
54
- version: 3.7.5
68
+ version: 3.14.0
55
69
  description: A simple REST API for MessageBird in Ruby
56
70
  email: maurice@messagebird.com
57
71
  executables: []
@@ -87,6 +101,7 @@ files:
87
101
  - lib/messagebird/number.rb
88
102
  - lib/messagebird/number_client.rb
89
103
  - lib/messagebird/recipient.rb
104
+ - lib/messagebird/request_validator.rb
90
105
  - lib/messagebird/signed_request.rb
91
106
  - lib/messagebird/verify.rb
92
107
  - lib/messagebird/verify_email_message.rb
@@ -105,7 +120,7 @@ homepage: https://github.com/messagebird/ruby-rest-api
105
120
  licenses:
106
121
  - BSD-2-Clause
107
122
  metadata: {}
108
- post_install_message:
123
+ post_install_message:
109
124
  rdoc_options: []
110
125
  require_paths:
111
126
  - lib
@@ -113,15 +128,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
113
128
  requirements:
114
129
  - - ">="
115
130
  - !ruby/object:Gem::Version
116
- version: '2.0'
131
+ version: '3.0'
117
132
  required_rubygems_version: !ruby/object:Gem::Requirement
118
133
  requirements:
119
134
  - - ">="
120
135
  - !ruby/object:Gem::Version
121
136
  version: '0'
122
137
  requirements: []
123
- rubygems_version: 3.2.15
124
- signing_key:
138
+ rubygems_version: 3.1.2
139
+ signing_key:
125
140
  specification_version: 4
126
141
  summary: MessageBird's REST API
127
142
  test_files: []