class Blobs::ApiClient

Attributes

current_user[RW]
token[RW]

Public Instance Methods

add_dir(base_path, rm = false, persist_locally = false) click to toggle source
# File lib/blobs.rb, line 188
def add_dir(base_path, rm = false, persist_locally = false)
  raise 'No base path given!' unless base_path
  blob_ids = []
  Dir["#{base_path}/**/*.*"].each do |file|
    key = file.split('/')[0...-1].join('/')
    log("Adding file: #{file}")
    log("Key: #{key}")
    unless file.include?('.blob')
      if (blob_id = add_file(file, key, persist_locally))
        blob_ids << blob_id
        File.delete(file) if rm
      end
    end
  end
  blob_ids
end
add_file(file_path, key = 'default', persist_locally = false) click to toggle source
# File lib/blobs.rb, line 157
def add_file(file_path, key = 'default', persist_locally = false)
  raise 'No file path given!' unless file_path
  raise "File doesn't exist!" unless File.file?(file_path)
  file_json = file_to_json(file_path)
  resp = create(file_json, key, true)
  if persist_locally
    if resp['id']
      File.open("#{file_path.gsub(file_json[:file_name], resp['id'])}.blob", 'wb') do |f|
        f.write(Base64.encode64(file_json.to_json))
        f.close
      end
    else
      raise "No blob id for file: #{file_json[:file_name]}"
    end
  end
  resp ? resp['id'] : nil
end
all(key = nil) click to toggle source
# File lib/blobs.rb, line 53
def all(key = nil)
  raise 'No access token!' unless self.token
  url = "#{base_url}/blobs"
  url += "?key=#{CGI::escape(sha256(key))}" if key
  response = HTTParty.get(url, { headers: headers })
  log "Blobs: #{response.parsed_response.inspect}"
  response.parsed_response.map do |blob|
    {
      id: blob['id'],
      created_at: blob['createdAt'],
      json: (blob['json'] ? (JSON.parse(decrypt(blob['json'])) rescue nil) : nil),
      key: blob['key'],
      file: blob['file'] ? blob['file'] : false
    }
  end
end
base_url() click to toggle source
# File lib/blobs.rb, line 18
def base_url
  ENV['BLOB_STORE_API_BASE_URL']
end
create(json, key = 'default', is_file = false) click to toggle source
# File lib/blobs.rb, line 78
def create(json, key = 'default', is_file = false)
  raise 'No access token!' unless self.token
  raise "No json hash!" unless json
  raise "No key!" unless key
  response = HTTParty.post("#{base_url}/blobs",
    {
      body: { json: encrypt(json.to_json), key: sha256(key), isFile: is_file },
      headers: headers
    }
  )
  log "Blob created: #{response.parsed_response.inspect}"
  response.parsed_response
end
create_user(email, password) click to toggle source
# File lib/blobs.rb, line 44
def create_user(email, password)
  raise "You need to set the master key to create users!" unless master_token
  user = { email: email, password: password }
  response = HTTParty.post("#{base_url}/users",
    body: user, headers: { 'Authorization': "Bearer #{master_token}" })
  log "#{response.parsed_response.inspect}"
  response.parsed_response
end
destroy(blob_id) click to toggle source
# File lib/blobs.rb, line 102
def destroy(blob_id)
  raise 'No access token!' unless self.token
  raise "No blob id!" unless blob_id
  response = HTTParty.delete("#{base_url}/blobs/#{blob_id}", { headers: headers })
  log "Blob destroyed: #{response.parsed_response.inspect}"
  response.parsed_response
