Class: OAuthSignature::Certificate

Inherits:
Object
  • Object
show all
Defined in:
lib/oauth_signature/certificate.rb

Overview

Author:

Since:

  • 0.0.1

Version:

  • 0.2

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(encoded_data = nil) ⇒ Certificate

A new instance of Certificate

Parameters:

  • encoded_data (String|nil) (defaults to: nil)

    the encoded certificate

Raises:

Since:

  • 0.0.1


31
32
33
# File 'lib/oauth_signature/certificate.rb', line 31

def initialize(encoded_data = nil)
  parse_encoded_data(encoded_data) if encoded_data
end

Instance Attribute Details

#bodyString|nil (readonly)

Returns the Base64 encoded encrypted certificate body.

Returns:

  • (String|nil)

    the Base64 encoded encrypted certificate body

Since:

  • 0.0.1


22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/oauth_signature/certificate.rb', line 22

class Certificate
  attr_accessor :encoded_data
  attr_reader :signed_by, :signed_on, :signature, :body, :signer

  # A new instance of Certificate
  # @param encoded_data [String|nil] the encoded certificate
  # @raise [OAuthSignatureError::InvalidCertificateError] if encoded_data is malformed
  # @raise [OAuthSignatureError::InvalidSignatureError] if the Signer class is invalid
  # @return [Certificate] an instance of Certificate
  def initialize(encoded_data = nil)
    parse_encoded_data(encoded_data) if encoded_data
  end

  # Assign a {Signer} to the Certificate
  # @param signer [Signer|MySignerClass] the Signer to assign to the certificate
  # @example Assign a signer to a certificate
  #   my_signer = OAuthSignature::Signer.new(id: 'fe34a634-64c9-4333-93a5-f1728563603c')
  #   my_cert = OAuthSignature::Certificate.new('Zm9vIGJ...2E7a2xmag==')
  #   my_cert.assign_signer(my_signer)
  # @return [Certificate] an instance of Certificate
  def assign_signer(signer)
    @signer = signer if signer_valid?(signer)
    self
  end

  # Generate a Certificate
  # @param client [Client|MyClientClass] the client data to encrypt
  # @example Generate a Certificate
  #   # create a client
  #   my_client = Client.new(name: 'My Awesome Application',
  #                          id: 'd58539ce114dfedf324fb1508e4db660',
  #                          secret: '64a6c90b9de9a85863eabaf828d123cc',
  #                          redirect_uri: 'https://my-awesome-app.com/OAuth/callback',
  #                          agent: 'My Awesome Application/1.0.0',
  #                          version: '1.0.0')
  #   #=> <#OAuthSignature::Client>
  #
  #   # create a signer
  #   my_signer = Signer.new(id: 'fe34a634-64c9-4333-93a5-f1728563603c',
  #                          private_key: '-----BEGIN RSA PRIVATE KEY-----\...')
  #   #=> #<OAuthSignature::Signer>
  #
  #   # generate the certificate
  #   my_cert = Certificate.new
  #   my_cert.assign_signer(my_signer)
  #   my_cert.generate(my_signer, my_client)
  #   #=> <#OAuthSignature::Certificate>
  # @return [Certificate] an instance of Certificate
  def generate(client)
    return unless client.valid? && signer_valid?(signer)
    signed = signer.encrypt_and_sign(client.to_json)
    raw_cert = cert_json(signed)
    self.class.new(Base64.encode(raw_cert))
  end

  # Validate a certificate was signed by a Signer
  # @example Validate a certificate signature
  #   certificate = Certificate.new("Zm9vIGJ...2E7a2xmag==")
  #   certificate.assign_signer(my_signer)
  #   certificate.validate_signature
  #   #=> true
  # @return [Boolean] whether or not the signer signed the certificate
  def validate_signature
    return false unless signer_valid?(signer) && signature && body
    signer.validate_signature(signature, body)
  end

  # Read a certificate with a Signer
  # @example Read certificate Data
  #   certificate = Certficiate.new("Zm9vIGJ...2E7a2xmag==")
  #   certificate.assign_signer(my_signer)
  #   certificate.read
  #   #=> { name: 'My Awesome Application', id: 'd58539ce114dfedf324fb1508e4db660',... }
  # @raise [OAuthSignatureError::InvalidCertificateError] if encoded_data is malformed
  # @return [Hash{Symbol => String}|nil] the decrypted client data of the certificate
  def read
    return unless validate_signature
    decrypted = signer.read(body)
    JSON.parse(decrypted, symbolize_names: true)
  rescue JSON::ParserError
    raise OAuthSignatureError::InvalidCertificateError, 'Certificate Invalid'
  end

  private_class_method def self.signer_valid?(signer)
    return false unless signer
    signer.respond_to?(:id) && signer.respond_to?(:encrypt_and_sign) &&
    signer.respond_to?(:validate_signature) && signer.respond_to?(:read)
  end

  private

  def parse_encoded_data(encoded_data)
    @encoded_data = encoded_data
    decoded = JSON.parse(Base64.decode(encoded_data), symbolize_names: true)
    @signed_by = decoded[:signed_by]
    @signed_on = Time.parse decoded[:signed_on] if decoded[:signed_on]
    @signature = decoded[:signature]
    @signer = find_signer(decoded[:signed_by]) if decoded[:signed_by]
    @body = decoded[:certificate]
    self
  rescue JSON::ParserError
    raise OAuthSignatureError::InvalidCertificateError, 'Certificate Invalid'
  end

  def cert_json(signed)
    {
      signed_by: [signer.class.name, signer.id],
      signed_on: Time.now.utc,
      signature: signed[:signature],
      certificate: signed[:data]
    }.to_json
  end

  def signer_valid?(signer)
    self.class.send(:signer_valid?, signer)
  end

  def find_signer(signer_by)
    signer_class = load_signer_class(signed_by[0])
    signer = signer_class.find(signer_by[1]) if signer_class.respond_to?(:find)
    signer = signer_class.load_local(signer_by[1]) if !signer && signer_class.respond_to?(:load_local)
    signer if signer_valid?(signer)
  end

  def load_signer_class(klass)
    Kernel.const_get(klass)
  rescue NameError => err
    raise OAuthSignatureError::InvalidSignatureError, err.message
  end
