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

- (Store) initialize(file, master_password)

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

+ (Object) load



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

alias_method :load, :new

+ (Object) new(file, master_password, options = {})

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

+ (Object) open(file, master_password)

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

+ (Object) password_policy



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

def password_policy
  @password_policy || DEFAULT_PASSWORD_POLICY
end

+ (Object) password_policy=(policy)



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

def password_policy=(policy)
  @password_policy = policy
end

Instance Method Details

- (Object) authenticate

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

- (Object) change_password!(new_master_password)

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

- (Object) created

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

- (Object) delete(key)

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

- (Object) get(key)

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

- (Object) last_accessed

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

- (Object) last_modified

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

- (Object) list(filter = nil)

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

- (Object) put(key, value)

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

- (Object) reset!

(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