183 lines
3.8 KiB
Python
183 lines
3.8 KiB
Python
from datetime import datetime
|
|
|
|
from .base import PasswordItem, PasswordStorage
|
|
|
|
|
|
class SqlItem(PasswordItem):
|
|
def __init__(self, storage, row):
|
|
self._storage = storage
|
|
self._data = row
|
|
|
|
|
|
def __repr__(self):
|
|
return f'{class_name(self)}("{self.label}", username="{self.username}", domain="{self.domain}")'
|
|
|
|
|
|
def __getitem__(self, key):
|
|
if key not in self.keys():
|
|
raise KeyError(key)
|
|
|
|
value = self._get_value(key)
|
|
return self.__default_parse(key, value)
|
|
|
|
|
|
def __setitem__(self, key, value):
|
|
if key in ['id', 'created', 'modified', 'domain'] or key not in self.keys():
|
|
raise KeyError(key)
|
|
|
|
value = self.__default_parse(key, value)
|
|
self._set_value(key, value)
|
|
|
|
|
|
def __delitem__(self, key):
|
|
raise AttributeError('Cannot delete item values')
|
|
|
|
|
|
def __getattr__(self, key):
|
|
try:
|
|
return self.__getitem__(key)
|
|
|
|
except KeyError:
|
|
return object.__getattribute__(self, key)
|
|
|
|
|
|
def __setattr__(self, key, value):
|
|
try:
|
|
self.__setitem__(key, value)
|
|
|
|
except KeyError:
|
|
object.__setattr__(self, key, value)
|
|
|
|
|
|
def __default_parse(self, key, value):
|
|
if key == 'url' and not isinstance(value, (Url, type(None))):
|
|
return Url(value)
|
|
|
|
elif key in ['created', 'modified'] and not isinstance(value, (DateString, type(None))):
|
|
return DateString.new_http(value)
|
|
|
|
return self._parse_value(key, value)
|
|
|
|
|
|
def _parse_value(self, key, value):
|
|
return value
|
|
|
|
|
|
def _get_value(self, key):
|
|
return self.row[key]
|
|
|
|
|
|
def _set_value(self, key, value):
|
|
self.row[key] = value
|
|
|
|
|
|
def copy_password(self, timeout=60):
|
|
app = get_app()
|
|
app.set_clipboard_text(self.password)
|
|
|
|
timer = TimeoutCallback(timeout, run_in_gui_thread, app.handle_clipboard_clear_password, self.password)
|
|
timer.start()
|
|
|
|
|
|
def copy_username(self):
|
|
get_app().set_clipboard_text(self.username)
|
|
|
|
|
|
def insert_row(self, storage=None):
|
|
if self.id:
|
|
raise RuntimeError('Row already has an ID and is probably in the DB')
|
|
|
|
if storage:
|
|
return storage.insert(**self.to_dict())
|
|
|
|
return self._storage.insert(**self.to_dict())
|
|
|
|
|
|
def update_row(self, **kwargs):
|
|
self.update(**kwargs)
|
|
self._storage.update(self.id, **kwargs)
|
|
|
|
|
|
def remove_row(self):
|
|
self._storage.remove(self.id)
|
|
|
|
|
|
def to_dict(self, indent=None):
|
|
return DotDict({key: self[key] for key in self.keys()})
|
|
|
|
|
|
## dict methods
|
|
def items(self):
|
|
for key in self.keys():
|
|
yield (key, self[key])
|
|
|
|
|
|
def keys(self):
|
|
return PASSWORD_KEYS.copy()
|
|
|
|
|
|
def values(self):
|
|
return tuple(self[key] for key in self.keys())
|
|
|
|
|
|
def update(self, _data={}, **kwargs):
|
|
kwargs.update(data)
|
|
self.data.update(kwargs)
|
|
|
|
|
|
class SqlStorage(PasswordStorage):
|
|
item_class = SqlItem
|
|
|
|
|
|
def __init__(self, db):
|
|
self.db = db
|
|
|
|
|
|
def fetch(self, *args, deleted=None, **kwargs):
|
|
with self.db.session as s:
|
|
if (rowid := kwargs.get('id')):
|
|
if not (row := s.fetch('passwords', id=rowid).one()):
|
|
raise NoPasswordError(rowid)
|
|
|
|
return row
|
|
|
|
data = self.parse_data(*args, **kwargs)
|
|
return s.fetch('passwords', **data)
|
|
|
|
|
|
def insert(self, *args, **kwargs):
|
|
data = self.parse_data(*args, **kwargs)
|
|
data['created'] = datetime.now()
|
|
|
|
if not data.get('domain') and data.get('url'):
|
|
data['domain'] = data['url'].hostname()
|
|
|
|
with self.db.session as s:
|
|
s.insert('passwords', **data)
|
|
|
|
return s.fetch('passwords',
|
|
username = data['username'],
|
|
password = data['password'],
|
|
domain = data['url'].domain if data.get('url') else None
|
|
).one()
|
|
|
|
|
|
def update(self, row_id, *args, **kwargs):
|
|
data = self.parse_data(*args, **kwargs)
|
|
data['modified'] = datetime.now()
|
|
|
|
if (url := data.get('domain')):
|
|
data['domain'] = url.hostname()
|
|
|
|
with self.db.session as s:
|
|
s.update('passwords', data, id=row_id)
|
|
|
|
return s.fetch('passwords', id=row_id).one()
|
|
|
|
|
|
def remove(self, row_id):
|
|
with self.db.session as s:
|
|
s.remove('passwords', id=row_id)
|
|
|
|
return True
|