end

#encoded_dataString

Returns the Base64 encoded certificate data.

Returns:

  • (String)

    the Base64 encoded certificate data

Since:

  • 0.0.1


22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/oauth_signature/certificate.rb', line 22

class Certificate
  attr_accessor :encoded_data
  attr_reader :signed_by, :signed_on, :signature, :body, :signer

  # A new instance of Certificate
  # @param encoded_data [String|nil] the encoded certificate
  # @raise [OAuthSignatureError::InvalidCertificateError] if encoded_data is malformed
  # @raise [OAuthSignatureError::InvalidSignatureError] if the Signer class is invalid
  # @return [Certificate] an instance of Certificate
  def initialize(encoded_data = nil)
    parse_encoded_data(encoded_data) if encoded_data
  end

  # Assign a {Signer} to the Certificate
  # @param signer [Signer|MySignerClass] the Signer to assign to the certificate
  # @example Assign a signer to a certificate
  #   my_signer = OAuthSignature::Signer.new(id: 'fe34a634-64c9-4333-93a5-f1728563603c')
  #   my_cert = OAuthSignature::Certificate.new('Zm9vIGJ...2E7a2xmag==')
  #   my_cert.assign_signer(my_signer)
  # @return [Certificate] an instance of Certificate
  def assign_signer(signer)
    @signer = signer if signer_valid?(signer)
    self
  end

  # Generate a Certificate
  # @param client [Client|MyClientClass] the client data to encrypt
  # @example Generate a Certificate
  #   # create a client
  #   my_client = Client.new(name: 'My Awesome Application',
  #                          id: 'd58539ce114dfedf324fb1508e4db660',
  #                          secret: '64a6c90b9de9a85863eabaf828d123cc',
  #                          redirect_uri: 'https://my-awesome-app.com/OAuth/callback',
  #                          agent: 'My Awesome Application/1.0.0',
  #                          version: '1.0.0')
  #   #=> <#OAuthSignature::Client>
  #
  #   # create a signer
  #   my_signer = Signer.new(id: 'fe34a634-64c9-4333-93a5-f1728563603c',
  #                          private_key: '-----BEGIN RSA PRIVATE KEY-----\...')
  #   #=> #<OAuthSignature::Signer>
  #
  #   # generate the certificate
  #   my_cert = Certificate.new
  #   my_cert.assign_signer(my_signer)
  #   my_cert.generate(my_signer, my_client)
  #   #=> <#OAuthSignature::Certificate>
  # @return [Certificate] an instance of Certificate
  def generate(client)
    return unless client.valid? && signer_valid?(signer)
    signed = signer.encrypt_and_sign(client.to_json)
    raw_cert = cert_json(signed)
    self.class.new(Base64.encode(raw_cert))
  end

  # Validate a certificate was signed by a Signer
  # @example Validate a certificate signature
  #   certificate = Certificate.new("Zm9vIGJ...2E7a2xmag==")
  #   certificate.assign_signer(my_signer)
  #   certificate.validate_signature
  #   #=> true
  # @return [Boolean] whether or not the signer signed the certificate
  def validate_signature
    return false unless signer_valid?(signer) && signature && body
    signer.validate_signature(signature, body)
  end

  # Read a certificate with a Signer
  # @example Read certificate Data
  #   certificate = Certficiate.new("Zm9vIGJ...2E7a2xmag==")
  #   certificate.assign_signer(my_signer)
  #   certificate.read
  #   #=> { name: 'My Awesome Application', id: 'd58539ce114dfedf324fb1508e4db660',... }
  # @raise [OAuthSignatureError::InvalidCertificateError] if encoded_data is malformed
  # @return [Hash{Symbol => String}|nil] the decrypted client data of the certificate
  def read
    return unless validate_signature
    decrypted = signer.read(body)
    JSON.parse(decrypted, symbolize_names: true)
  rescue JSON::ParserError
    raise OAuthSignatureError::InvalidCertificateError, 'Certificate Invalid'
  end

  private_class_method def self.signer_valid?(signer)
    return false unless signer
    signer.respond_to?(:id) && signer.respond_to?(:encrypt_and_sign) &&
    signer.respond_to?(:validate_signature) && signer.respond_to?(:read)
  end

  private

  def parse_encoded_data(encoded_data)
    @encoded_data = encoded_data
    decoded = JSON.parse(Base64.decode(encoded_data), symbolize_names: true)
    @signed_by = decoded[:signed_by]
    @signed_on = Time.parse decoded[:signed_on] if decoded[:signed_on]
    @signature = decoded[:signature]
    @signer = find_signer(decoded[:signed_by]) if decoded[:signed_by]
    @body = decoded[:certificate]
    self
  rescue JSON::ParserError
    raise OAuthSignatureError::InvalidCertificateError, 'Certificate Invalid'
  end

  def cert_json(signed)
    {
      signed_by: [signer.class.name, signer.id],
      signed_on: Time.now.utc,
      signature: signed[:signature],
      certificate: signed[:data]
    }.to_json
  end

  def signer_valid?(signer)
    self.class.send(:signer_valid?, signer)
  end

  def find_signer(signer_by)
    signer_class = load_signer_class(signed_by[0])
    signer = signer_class.find(signer_by[1]) if signer_class.respond_to?(:find)
    signer = signer_class.load_local(signer_by[1]) if !signer && signer_class.respond_to?(:load_local)
    signer if signer_valid?(signer)
  end

  def load_signer_class(klass)
    Kernel.const_get(klass)
  rescue NameError => err
    raise OAuthSignatureError::InvalidSignatureError, err.message
  end
