An unofficial, mostly Bitwarden-compatible API server written in Ruby (Sinatra and ActiveRecord)
at master 148 lines 4.1 kB view raw
1# 2# Copyright (c) 2017-2018 joshua stein <jcs@jcs.org> 3# 4# Permission to use, copy, modify, and distribute this software for any 5# purpose with or without fee is hereby granted, provided that the above 6# copyright notice and this permission notice appear in all copies. 7# 8# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15# 16 17class Cipher < DBModel 18 self.table_name = "ciphers" 19 #set_primary_key "uuid" 20 21 before_create :generate_uuid_primary_key 22 23 belongs_to :user, foreign_key: :user_uuid, inverse_of: :folders 24 belongs_to :folder, foreign_key: :folder_uuid, inverse_of: :ciphers, optional: true 25 has_many :attachments, foreign_key: :cipher_uuid, dependent: :destroy 26 27 serialize :fields, JSON 28 serialize :login, JSON 29 serialize :securenote, JSON 30 serialize :card, JSON 31 serialize :identity, JSON 32 serialize :passwordhistory, JSON 33 34 TYPE_LOGIN = 1 35 TYPE_NOTE = 2 36 TYPE_CARD = 3 37 TYPE_IDENTITY = 4 38 39 def self.type_s(type) 40 case type 41 when TYPE_LOGIN 42 "login" 43 when TYPE_NOTE 44 "note" 45 when TYPE_CARD 46 "card" 47 when TYPE_IDENTITY 48 "identity" 49 else 50 type.to_s 51 end 52 end 53 54 # migrate from older style everything-in-data to separate fields 55 def migrate_data! 56 return false if !self.data 57 58 js = JSON.parse(self.data) 59 return false if !js 60 61 self.name = js.delete("Name") 62 self.notes = js.delete("Notes") 63 self.fields = js.delete("Fields") 64 65 if self.type == TYPE_LOGIN 66 js["Uris"] = [ 67 { "Uri" => js["Uri"], "Match" => nil }, 68 ] 69 js.delete("Uri") 70 end 71 72 # move the remaining fields into the new dedicated field based on the type 73 fmap = { 74 TYPE_LOGIN => "login", 75 TYPE_NOTE => "securenote", 76 TYPE_CARD => "card", 77 TYPE_IDENTITY => "identity", 78 } 79 self.send("#{fmap[self.type]}=", js) 80 81 self.save || raise("failed migrating #{self.inspect}") 82 true 83 end 84 85 def to_hash 86 { 87 "Id" => self.uuid, 88 "Type" => self.type, 89 "RevisionDate" => self.updated_at.strftime("%Y-%m-%dT%H:%M:%S.000000Z"), 90 "FolderId" => self.folder_uuid, 91 "Favorite" => self.favorite, 92 "OrganizationId" => nil, 93 "Attachments" => self.attachments.map(&:to_hash), 94 "OrganizationUseTotp" => false, 95 "Object" => "cipher", 96 "Name" => self.name, 97 "Notes" => self.notes, 98 "Fields" => self.fields, 99 "Login" => self.login, 100 "Card" => self.card, 101 "Identity" => self.identity, 102 "SecureNote" => self.securenote, 103 "PasswordHistory" => self.passwordhistory, 104 } 105 end 106 107 def update_from_params(params) 108 self.folder_uuid = params[:folderid] 109 self.organization_uuid = params[:organizationid] 110 self.favorite = params[:favorite] 111 self.type = params[:type].to_i 112 113 self.name = params[:name] 114 self.notes = params[:notes] 115 116 self.fields = nil 117 if params[:fields] && params[:fields].is_a?(Array) 118 self.fields = params[:fields].map{|h| h.ucfirst_hash } 119 end 120 121 case self.type 122 when TYPE_LOGIN 123 tlogin = params[:login].ucfirst_hash 124 125 if tlogin["Uris"] && tlogin["Uris"].is_a?(Array) 126 tlogin["Uris"].map!{|h| h.ucfirst_hash } 127 end 128 129 self.login = tlogin 130 131 if params[:passwordhistory].present? 132 self.passwordhistory = params[:passwordhistory]. 133 map{|ph| ph.ucfirst_hash } 134 else 135 self.passwordhistory = nil 136 end 137 138 when TYPE_NOTE 139 self.securenote = params[:securenote].ucfirst_hash 140 141 when TYPE_CARD 142 self.card = params[:card].ucfirst_hash 143 144 when TYPE_IDENTITY 145 self.identity = params[:identity].ucfirst_hash 146 end 147 end 148end