Class: Pwl::Locker
- Inherits:
-
Object
- Object
- Pwl::Locker
- Defined in:
- lib/pwl/locker.rb
Defined Under Namespace
Classes: BlankError, BlankKeyError, BlankValueError, FileAlreadyExistsError, FileNotFoundError, KeyNotFoundError, NotInitializedError, WrongMasterPasswordError
Constant Summary collapse
- DEFAULT_PASSWORD_POLICY =
ReasonableComplexityPasswordPolicy.new
Class Method Summary collapse
- .load ⇒ Object
-
.new(file, master_password, options = {}) ⇒ Object
Constructs a new locker (not only the object, but also the file behind it).
-
.open(file, master_password) ⇒ Object
Opens an existing locker.
- .password_policy ⇒ Object
- .password_policy=(policy) ⇒ Object
Instance Method Summary collapse
-
#add(entry_or_key, value = nil) ⇒ Object
Store entry or value under key.
-
#all ⇒ Object
Return all entries as array.
-
#authenticate ⇒ Object
Check that the master password is correct.
-
#change_password!(new_master_password) ⇒ Object
Change the master password to
new_master_password
. -
#created ⇒ Object
Return the date when the locker was created.
-
#delete(key) ⇒ Object
Delete the value that is stored under key and return it.
-
#get(key) ⇒ Object
Return the value stored under key.
-
#initialize(file, master_password) ⇒ Locker
constructor
Create a new locker object by loading an existing file.
-
#last_accessed ⇒ Object
Return the date when the locker was last accessed.
-
#last_modified ⇒ Object
Return the date when the locker was last modified.
-
#list(filter = nil) ⇒ Object
Return all keys, optionally filtered by filter.
-
#path ⇒ Object
Return the path to the file backing this locker.
-
#reset! ⇒ Object
(Re-) Initialize the database.
Constructor Details
#initialize(file, master_password) ⇒ Locker
Create a new locker object by loading an existing file.
Beware: New is overridden; it performs additional actions before and after #initialize
95 96 97 98 99 |
# File 'lib/pwl/locker.rb', line 95 def initialize(file, master_password) @backend = PStore.new(file, true) @backend.ultra_safe = true @master_password = master_password end |
Class Method Details
.load ⇒ Object
52 |
# File 'lib/pwl/locker.rb', line 52 alias_method :load, :new |
.new(file, master_password, options = {}) ⇒ Object
Constructs a new locker (not only the object, but also the file behind it).
59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/pwl/locker.rb', line 59 def new(file, master_password, = {}) if File.exists?(file) && ![:force] # don't allow accedidential override of existing file raise FileAlreadyExistsError.new(file) else password_policy.validate!(master_password) locker = load(file, master_password) locker.reset! end locker end |
.open(file, master_password) ⇒ Object
Opens an existing locker. Throws if the backing file does not exist or isn’t initialized.
74 75 76 77 78 79 |
# File 'lib/pwl/locker.rb', line 74 def open(file, master_password) raise FileNotFoundError.new(file) unless File.exists?(file) locker = load(file, master_password) locker.authenticate # do not allow openeing without successful authentication locker end |
.password_policy ⇒ Object
81 82 83 |
# File 'lib/pwl/locker.rb', line 81 def password_policy @password_policy || DEFAULT_PASSWORD_POLICY end |
.password_policy=(policy) ⇒ Object
85 86 87 |
# File 'lib/pwl/locker.rb', line 85 def password_policy=(policy) @password_policy = policy end |
Instance Method Details
#add(entry_or_key, value = nil) ⇒ Object
Store entry or value under key
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/pwl/locker.rb', line 143 def add(entry_or_key, value = nil) if value.nil? and entry_or_key.is_a?(Entry) # treat as entry entry = entry_or_key else entry = Entry.new(entry_or_key) entry.password = value end entry.validate! @backend.transaction{ (:last_modified) @backend[:user][encrypt(entry.name)] = encrypt(EntryMapper.to_json(entry)) } end |
#all ⇒ Object
Return all entries as array
190 191 192 193 194 195 196 |
# File 'lib/pwl/locker.rb', line 190 def all result = [] @backend.transaction(true){ @backend[:user].each{|k,v| result << EntryMapper.from_json(decrypt(v))} } result end |
#authenticate ⇒ Object
Check that the master password is correct. This is done to prevent opening an existing but blank locker with the wrong password.
116 117 118 119 120 121 122 123 124 125 |
# File 'lib/pwl/locker.rb', line 116 def authenticate begin @backend.transaction(true){ raise NotInitializedError.new(@backend.path.path) unless @backend[:user] && @backend[:system] && @backend[:system][:created] check_salt! } rescue OpenSSL::Cipher::CipherError raise WrongMasterPasswordError end end |
#change_password!(new_master_password) ⇒ Object
Change the master password to new_master_password
. Note that we don’t take a password confirmation here. This is up to a UI layer.
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'lib/pwl/locker.rb', line 202 def change_password!(new_master_password) self.class.password_policy.validate!(new_master_password) @backend.transaction{ # Decrypt each key and value with the old master password and encrypt them with the new master password copy = {} @backend[:user].each{|k,v| # No need to (de)serialize - the value comes in as JSON and goes out as JSON new_key = Encryptor.encrypt(decrypt(k), :key => new_master_password) new_val = Encryptor.encrypt(decrypt(v), :key => new_master_password) copy[new_key] = new_val } # re-write user branch with newly encrypted keys and values @backend[:user] = copy # from now on, use the new master password as long as the object lives @master_password = new_master_password (:last_modified) @backend[:system][:salt] = encrypt(Random.rand.to_s) } end |
#created ⇒ Object
Return the date when the locker was created
229 230 231 |
# File 'lib/pwl/locker.rb', line 229 def created @backend.transaction(true){@backend[:system][:created]} end |
#delete(key) ⇒ Object
Delete the value that is stored under key and return it
162 163 164 165 166 167 168 169 170 |
# File 'lib/pwl/locker.rb', line 162 def delete(key) raise BlankKeyError if key.blank? @backend.transaction{ (:last_modified) old_value = @backend[:user].delete(encrypt(key)) raise KeyNotFoundError.new(key) unless old_value EntryMapper.from_json(decrypt(old_value)) } end |
#get(key) ⇒ Object
Return the value stored under key
130 131 132 133 134 135 136 137 138 |
# File 'lib/pwl/locker.rb', line 130 def get(key) raise BlankKeyError if key.blank? @backend.transaction{ (:last_accessed) value = @backend[:user][encrypt(key)] raise KeyNotFoundError.new(key) unless value EntryMapper.from_json(decrypt(value)) } end |
#last_accessed ⇒ Object
Return the date when the locker was last accessed
236 237 238 |
# File 'lib/pwl/locker.rb', line 236 def last_accessed @backend.transaction(true){@backend[:system][:last_accessed]} end |
#last_modified ⇒ Object
Return the date when the locker was last modified
243 244 245 |
# File 'lib/pwl/locker.rb', line 243 def last_modified @backend.transaction(true){@backend[:system][:last_modified]} end |
#list(filter = nil) ⇒ Object
Return all keys, optionally filtered by filter
175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/pwl/locker.rb', line 175 def list(filter = nil) @backend.transaction(true){ result = @backend[:user].keys.collect{|k| decrypt(k)} if filter.blank? result else result.select{|k,v| k =~ /#{filter}/} end } end |
#path ⇒ Object
Return the path to the file backing this locker
250 251 252 |
# File 'lib/pwl/locker.rb', line 250 def path @backend.path end |
#reset! ⇒ Object
(Re-) Initialize the database
104 105 106 107 108 109 110 111 |
# File 'lib/pwl/locker.rb', line 104 def reset! @backend.transaction{ @backend[:user] = {} @backend[:system] = {} @backend[:system][:created] = DateTime.now @backend[:system][:salt] = encrypt(Random.rand.to_s) } end |