end

#signatureString|nil (readonly)

Returns the Base64 encoded signature of the certificate.

Returns:

  • (String|nil)

    the Base64 encoded signature of the certificate

Since:

  • 0.0.1


22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/oauth_signature/certificate.rb', line 22

class Certificate
  attr_accessor :encoded_data
  attr_reader :signed_by, :signed_on, :signature, :body, :signer

  # A new instance of Certificate
  # @param encoded_data [String|nil] the encoded certificate
  # @raise [OAuthSignatureError::InvalidCertificateError] if encoded_data is malformed
  # @raise [OAuthSignatureError::InvalidSignatureError] if the Signer class is invalid
  # @return [Certificate] an instance of Certificate
  def initialize(encoded_data = nil)
    parse_encoded_data(encoded_data) if encoded_data
  end

  # Assign a {Signer} to the Certificate
  # @param signer [Signer|MySignerClass] the Signer to assign to the certificate
  # @example Assign a signer to a certificate
  #   my_signer = OAuthSignature::Signer.new(id: 'fe34a634-64c9-4333-93a5-f1728563603c')
  #   my_cert = OAuthSignature::Certificate.new('Zm9vIGJ...2E7a2xmag==')
  #   my_cert.assign_signer(my_signer)
  # @return [Certificate] an instance of Certificate
  def assign_signer(signer)
    @signer = signer if signer_valid?(signer)
    self
  end

  # Generate a Certificate
  # @param client [Client|MyClientClass] the client data to encrypt
  # @example Generate a Certificate
  #   # create a client
  #   my_client = Client.new(name: 'My Awesome Application',
  #                          id: 'd58539ce114dfedf324fb1508e4db660',
  #                          secret: '64a6c90b9de9a85863eabaf828d123cc',
  #                          redirect_uri: 'https://my-awesome-app.com/OAuth/callback',
  #                          agent: 'My Awesome Application/1.0.0',
  #                          version: '1.0.0')
  #   #=> <#OAuthSignature::Client>
  #
  #   # create a signer
  #   my_signer = Signer.new(id: 'fe34a634-64c9-4333-93a5-f1728563603c',
  #                          private_key: '-----BEGIN RSA PRIVATE KEY-----\...')
  #   #=> #<OAuthSignature::Signer>
  #
  #   # generate the certificate
  #   my_cert = Certificate.new
  #   my_cert.assign_signer(my_signer)
  #   my_cert.generate(my_signer, my_client)
  #   #=> <#OAuthSignature::Certificate>
  # @return [Certificate] an instance of Certificate
  def generate(client)
    return unless client.valid? && signer_valid?(signer)
    signed = signer.encrypt_and_sign(client.to_json)
    raw_cert = cert_json(signed)
    self.class.new(Base64.encode(raw_cert))
  end

  # Validate a certificate was signed by a Signer
  # @example Validate a certificate signature
  #   certificate = Certificate.new("Zm9vIGJ...2E7a2xmag==")
  #   certificate.assign_signer(my_signer)
  #   certificate.validate_signature
  #   #=> true
  # @return [Boolean] whether or not the signer signed the certificate
  def validate_signature
    return false unless signer_valid?(signer) && signature && body
    signer.validate_signature(signature, body)
  end

  # Read a certificate with a Signer
  # @example Read certificate Data
  #   certificate = Certficiate.new("Zm9vIGJ...2E7a2xmag==")
  #   certificate.assign_signer(my_signer)
  #   certificate.read
  #   #=> { name: 'My Awesome Application', id: 'd58539ce114dfedf324fb1508e4db660',... }
  # @raise [OAuthSignatureError::InvalidCertificateError] if encoded_data is malformed
  # @return [Hash{Symbol => String}|nil] the decrypted client data of the certificate
  def read
    return unless validate_signature
    decrypted = signer.read(body)
    JSON.parse(decrypted, symbolize_names: true)
  rescue JSON::ParserError
    raise OAuthSignatureError::InvalidCertificateError, 'Certificate Invalid'
  end

  private_class_method def self.signer_valid?(signer)
    return false unless signer
    signer.respond_to?(:id) && signer.respond_to?(:encrypt_and_sign) &&
    signer.respond_to?(:validate_signature) && signer.respond_to?(:read)
  end

  private

  def parse_encoded_data(encoded_data)
    @encoded_data = encoded_data
    decoded = JSON.parse(Base64.decode(encoded_data), symbolize_names: true)
    @signed_by = decoded[:signed_by]
    @signed_on = Time.parse decoded[:signed_on] if decoded[:signed_on]
    @signature = decoded[:signature]
    @signer = find_signer(decoded[:signed_by]) if decoded[:signed_by]
    @body = decoded[:certificate]
    self
  rescue JSON::ParserError
    raise OAuthSignatureError::InvalidCertificateError, 'Certificate Invalid'
  end

  def cert_json(signed)
    {
      signed_by: [signer.class.name, signer.id],
      signed_on: Time.now.utc,
      signature: signed[:signature],
      certificate: signed[:data]
    }.to_json
  end

  def signer_valid?(signer)
    self.class.send(:signer_valid?, signer)
  end

  def find_signer(signer_by)
    signer_class = load_signer_class(signed_by[0])
    signer = signer_class.find(signer_by[1]) if signer_class.respond_to?(:find)
    signer = signer_class.load_local(signer_by[1]) if !signer && signer_class.respond_to?(:load_local)
    signer if signer_valid?(signer)
  end

  def load_signer_class(klass)
    Kernel.const_get(klass)
  rescue NameError => err
    raise OAuthSignatureError::InvalidSignatureError, err.message
  end
