Class: Caren::Api

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

Constant Summary collapse

DIGEST_ALGORITHMS =
[OpenSSL::Digest::SHA256.new, OpenSSL::Digest::SHA1.new]

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(private_key, url, caren_public_key = nil) ⇒ Api

Initialize new API session. Specify your private key to sign outgoing messages and your care provider url. Optionally you can pass the caren public key used to verify incoming requests.



37
38
39
40
41
# File 'lib/caren/caren.rb', line 37

def initialize private_key, url, caren_public_key=nil
  self.url = url
  self.private_key = private_key.is_a?(String) ? Caren::Api.key_from_string(private_key) : private_key
  self.caren_public_key = caren_public_key || Caren::Api.key_from_path("#{File.dirname(__FILE__)}/../../certs/caren-api.pub")
end

Class Attribute Details

.sessionObject

Returns the value of attribute session.



29
30
31
# File 'lib/caren/caren.rb', line 29

def session
  @session
end

Instance Attribute Details

#caren_public_keyObject

The user_agent is an optional identifier



33
34
35
# File 'lib/caren/caren.rb', line 33

def caren_public_key
  @caren_public_key
end

#private_keyObject

The user_agent is an optional identifier



33
34
35
# File 'lib/caren/caren.rb', line 33

def private_key
  @private_key
end

#urlObject

The user_agent is an optional identifier



33
34
35
# File 'lib/caren/caren.rb', line 33

def url
  @url
end

#user_agentObject

The user_agent is an optional identifier



33
34
35
# File 'lib/caren/caren.rb', line 33

def user_agent
  @user_agent
end

Class Method Details

.generate_private_key(size = 2048) ⇒ Object

Generate a new private key



54
55
56
# File 'lib/caren/caren.rb', line 54

def self.generate_private_key size=2048
  OpenSSL::PKey::RSA.generate( size )
end

.key_from_path(path) ⇒ Object

Read a file and create key from string



49
50
51
# File 'lib/caren/caren.rb', line 49

def self.key_from_path path
  self.key_from_string( File.read(path) )
end

.key_from_string(string) ⇒ Object

Create key from string



44
45
46
# File 'lib/caren/caren.rb', line 44

def self.key_from_string string
  OpenSSL::PKey::RSA.new(string)
end

.supported_incoming_objectsObject

These types of Caren objects are supported by the Caren::Api.incoming method



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/caren/caren.rb', line 120

def self.supported_incoming_objects
  { :links => Caren::Link,
    :external_messages => Caren::ExternalMessage,
    :chat_sessions => Caren::ChatSession,
    :chat_session_messages => Caren::ChatSessionMessage,
    :care_providers => Caren::CareProvider,
    :dossier_entries => Caren::Dossier::DossierEntry,
    :billable_categories => Caren::Store::BillableCategory,
    :billables => Caren::Store::Billable,
    :invoices => Caren::Store::Invoice,
    :payments => Caren::Store::Payment,
    :line_items => Caren::Store::LineItem,
    :accounts => Caren::Store::Account,
    :account_entries => Caren::Store::AccountEntry
  }
end

.supported_incoming_single_objectsObject

These types of Caren objects are supported by the Caren::Api.incoming method



138
139
140
141
142
143
144
# File 'lib/caren/caren.rb', line 138

def self.supported_incoming_single_objects
  singles = {}
  self.supported_incoming_objects.each do |object,klass|
    singles[ klass.node_root ] = klass
  end
  return singles
end

Instance Method Details

#check_signature(response) ⇒ Object

Check the signature of the response from rest-client



186
187
188
189
# File 'lib/caren/caren.rb', line 186

def check_signature response
  return response if self.verify_signature( response.headers[:signature], response.headers[:timestamp], nil, response )
  raise Caren::Exceptions::SignatureMismatch.new
end

#create_photo_signature(url_shortcut, external_or_caren_id, private_key = self.private_key) ⇒ Object



197
198
199
# File 'lib/caren/caren.rb', line 197

def create_photo_signature url_shortcut, external_or_caren_id, private_key=self.private_key
  return sign_string(url_shortcut.to_s + external_or_caren_id.to_s, private_key)
end

#delete(path) ⇒ Object



91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/caren/caren.rb', line 91

def delete path
  begin
    timestamp = DateTime.now.to_i
    response = RestClient.delete url_for(path), :content_type => :xml,
                                                :accept => :xml,
                                                :timestamp => timestamp,
                                                :signature => sign(timestamp,path),
                                                :user_agent => user_agent
    return check_signature(response)
  rescue RestClient::Exception => e
    handle_error(e.response,e.http_code)
  end
end

#get(path) ⇒ Object



105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/caren/caren.rb', line 105

def get path
  begin
    timestamp = DateTime.now.to_i
    response = RestClient.get url_for(path), :content_type => :xml,
                                             :accept => :xml,
                                             :timestamp => timestamp,
                                             :signature => sign(timestamp,path),
                                             :user_agent => user_agent
    return check_signature(response)
  rescue RestClient::Exception => e
    handle_error(e.response,e.http_code)
  end