end
export_key_file(key_file = " click to toggle source
# File lib/blobs.rb, line 114
def export_key_file(key_file = "#{ENV['HOME']}/.blobs.key.enc")
  export_file = "#{ENV['HOME']}/blobs.key-#{Time.now.to_i}"
  decrypt_file(key_file, export_file)
  export_file
end
export_key_file_qrcode(key_file = " click to toggle source
# File lib/blobs.rb, line 120
def export_key_file_qrcode(key_file = "#{ENV['HOME']}/.blobs.key.enc")
  encryption_key unless @encryption_key and @iv
  key_file_json_str = Base64.encode64({ key: @encryption_key, iv: @iv }.to_json)
  qrcode = RQRCode::QRCode.new(key_file_json_str)
  png = qrcode.as_png(
    resize_gte_to: false,
    resize_exactly_to: false,
    fill: 'white',
    color: 'black',
    size: 240,
    border_modules: 4,
    module_px_size: 6,
    file: nil # path to write
  )
  qrcode_file_path = "#{ENV['HOME']}/blobs.key-#{SecureRandom.hex(4)}#{Time.now.to_i}.png"
  IO.write(qrcode_file_path, png.to_s)
  qrcode_file_path
end
file_from_json(file_json, path = '/tmp', blob_id = nil) click to toggle source
# File lib/blobs.rb, line 145
def file_from_json(file_json, path = '/tmp', blob_id = nil)
  if file_json and file_json['file'] and file_json['file_name']
    decoded_string = Base64.decode64(file_json['file'])
    blob_file_path = "#{path}/#{blob_id ? "#{blob_id}-" : ''}#{file_json['file_name']}"
    File.open(blob_file_path, 'wb') do |f|
      f.write(decoded_string)
      f.close
    end
    blob_file_path
  end
end
file_to_json(file_path) click to toggle source
# File lib/blobs.rb, line 139
def file_to_json(file_path)
  encoded_string = Base64.encode64(File.open(file_path, 'rb').read)
  { file: encoded_string, file_name: file_path.split('/').last,
    file_size: File.size(file_path) }
end
find(blob_id) click to toggle source
# File lib/blobs.rb, line 70
def find(blob_id)
  raise 'No access token!' unless self.token
  raise "No blob id!" unless blob_id
  response = HTTParty.get("#{base_url}/blobs/#{blob_id}", { headers: headers })
  log "Blob: #{response.parsed_response.inspect}"
  response.parsed_response ? JSON.parse(decrypt(response.parsed_response['json'])) : nil
end
get_dir(base_path) click to toggle source
# File lib/blobs.rb, line 205
def get_dir(base_path)
  raise 'No base path given!' unless base_path
  files = []
  if (blobs = all(base_path))
    blobs.each do |blob|
      if blob[:file] and blob[:json]
        blob_json = blob[:json]
        log("Decrypting #{blob_json['file_name']} to #{base_path}...")
        files << blob_json['file_name']
        file_from_json(blob_json, base_path)
      end
    end
  end
  files
end
get_file(blob_id, path = '/tmp') click to toggle source
# File lib/blobs.rb, line 181
def get_file(blob_id, path = '/tmp')
  if (blob = find(blob_id))
    return file_from_json(blob, path, blob_id) if blob['file'] and blob['file_name']
  end
  nil
end
log(msg) click to toggle source
# File lib/blobs.rb, line 26
def log(msg)
  puts msg if DEBUG
end
login(email, password) click to toggle source
# File lib/blobs.rb, line 30
def login(email, password)
  auth = { username: email, password: password }
  response = HTTParty.post("#{base_url}/auth",
    basic_auth: auth)
  if response.code == 201 and response.parsed_response
    log "#{response.parsed_response.inspect}"
    self.current_user = response.parsed_response['user']
    self.token = response.parsed_response['token']
    encryption_key
    return self.token
  end
  nil
end
master_token() click to toggle source
# File lib/blobs.rb, line 22
def master_token
  ENV['MASTER_KEY']
end
restore_local_dir(base_path, rm = false) click to toggle source
# File lib/blobs.rb, line 221
def restore_local_dir(base_path, rm = false)
  files = []
  Dir["#{base_path}/**/*.*"].each do |file|
    if file.include?('.blob')
      decoded_string = Base64.decode64(File.open(file, 'rb').read)
      file_json = JSON.parse(decoded_string)
      dir = file.split('/')[0...-1].join('/')
      file_from_json(file_json, dir)
      files << "#{dir}/#{file_json['file_name']}"
      File.delete(file) if rm
    end
  end
  files
end
update(blob_id, json, key = 'default') click to toggle source
# File lib/blobs.rb, line 92
def update(blob_id, json, key = 'default')
  raise 'No access token!' unless self.token
  raise "No blob id!" unless json
  raise "No json hash!" unless json
  response = HTTParty.put("#{base_url}/blobs/#{blob_id}",
    { body: { json: encrypt(json.to_json), key: sha256(key) }, headers: headers })
  log "Blob update: #{response.parsed_response.inspect}"
  response.parsed_response
end
update_file(blob_id, file_path, key = 'default') click to toggle source
# File lib/blobs.rb, line 175
def update_file(blob_id, file_path, key = 'default')
  raise 'No file path given!' unless file_path
  raise "File doesn't exist!" unless File.file?(file_path)
  update(blob_id, file_to_json(file_path), key)
end
user_token() click to toggle source
# File lib/blobs.rb, line 110
def user_token
  Base64.decode64(self.current_user['userToken'])
end

Private Instance Methods

decrypt(enc_str) click to toggle source
# File lib/blobs.rb, line 255
def decrypt(enc_str)
  decipher = OpenSSL::Cipher::AES256.new :CBC
  decipher.decrypt
  decipher.key = encryption_key
  decipher.iv = get_iv
  decipher.update(Base64.decode64(enc_str).strip) + decipher.final
end
decrypt_file(file_path, dec_file_path) click to toggle source
# File lib/blobs.rb, line 321
def decrypt_file(file_path, dec_file_path)
  raise 'No user token!' unless self.user_token

  cipher = OpenSSL::Cipher::AES256.new :CBC
  cipher.decrypt
  cipher.key = self.user_token

  buf = ""
  File.open(dec_file_path, 'wb') do |outf|
    File.open(file_path, 'rb') do |inf|
      while inf.read(4096, buf)
        outf << cipher.update(buf)
      end
      outf << cipher.final
    end
  end
  true
end
encrypt(str) click to toggle source
# File lib/blobs.rb, line 247
def encrypt(str)
  cipher = OpenSSL::Cipher::AES256.new :CBC
  cipher.encrypt
  cipher.key = encryption_key
  cipher.iv = get_iv
  Base64.encode64(cipher.update(str) + cipher.final).strip
end
encrypt_file(file_path, enc_file_path) click to toggle source
# File lib/blobs.rb, line 302
def encrypt_file(file_path, enc_file_path)
  raise 'No user token!' unless self.user_token

  cipher = OpenSSL::Cipher::AES256.new :CBC
  cipher.encrypt
  cipher.key = self.user_token

  buf = ""
  File.open(enc_file_path, 'wb') do |outf|
    File.open(file_path, 'rb') do |inf|
      while inf.read(4096, buf)
        outf << cipher.update(buf)
      end
      outf << cipher.final
    end
  end
  true
end
encryption_key() click to toggle source
# File lib/blobs.rb, line 263
def encryption_key
  return @encryption_key if @encryption_key

  key_file = "#{ENV['HOME']}/.blobs.key.enc"
  decrypted_key_file = "#{key_file.gsub('.enc', '')}-#{SecureRandom.hex(4)}#{Time.now.to_i}"
  if File.file?(key_file)
    if decrypt_file(key_file, decrypted_key_file)
      key_file_contents = File.read(decrypted_key_file)
      key_file_yml = YAML.load(Base64.strict_decode64(key_file_contents))
      if key_file_yml[:key] and key_file_yml[:iv]
        @iv = key_file_yml[:iv]
        @encryption_key = key_file_yml[:key]
        File.delete(decrypted_key_file)
        return @encryption_key
      else
        raise "Key file is corrupt!"
      end
    else
      raise "Key file couldn't be decrypted!"
    end
  else
    cipher = OpenSSL::Cipher::AES256.new :CBC
    @encryption_key = Digest::SHA256.hexdigest(cipher.random_key)[0..31]
    @iv = Digest::SHA256.hexdigest(cipher.random_iv)[0..15]
    key_file_yml = { key: @encryption_key, iv: @iv }
    File.open(decrypted_key_file, 'wb') do |f|
      enc = Base64.strict_encode64(key_file_yml.to_yaml)
      f.write(enc)
      f.close
    end
    File.delete(decrypted_key_file) if encrypt_file(decrypted_key_file, key_file)
  end
  @encryption_key
end
get_iv() click to toggle source
# File lib/blobs.rb, line 298
def get_iv
  @iv
end
headers(additional_headers = {}) click to toggle source
# File lib/blobs.rb, line 237
def headers(additional_headers = {})
  additional_headers.merge({
    'Authorization': "Bearer #{self.token}"
  })
end
sha256(str) click to toggle source
# File lib/blobs.rb, line 243
def sha256(str)
  Base64.encode64(Digest::SHA256.digest(str)).strip
end