end

#signed_byArray<String|Fixnum|nil>|nil (readonly)

Returns the Signer class name and identifier.

Returns:

  • (Array<String|Fixnum|nil>|nil)

    the Signer class name and identifier

Since:

  • 0.0.1


22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/oauth_signature/certificate.rb', line 22

class Certificate
  attr_accessor :encoded_data
  attr_reader :signed_by, :signed_on, :signature, :body, :signer

  # A new instance of Certificate
  # @param encoded_data [String|nil] the encoded certificate
  # @raise [OAuthSignatureError::InvalidCertificateError] if encoded_data is malformed
  # @raise [OAuthSignatureError::InvalidSignatureError] if the Signer class is invalid
  # @return [Certificate] an instance of Certificate
  def initialize(encoded_data = nil)
    parse_encoded_data(encoded_data) if encoded_data
  end

  # Assign a {Signer} to the Certificate
  # @param signer [Signer|MySignerClass] the Signer to assign to the certificate
  # @example Assign a signer to a certificate
  #   my_signer = OAuthSignature::Signer.new(id: 'fe34a634-64c9-4333-93a5-f1728563603c')
  #   my_cert = OAuthSignature::Certificate.new('Zm9vIGJ...2E7a2xmag==')
  #   my_cert.assign_signer(my_signer)
  # @return [Certificate] an instance of Certificate
  def assign_signer(signer)
    @signer = signer if signer_valid?(signer)
    self
  end

  # Generate a Certificate
  # @param client [Client|MyClientClass] the client data to encrypt
  # @example Generate a Certificate
  #   # create a client
  #   my_client = Client.new(name: 'My Awesome Application',
  #                          id: 'd58539ce114dfedf324fb1508e4db660',
  #                          secret: '64a6c90b9de9a85863eabaf828d123cc',
  #                          redirect_uri: 'https://my-awesome-app.com/OAuth/callback',
  #                          agent: 'My Awesome Application/1.0.0',
  #                          version: '1.0.0')
  #   #=> <#OAuthSignature::Client>
  #
  #   # create a signer
  #   my_signer = Signer.new(id: 'fe34a634-64c9-4333-93a5-f1728563603c',
  #                          private_key: '-----BEGIN RSA PRIVATE KEY-----\...')
  #   #=> #<OAuthSignature::Signer>
  #
  #   # generate the certificate
  #   my_cert = Certificate.new
  #   my_cert.assign_signer(my_signer)
  #   my_cert.generate(my_signer, my_client)
  #   #=> <#OAuthSignature::Certificate>
  # @return [Certificate] an instance of Certificate
  def generate(client)
    return unless client.valid? && signer_valid?(signer)
    signed = signer.encrypt_and_sign(client.to_json)
    raw_cert = cert_json(signed)
    self.class.new(Base64.encode(raw_cert))
  end

  # Validate a certificate was signed by a Signer
  # @example Validate a certificate signature
  #   certificate = Certificate.new("Zm9vIGJ...2E7a2xmag==")
  #   certificate.assign_signer(my_signer)
  #   certificate.validate_signature
  #   #=> true
  # @return [Boolean] whether or not the signer signed the certificate
  def validate_signature
    return false unless signer_valid?(signer) && signature && body
    signer.validate_signature(signature, body)
  end

  # Read a certificate with a Signer
  # @example Read certificate Data
  #   certificate = Certficiate.new("Zm9vIGJ...2E7a2xmag==")
  #   certificate.assign_signer(my_signer)
  #   certificate.read
  #   #=> { name: 'My Awesome Application', id: 'd58539ce114dfedf324fb1508e4db660',... }
  # @raise [OAuthSignatureError::InvalidCertificateError] if encoded_data is malformed
  # @return [Hash{Symbol => String}|nil] the decrypted client data of the certificate
  def read
    return unless validate_signature
    decrypted = signer.read(body)
    JSON.parse(decrypted, symbolize_names: true)
  rescue JSON::ParserError
    raise OAuthSignatureError::InvalidCertificateError, 'Certificate Invalid'
  end

  private_class_method def self.signer_valid?(signer)
    return false unless signer
    signer.respond_to?(:id) && signer.respond_to?(:encrypt_and_sign) &&
    signer.respond_to?(:validate_signature) && signer.respond_to?(:read)
  end

  private

  def parse_encoded_data(encoded_data)
    @encoded_data = encoded_data
    decoded = JSON.parse(Base64.decode(encoded_data), symbolize_names: true)
    @signed_by = decoded[:signed_by]
    @signed_on = Time.parse decoded[:signed_on] if decoded[:signed_on]
    @signature = decoded[:signature]
    @signer = find_signer(decoded[:signed_by]) if decoded[:signed_by]
    @body = decoded[:certificate]
    self
  rescue JSON::ParserError
    raise OAuthSignatureError::InvalidCertificateError, 'Certificate Invalid'
  end

  def cert_json(signed)
    {
      signed_by: [signer.class.name, signer.id],
      signed_on: Time.now.utc,
      signature: signed[:signature],
      certificate: signed[:data]
    }.to_json
  end

  def signer_valid?(signer)
    self.class.send(:signer_valid?, signer)
  end

  def find_signer(signer_by)
    signer_class = load_signer_class(signed_by[0])
    signer = signer_class.find(signer_by[1]) if signer_class.respond_to?(:find)
    signer = signer_class.load_local(signer_by[1]) if !signer && signer_class.respond_to?(:load_local)
    signer if signer_valid?(signer)
  end

  def load_signer_class(klass)
    Kernel.const_get(klass)
  rescue NameError => err
    raise OAuthSignatureError::InvalidSignatureError, err.message
  end