end

#incoming(xml, signature, timestamp) ⇒ Object

Pass an XML string to be handled. Only a valid caren_objects xml hash will be parsed.



147
148
149
150
151
152
153
# File 'lib/caren/caren.rb', line 147

def incoming xml, signature, timestamp
  if self.verify_signature(signature,timestamp, xml)
   return parse(xml)
  else
    raise Caren::Exceptions::SignatureMismatch.new
  end
end

#parse(xml) ⇒ Object



155
156
157
158
159
160
161
162
163
164
165
# File 'lib/caren/caren.rb', line 155

def parse xml
  objects = []
  hash   = Hash.from_xml(xml)
  if hash["caren_objects"]
    hash = hash["caren_objects"]
  end
  Caren::Api.supported_incoming_objects.each do |key,klass|
    objects << (hash[key]||hash[key.to_s]||[]).map{ |h| klass.init_dependent_objects(klass.new(h,xml)) }
  end
  return objects.flatten
end

#parse_object(xml) ⇒ Object



167
168
169
170
171
172
173
174
175
176
# File 'lib/caren/caren.rb', line 167

def parse_object xml
  hash = Hash.from_xml(xml)
  #todo: rewrite so we lookup the xml tag in the supported_incoming_single_objects hash, faster :)
  Caren::Api.supported_incoming_single_objects.each do |key, klass|
    object = hash[key] || hash[key.to_s]
    if object
      return klass.init_dependent_objects(klass.new(object,xml))
    end
  end
end

#post(path, xml) ⇒ Object



77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/caren/caren.rb', line 77

def post path, xml
  begin
    timestamp = DateTime.now.to_i
    response = RestClient.post url_for(path), xml, :content_type => :xml,
                                                   :accept => :xml,
                                                   :timestamp => timestamp,
                                                   :signature => sign(timestamp,path,xml),
                                                   :user_agent => user_agent
    return check_signature(response)
  rescue RestClient::Exception => e
    handle_error(e.response,e.http_code)
  end
end

#put(path, xml) ⇒ Object



63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/caren/caren.rb', line 63

def put path, xml
  begin
    timestamp = DateTime.now.to_i
    response = RestClient.put url_for(path), xml, :content_type => :xml,
                                                  :accept => :xml,
                                                  :timestamp => timestamp,
                                                  :signature => sign(timestamp,path,xml),
                                                  :user_agent => user_agent
    return check_signature(response)
  rescue RestClient::Exception => e
    handle_error(e.response,e.http_code)
  end
end

#sign(timestamp, path = nil, string = nil, private_key = self.private_key) ⇒ Object

Sign your string and timestamp using private key Timestamp is UNIX timestamp seconds since 1970



180
181
182
183
# File 'lib/caren/caren.rb', line 180

def sign timestamp, path=nil, string=nil, private_key=self.private_key
  path = URI.parse(path).path if path
  return sign_string("#{path}#{string}#{timestamp}",private_key)
end

#sign_string(provided_string, key = self.private_key) ⇒ Object



207
208
209
210
# File 'lib/caren/caren.rb', line 207

def sign_string provided_string, key=self.private_key
  digest = OpenSSL::PKey::RSA.new(key).sign( DIGEST_ALGORITHMS.first, provided_string.strip )
  return CGI.escape(Base64.encode64(digest))
end

#url_for(path) ⇒ Object

URL from path using session base url



59
60
61
# File 'lib/caren/caren.rb', line 59

def url_for path
  "#{self.url}#{path}"
end

#verify_photo_signature(signature, url_shortcut, external_id, public_key = self.caren_public_key) ⇒ Object

Verify photo url signature using the caren public key



202
203
204
205
# File 'lib/caren/caren.rb', line 202

def verify_photo_signature signature, url_shortcut, external_id, public_key=self.caren_public_key
  return false unless public_key
  verify_string(signature, url_shortcut.to_s + external_id.to_s, public_key)
end

#verify_signature(signature, timestamp, path, string = nil, public_key = self.caren_public_key) ⇒ Object

Verify the signature using the caren public key



192
193
194
195
# File 'lib/caren/caren.rb', line 192

def verify_signature signature, timestamp, path, string=nil, public_key=self.caren_public_key
  return false unless public_key
  verify_string(signature, "#{path}#{string}#{timestamp}", public_key)
end

#verify_string(provided_string, expected_string, key = self.caren_public_key, options = {}) ⇒ Object



212
213
214
215
216
217
218
219
# File 'lib/caren/caren.rb', line 212

def verify_string provided_string, expected_string, key=self.caren_public_key, options={}
  if options[:skip_cgi_unescape]
    provided_string = Base64.decode64(provided_string.to_s)
  else
    provided_string = Base64.decode64(CGI.unescape(provided_string.to_s))
  end
  DIGEST_ALGORITHMS.map{ |algorithm| key.verify( algorithm, provided_string, expected_string.strip ) }.any?
end