Class: Pwm::Store

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

Defined Under Namespace

Classes: BlankError, BlankKeyError, BlankValueError, FileAlreadyExistsError, FileNotFoundError, KeyNotFoundError, NotInitializedError, WrongMasterPasswordError

Constant Summary

DEFAULT_PASSWORD_POLICY =
ReasonableComplexityPasswordPolicy.new

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(file, master_password) ⇒ Store

Create a new store object by loading an existing file.

Beware: New is overridden; it performs additional actions after before and after #initialize



97
98
99
100
101
# File 'lib/pwm/store.rb', line 97

def initialize(file, master_password)
  @backend = PStore.new(file, true)
  @backend.ultra_safe = true
  @master_password = master_password
end

Class Method Details

.loadObject



54
# File 'lib/pwm/store.rb', line 54

alias_method :load, :new

.new(file, master_password, options = {}) ⇒ Object

Constructs a new store (not only the object, but also the file behind it).



61
62
63
64
65
66
67
68
69
70
71
# File 'lib/pwm/store.rb', line 61

def new(file, master_password, options = {})
  if File.exists?(file) && !options[:force] # don't allow accedidential override of existing file
    raise FileAlreadyExistsError.new(file)
  else
    password_policy.validate!(master_password)
    store = load(file, master_password)
    store.reset!
  end

  store
end

.open(file, master_password) ⇒ Object

Opens an existing store. Throws if the backing file does not exist or isn't initialized.

Raises:



76
77
78
79
80
81
# File 'lib/pwm/store.rb', line 76

def open(file, master_password)
  raise FileNotFoundError.new(file) unless File.exists?(file)
  store = load(file, master_password)
  store.authenticate # do not allow openeing without successful authentication
  store
end

.password_policyObject



83
84
85
# File 'lib/pwm/store.rb', line 83

def password_policy
  @password_policy || DEFAULT_PASSWORD_POLICY
end

.password_policy=(policy) ⇒ Object



87
88
89
# File 'lib/pwm/store.rb', line 87

def password_policy=(policy)
  @password_policy = policy
end

Instance Method Details

#authenticateObject

Check that the master password is correct. This is done to prevent opening an existing but blank store with the wrong password.



118
119
120
121
122
123
124
125
126
127
# File 'lib/pwm/store.rb', line 118

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.



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/pwm/store.rb', line 186

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|
      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

    timestamp!(:last_modified)
    @backend[:system][:salt] = encrypt(Random.rand.to_s)
  }
end

#createdObject

Return the date when the store was created



212
213
214
# File 'lib/pwm/store.rb', line 212

def created
  @backend.transaction(true){@backend[:system][:created]}
end

#delete(key) ⇒ Object

Delete the value that is stored under key and return it

Raises:



157
158
159
160
161
162
163
164
165
# File 'lib/pwm/store.rb', line 157

def delete(key)
  raise BlankKeyError if key.blank?
  @backend.transaction{
    timestamp!(:last_modified)
    old_value = @backend[:user].delete(encrypt(key))
    raise KeyNotFoundError.new(key) unless old_value
    decrypt(old_value)
  }
end

#get(key) ⇒ Object

Return the value stored under key

Raises:



132
133
134
135
136
137
138
139
140
# File 'lib/pwm/store.rb', line 132

def get(key)
  raise BlankKeyError if key.blank?
  @backend.transaction{
    timestamp!(:last_accessed)
    value = @backend[:user][encrypt(key)]
    raise KeyNotFoundError.new(key) unless value
    decrypt(value)
  }
end

#last_accessedObject

Return the date when the store was last accessed



219
220
221
# File 'lib/pwm/store.rb', line 219

def last_accessed
  @backend.transaction(true){@backend[:system][:last_accessed]}
end

#last_modifiedObject

Return the date when the store was last modified



226
227
228
# File 'lib/pwm/store.rb', line 226

def last_modified
  @backend.transaction(true){@backend[:system][:last_modified]}
end

#list(filter = nil) ⇒ Object

Return all keys, optionally filtered by filter.



170
171
172
173
174
175
176
177
178
179
180
# File 'lib/pwm/store.rb', line 170

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

#put(key, value) ⇒ Object

Store value stored under key

Raises:



145
146
147
148
149
150
151
152
# File 'lib/pwm/store.rb', line 145

def put(key, value)
  raise BlankKeyError if key.blank?
  raise BlankValueError if value.blank?
  @backend.transaction{
    timestamp!(:last_modified)
    @backend[:user][encrypt(key)] = encrypt(value)
  }
end

#reset!Object

(Re-) Initialize the database



106
107
108
109
110
111
112
113
# File 'lib/pwm/store.rb', line 106

def reset!
  @backend.transaction{
    @backend[:user] = {}
    @backend[:system] = {}
    @backend[:system][:created] = DateTime.now
    @backend[:system][:salt] = encrypt(Random.rand.to_s)
  }
end