end

#signed_onTime|nil (readonly)

Returns the datetime the certificate was signed.

Returns:

  • (Time|nil)

    the datetime the certificate was signed

Since:

  • 0.0.1


22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/oauth_signature/certificate.rb', line 22

class Certificate
  attr_accessor :encoded_data
  attr_reader :signed_by, :signed_on, :signature, :body, :signer

  # A new instance of Certificate
  # @param encoded_data [String|nil] the encoded certificate
  # @raise [OAuthSignatureError::InvalidCertificateError] if encoded_data is malformed
  # @raise [OAuthSignatureError::InvalidSignatureError] if the Signer class is invalid
  # @return [Certificate] an instance of Certificate
  def initialize(encoded_data = nil)
    parse_encoded_data(encoded_data) if encoded_data
  end

  # Assign a {Signer} to the Certificate
  # @param signer [Signer|MySignerClass] the Signer to assign to the certificate
  # @example Assign a signer to a certificate
  #   my_signer = OAuthSignature::Signer.new(id: 'fe34a634-64c9-4333-93a5-f1728563603c')
  #   my_cert = OAuthSignature::Certificate.new('Zm9vIGJ...2E7a2xmag==')
  #   my_cert.assign_signer(my_signer)
  # @return [Certificate] an instance of Certificate
  def assign_signer(signer)
    @signer = signer if signer_valid?(signer)
    self
  end

  # Generate a Certificate
  # @param client [Client|MyClientClass] the client data to encrypt
  # @example Generate a Certificate
  #   # create a client
  #   my_client = Client.new(name: 'My Awesome Application',
  #                          id: 'd58539ce114dfedf324fb1508e4db660',
  #                          secret: '64a6c90b9de9a85863eabaf828d123cc',
  #                          redirect_uri: 'https://my-awesome-app.com/OAuth/callback',
  #                          agent: 'My Awesome Application/1.0.0',
  #                          version: '1.0.0')
  #   #=> <#OAuthSignature::Client>
  #
  #   # create a signer
  #   my_signer = Signer.new(id: 'fe34a634-64c9-4333-93a5-f1728563603c',
  #                          private_key: '-----BEGIN RSA PRIVATE KEY-----\...')
  #   #=> #<OAuthSignature::Signer>
  #
  #   # generate the certificate
  #   my_cert = Certificate.new
  #   my_cert.assign_signer(my_signer)
  #   my_cert.generate(my_signer, my_client)
  #   #=> <#OAuthSignature::Certificate>
  # @return [Certificate] an instance of Certificate
  def generate(client)
    return unless client.valid? && signer_valid?(signer)
    signed = signer.encrypt_and_sign(client.to_json)
    raw_cert = cert_json(signed)
    self.class.new(Base64.encode(raw_cert))
  end

  # Validate a certificate was signed by a Signer
  # @example Validate a certificate signature
  #   certificate = Certificate.new("Zm9vIGJ...2E7a2xmag==")
  #   certificate.assign_signer(my_signer)
  #   certificate.validate_signature
  #   #=> true
  # @return [Boolean] whether or not the signer signed the certificate
  def validate_signature
    return false unless signer_valid?(signer) && signature && body
    signer.validate_signature(signature, body)
  end

  # Read a certificate with a Signer
  # @example Read certificate Data
  #   certificate = Certficiate.new("Zm9vIGJ...2E7a2xmag==")
  #   certificate.assign_signer(my_signer)
  #   certificate.read
  #   #=> { name: 'My Awesome Application', id: 'd58539ce114dfedf324fb1508e4db660',... }
  # @raise [OAuthSignatureError::InvalidCertificateError] if encoded_data is malformed
  # @return [Hash{Symbol => String}|nil] the decrypted client data of the certificate
  def read
    return unless validate_signature
    decrypted = signer.read(body)
    JSON.parse(decrypted, symbolize_names: true)
  rescue JSON::ParserError
    raise OAuthSignatureError::InvalidCertificateError, 'Certificate Invalid'
  end

  private_class_method def self.signer_valid?(signer)
    return false unless signer
    signer.respond_to?(:id) && signer.respond_to?(:encrypt_and_sign) &&
    signer.respond_to?(:validate_signature) && signer.respond_to?(:read)
  end

  private

  def parse_encoded_data(encoded_data)
    @encoded_data = encoded_data
    decoded = JSON.parse(Base64.decode(encoded_data), symbolize_names: true)
    @signed_by = decoded[:signed_by]
    @signed_on = Time.parse decoded[:signed_on] if decoded[:signed_on]
    @signature = decoded[:signature]
    @signer = find_signer(decoded[:signed_by]) if decoded[:signed_by]
    @body = decoded[:certificate]
    self
  rescue JSON::ParserError
    raise OAuthSignatureError::InvalidCertificateError, 'Certificate Invalid'
  end

  def cert_json(signed)
    {
      signed_by: [signer.class.name, signer.id],
      signed_on: Time.now.utc,
      signature: signed[:signature],
      certificate: signed[:data]
    }.to_json
  end

  def signer_valid?(signer)
    self.class.send(:signer_valid?, signer)
  end

  def find_signer(signer_by)
    signer_class = load_signer_class(signed_by[0])
    signer = signer_class.find(signer_by[1]) if signer_class.respond_to?(:find)
    signer = signer_class.load_local(signer_by[1]) if !signer && signer_class.respond_to?(:load_local)
    signer if signer_valid?(signer)
  end

  def load_signer_class(klass)
    Kernel.const_get(klass)
  rescue NameError => err
    raise OAuthSignatureError::InvalidSignatureError, err.message
  end
end

#signerSinger|MySignerClass (readonly)

Returns the assigned Certificate signer.

Returns:

  • (Singer|MySignerClass)

    the assigned Certificate signer

Since:

  • 0.0.1


22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/oauth_signature/certificate.rb', line 22

class Certificate
  attr_accessor :encoded_data
  attr_reader :signed_by, :signed_on, :signature, :body, :signer

  # A new instance of Certificate
  # @param encoded_data [String|nil] the encoded certificate
  # @raise [OAuthSignatureError::InvalidCertificateError] if encoded_data is malformed
  # @raise [OAuthSignatureError::InvalidSignatureError] if the Signer class is invalid
  # @return [Certificate] an instance of Certificate
  def initialize(encoded_data = nil)
    parse_encoded_data(encoded_data) if encoded_data
  end

  # Assign a {Signer} to the Certificate
  # @param signer [Signer|MySignerClass] the Signer to assign to the certificate
  # @example Assign a signer to a certificate
  #   my_signer = OAuthSignature::Signer.new(id: 'fe34a634-64c9-4333-93a5-f1728563603c')
  #   my_cert = OAuthSignature::Certificate.new('Zm9vIGJ...2E7a2xmag==')
  #   my_cert.assign_signer(my_signer)
  # @return [Certificate] an instance of Certificate
  def assign_signer(signer)
    @signer = signer if signer_valid?(signer)
    self
  end

  # Generate a Certificate
  # @param client [Client|MyClientClass] the client data to encrypt
  # @example Generate a Certificate
  #   # create a client
  #   my_client = Client.new(name: 'My Awesome Application',
  #                          id: 'd58539ce114dfedf324fb1508e4db660',
  #                          secret: '64a6c90b9de9a85863eabaf828d123cc',
  #                          redirect_uri: 'https://my-awesome-app.com/OAuth/callback',
  #                          agent: 'My Awesome Application/1.0.0',
  #                          version: '1.0.0')
  #   #=> <#OAuthSignature::Client>
  #
  #   # create a signer
  #   my_signer = Signer.new(id: 'fe34a634-64c9-4333-93a5-f1728563603c',
  #                          private_key: '-----BEGIN RSA PRIVATE KEY-----\...')
  #   #=> #<OAuthSignature::Signer>
  #
  #   # generate the certificate
  #   my_cert = Certificate.new
  #   my_cert.assign_signer(my_signer)
  #   my_cert.generate(my_signer, my_client)
  #   #=> <#OAuthSignature::Certificate>
  # @return [Certificate] an instance of Certificate
  def generate(client)
    return unless client.valid? && signer_valid?(signer)
    signed = signer.encrypt_and_sign(client.to_json)
    raw_cert = cert_json(signed)
    self.class.new(Base64.encode(raw_cert))
  end

  # Validate a certificate was signed by a Signer
  # @example Validate a certificate signature
  #   certificate = Certificate.new("Zm9vIGJ...2E7a2xmag==")
  #   certificate.assign_signer(my_signer)
  #   certificate.validate_signature
  #   #=> true
  # @return [Boolean] whether or not the signer signed the certificate
  def validate_signature
    return false unless signer_valid?(signer) && signature && body
    signer.validate_signature(signature, body)
  end

  # Read a certificate with a Signer
  # @example Read certificate Data
  #   certificate = Certficiate.new("Zm9vIGJ...2E7a2xmag==")
  #   certificate.assign_signer(my_signer)
  #   certificate.read
  #   #=> { name: 'My Awesome Application', id: 'd58539ce114dfedf324fb1508e4db660',... }
  # @raise [OAuthSignatureError::InvalidCertificateError] if encoded_data is malformed
  # @return [Hash{Symbol => String}|nil] the decrypted client data of the certificate
  def read
    return unless validate_signature
    decrypted = signer.read(body)
    JSON.parse(decrypted, symbolize_names: true)
  rescue JSON::ParserError
    raise OAuthSignatureError::InvalidCertificateError, 'Certificate Invalid'
  end

  private_class_method def self.signer_valid?(signer)
    return false unless signer
    signer.respond_to?(:id) && signer.respond_to?(:encrypt_and_sign) &&
    signer.respond_to?(:validate_signature) && signer.respond_to?(:read)
  end

  private

  def parse_encoded_data(encoded_data)
    @encoded_data = encoded_data
    decoded = JSON.parse(Base64.decode(encoded_data), symbolize_names: true)
    @signed_by = decoded[:signed_by]
    @signed_on = Time.parse decoded[:signed_on] if decoded[:signed_on]
    @signature = decoded[:signature]
    @signer = find_signer(decoded[:signed_by]) if decoded[:signed_by]
    @body = decoded[:certificate]
    self
  rescue JSON::ParserError
    raise OAuthSignatureError::InvalidCertificateError, 'Certificate Invalid'
  end

  def cert_json(signed)
    {
      signed_by: [signer.class.name, signer.id],
      signed_on: Time.now.utc,
      signature: signed[:signature],
      certificate: signed[:data]
    }.to_json
  end

  def signer_valid?(signer)
    self.class.send(:signer_valid?, signer)
  end

  def find_signer(signer_by)
    signer_class = load_signer_class(signed_by[0])
    signer = signer_class.find(signer_by[1]) if signer_class.respond_to?(:find)
    signer = signer_class.load_local(signer_by[1]) if !signer && signer_class.respond_to?(:load_local)
    signer if signer_valid?(signer)
  end

  def load_signer_class(klass)
    Kernel.const_get(klass)
  rescue NameError => err
    raise OAuthSignatureError::InvalidSignatureError, err.message
  end
end

Instance Method Details

#assign_signer(signer) ⇒ Certificate

Assign a Signer to the Certificate

Examples:

Assign a signer to a certificate

my_signer = OAuthSignature::Signer.new(id: 'fe34a634-64c9-4333-93a5-f1728563603c')
my_cert = OAuthSignature::Certificate.new('Zm9vIGJ...2E7a2xmag==')
my_cert.assign_signer(my_signer)

Parameters:

  • signer (Signer|MySignerClass)

    the Signer to assign to the certificate

Returns:

Since:

  • 0.0.1


42
43
44
45
# File 'lib/oauth_signature/certificate.rb', line 42

def assign_signer(signer)
  @signer = signer if signer_valid?(signer)
  self
end

#generate(client) ⇒ Certificate

Generate a Certificate

Examples:

Generate a Certificate

# create a client
my_client = Client.new(name: 'My Awesome Application',
                       id: 'd58539ce114dfedf324fb1508e4db660',
                       secret: '64a6c90b9de9a85863eabaf828d123cc',
                       redirect_uri: 'https://my-awesome-app.com/OAuth/callback',
                       agent: 'My Awesome Application/1.0.0',
                       version: '1.0.0')
#=> <#OAuthSignature::Client>

# create a signer
my_signer = Signer.new(id: 'fe34a634-64c9-4333-93a5-f1728563603c',
                       private_key: '-----BEGIN RSA PRIVATE KEY-----\...')
#=> #<OAuthSignature::Signer>

# generate the certificate
my_cert = Certificate.new
my_cert.assign_signer(my_signer)
my_cert.generate(my_signer, my_client)
#=> <#OAuthSignature::Certificate>

Parameters:

  • client (Client|MyClientClass)

    the client data to encrypt

Returns:

Since:

  • 0.0.1


70
71
72
73
74
75
# File 'lib/oauth_signature/certificate.rb', line 70

def generate(client)
  return unless client.valid? && signer_valid?(signer)
  signed = signer.encrypt_and_sign(client.to_json)
  raw_cert = cert_json(signed)
  self.class.new(Base64.encode(raw_cert))
end

#readHash{Symbol => String}|nil

Read a certificate with a Signer

Examples:

Read certificate Data

certificate = Certficiate.new("Zm9vIGJ...2E7a2xmag==")
certificate.assign_signer(my_signer)
certificate.read
#=> { name: 'My Awesome Application', id: 'd58539ce114dfedf324fb1508e4db660',... }

Returns:

  • (Hash{Symbol => String}|nil)

    the decrypted client data of the certificate

Raises:

Since:

  • 0.0.1


97
98
99
100
101
102
103
# File 'lib/oauth_signature/certificate.rb', line 97

def read
  return unless validate_signature
  decrypted = signer.read(body)
  JSON.parse(decrypted, symbolize_names: true)
rescue JSON::ParserError
  raise OAuthSignatureError::InvalidCertificateError, 'Certificate Invalid'
end

#validate_signatureBoolean

Validate a certificate was signed by a Signer

Examples:

Validate a certificate signature

certificate = Certificate.new("Zm9vIGJ...2E7a2xmag==")
certificate.assign_signer(my_signer)
certificate.validate_signature
#=> true

Returns:

  • (Boolean)

    whether or not the signer signed the certificate

Since:

  • 0.0.1


84
85
86
87
# File 'lib/oauth_signature/certificate.rb', line 84

def validate_signature
  return false unless signer_valid?(signer) && signature && body
  signer.validate_signature(signature, body)
end