From 68acab230cb32f865732319e06ff7b358a874e5a Mon Sep 17 00:00:00 2001 From: Zolfa Date: Tue, 31 Mar 2020 17:22:03 +0200 Subject: [PATCH 1/6] gfk.py: backport playhouse.gfk from peewee 2.10.2 --- ca_manager/gfk.py | 167 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 ca_manager/gfk.py diff --git a/ca_manager/gfk.py b/ca_manager/gfk.py new file mode 100644 index 0000000..2690780 --- /dev/null +++ b/ca_manager/gfk.py @@ -0,0 +1,167 @@ +""" +Provide a "Generic ForeignKey", similar to Django. A "GFK" is composed of two +columns: an object ID and an object type identifier. The object types are +collected in a global registry (all_models), so all you need to do is subclass +``gfk.Model`` and your model will be added to the registry. + +Example: + +class Tag(Model): + tag = CharField() + object_type = CharField(null=True) + object_id = IntegerField(null=True) + object = GFKField('object_type', 'object_id') + +class Blog(Model): + tags = ReverseGFK(Tag, 'object_type', 'object_id') + +class Photo(Model): + tags = ReverseGFK(Tag, 'object_type', 'object_id') + +tag.object -> a blog or photo +blog.tags -> select query of tags for ``blog`` instance +Blog.tags -> select query of all tags for Blog instances +""" + +from peewee import * +from peewee import BaseModel as _BaseModel +from peewee import Model as _Model +from peewee import SelectQuery +from peewee import UpdateQuery +from peewee import with_metaclass + + +all_models = set() +table_cache = {} + + +class BaseModel(_BaseModel): + def __new__(cls, name, bases, attrs): + cls = super(BaseModel, cls).__new__(cls, name, bases, attrs) + if name not in ('_metaclass_helper_', 'Model'): + all_models.add(cls) + return cls + +class Model(with_metaclass(BaseModel, _Model)): + pass + +def get_model(tbl_name): + if tbl_name not in table_cache: + for model in all_models: + if model._meta.db_table == tbl_name: + table_cache[tbl_name] = model + break + return table_cache.get(tbl_name) + +class BoundGFKField(object): + __slots__ = ('model_class', 'gfk_field') + + def __init__(self, model_class, gfk_field): + self.model_class = model_class + self.gfk_field = gfk_field + + @property + def unique(self): + indexes = self.model_class._meta.indexes + fields = set((self.gfk_field.model_type_field, + self.gfk_field.model_id_field)) + for (indexed_columns, is_unique) in indexes: + if not fields - set(indexed_columns): + return True + return False + + @property + def primary_key(self): + pk = self.model_class._meta.primary_key + if isinstance(pk, CompositeKey): + fields = set((self.gfk_field.model_type_field, + self.gfk_field.model_id_field)) + if not fields - set(pk.field_names): + return True + return False + + def __eq__(self, other): + meta = self.model_class._meta + type_field = meta.fields[self.gfk_field.model_type_field] + id_field = meta.fields[self.gfk_field.model_id_field] + return ( + (type_field == other._meta.db_table) & + (id_field == other._get_pk_value())) + + def __ne__(self, other): + other_cls = type(other) + type_field = other._meta.fields[self.gfk_field.model_type_field] + id_field = other._meta.fields[self.gfk_field.model_id_field] + return ( + (type_field == other._meta.db_table) & + (id_field != other._get_pk_value())) + + +class GFKField(object): + def __init__(self, model_type_field='object_type', + model_id_field='object_id'): + self.model_type_field = model_type_field + self.model_id_field = model_id_field + self.att_name = '.'.join((self.model_type_field, self.model_id_field)) + + def get_obj(self, instance): + data = instance._data + if data.get(self.model_type_field) and data.get(self.model_id_field): + tbl_name = data[self.model_type_field] + model_class = get_model(tbl_name) + if not model_class: + raise AttributeError('Model for table "%s" not found in GFK ' + 'lookup.' % tbl_name) + query = model_class.select().where( + model_class._meta.primary_key == data[self.model_id_field]) + return query.get() + + def __get__(self, instance, instance_type=None): + if instance: + if self.att_name not in instance._obj_cache: + rel_obj = self.get_obj(instance) + if rel_obj: + instance._obj_cache[self.att_name] = rel_obj + return instance._obj_cache.get(self.att_name) + return BoundGFKField(instance_type, self) + + def __set__(self, instance, value): + instance._obj_cache[self.att_name] = value + instance._data[self.model_type_field] = value._meta.db_table + instance._data[self.model_id_field] = value._get_pk_value() + + +class ReverseGFK(object): + def __init__(self, model, model_type_field='object_type', + model_id_field='object_id'): + self.model_class = model + self.model_type_field = model._meta.fields[model_type_field] + self.model_id_field = model._meta.fields[model_id_field] + + def __get__(self, instance, instance_type=None): + if instance: + return self.model_class.select().where( + (self.model_type_field == instance._meta.db_table) & + (self.model_id_field == instance._get_pk_value()) + ) + else: + return self.model_class.select().where( + self.model_type_field == instance_type._meta.db_table + ) + + def __set__(self, instance, value): + mtv = instance._meta.db_table + miv = instance._get_pk_value() + if (isinstance(value, SelectQuery) and + value.model_class == self.model_class): + UpdateQuery(self.model_class, { + self.model_type_field: mtv, + self.model_id_field: miv, + }).where(value._where).execute() + elif all(map(lambda i: isinstance(i, self.model_class), value)): + for obj in value: + setattr(obj, self.model_type_field.name, mtv) + setattr(obj, self.model_id_field.name, miv) + obj.save() + else: + raise ValueError('ReverseGFK field unable to handle "%s"' % value) From 4adefa503e12d0ace416f7eba05ba542f68f92a6 Mon Sep 17 00:00:00 2001 From: Zolfa Date: Tue, 31 Mar 2020 17:22:10 +0200 Subject: [PATCH 2/6] gfk.py: fix for peewee 3.13.2 --- ca_manager/gfk.py | 67 +++++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/ca_manager/gfk.py b/ca_manager/gfk.py index 2690780..5f8d357 100644 --- a/ca_manager/gfk.py +++ b/ca_manager/gfk.py @@ -24,10 +24,10 @@ class Photo(Model): """ from peewee import * -from peewee import BaseModel as _BaseModel +from peewee import ModelBase as _BaseModel from peewee import Model as _Model from peewee import SelectQuery -from peewee import UpdateQuery +from peewee import Update as UpdateQuery from peewee import with_metaclass @@ -43,26 +43,29 @@ def __new__(cls, name, bases, attrs): return cls class Model(with_metaclass(BaseModel, _Model)): - pass + def __init__(self, *args, **kwargs): + self._obj_cache = {} + return super(Model, self).__init__(*args, **kwargs) + def get_model(tbl_name): if tbl_name not in table_cache: for model in all_models: - if model._meta.db_table == tbl_name: + if model._meta.table_name == tbl_name: table_cache[tbl_name] = model break return table_cache.get(tbl_name) class BoundGFKField(object): - __slots__ = ('model_class', 'gfk_field') + __slots__ = ('model', 'gfk_field') - def __init__(self, model_class, gfk_field): - self.model_class = model_class + def __init__(self, model, gfk_field): + self.model = model self.gfk_field = gfk_field @property def unique(self): - indexes = self.model_class._meta.indexes + indexes = self.model._meta.indexes fields = set((self.gfk_field.model_type_field, self.gfk_field.model_id_field)) for (indexed_columns, is_unique) in indexes: @@ -72,7 +75,7 @@ def unique(self): @property def primary_key(self): - pk = self.model_class._meta.primary_key + pk = self.model._meta.primary_key if isinstance(pk, CompositeKey): fields = set((self.gfk_field.model_type_field, self.gfk_field.model_id_field)) @@ -81,20 +84,20 @@ def primary_key(self): return False def __eq__(self, other): - meta = self.model_class._meta + meta = self.model._meta type_field = meta.fields[self.gfk_field.model_type_field] id_field = meta.fields[self.gfk_field.model_id_field] return ( - (type_field == other._meta.db_table) & - (id_field == other._get_pk_value())) + (type_field == other._meta.table_name) & + (id_field == other.get_id())) def __ne__(self, other): other_cls = type(other) type_field = other._meta.fields[self.gfk_field.model_type_field] id_field = other._meta.fields[self.gfk_field.model_id_field] return ( - (type_field == other._meta.db_table) & - (id_field != other._get_pk_value())) + (type_field == other._meta.table_name) & + (id_field != other.get_id())) class GFKField(object): @@ -105,15 +108,15 @@ def __init__(self, model_type_field='object_type', self.att_name = '.'.join((self.model_type_field, self.model_id_field)) def get_obj(self, instance): - data = instance._data + data = instance.__data__ if data.get(self.model_type_field) and data.get(self.model_id_field): tbl_name = data[self.model_type_field] - model_class = get_model(tbl_name) - if not model_class: + model = get_model(tbl_name) + if not model: raise AttributeError('Model for table "%s" not found in GFK ' 'lookup.' % tbl_name) - query = model_class.select().where( - model_class._meta.primary_key == data[self.model_id_field]) + query = model.select().where( + model._meta.primary_key == data[self.model_id_field]) return query.get() def __get__(self, instance, instance_type=None): @@ -127,38 +130,38 @@ def __get__(self, instance, instance_type=None): def __set__(self, instance, value): instance._obj_cache[self.att_name] = value - instance._data[self.model_type_field] = value._meta.db_table - instance._data[self.model_id_field] = value._get_pk_value() + instance.__data__[self.model_type_field] = value._meta.table_name + instance.__data__[self.model_id_field] = value.get_id() class ReverseGFK(object): def __init__(self, model, model_type_field='object_type', model_id_field='object_id'): - self.model_class = model + self.model = model self.model_type_field = model._meta.fields[model_type_field] self.model_id_field = model._meta.fields[model_id_field] def __get__(self, instance, instance_type=None): if instance: - return self.model_class.select().where( - (self.model_type_field == instance._meta.db_table) & - (self.model_id_field == instance._get_pk_value()) + return self.model.select().where( + (self.model_type_field == instance._meta.table_name) & + (self.model_id_field == instance.get_id()) ) else: - return self.model_class.select().where( - self.model_type_field == instance_type._meta.db_table + return self.model.select().where( + self.model_type_field == instance_type._meta.table_name ) def __set__(self, instance, value): - mtv = instance._meta.db_table - miv = instance._get_pk_value() + mtv = instance._meta.table_name + miv = instance.get_id() if (isinstance(value, SelectQuery) and - value.model_class == self.model_class): - UpdateQuery(self.model_class, { + value.model == self.model): + UpdateQuery(self.model, { self.model_type_field: mtv, self.model_id_field: miv, }).where(value._where).execute() - elif all(map(lambda i: isinstance(i, self.model_class), value)): + elif all(map(lambda i: isinstance(i, self.model), value)): for obj in value: setattr(obj, self.model_type_field.name, mtv) setattr(obj, self.model_id_field.name, miv) From 280afa37864b8ac51b9dc39d88219e8033bde356 Mon Sep 17 00:00:00 2001 From: Zolfa Date: Tue, 31 Mar 2020 17:22:26 +0200 Subject: [PATCH 3/6] migrate from playhouse.gfk to backported .gfk With peewee 3.x we also have to user __str__ instear of __repr__ to define model representation. --- ca_manager/manager.py | 2 -- ca_manager/models/authority.py | 5 ++--- ca_manager/models/certificate.py | 5 ++--- ca_manager/models/customModel.py | 2 +- ca_manager/models/ssh.py | 2 -- ca_manager/models/ssl.py | 2 -- 6 files changed, 5 insertions(+), 13 deletions(-) diff --git a/ca_manager/manager.py b/ca_manager/manager.py index aa843ac..56b1be0 100755 --- a/ca_manager/manager.py +++ b/ca_manager/manager.py @@ -6,8 +6,6 @@ import shutil import subprocess -from playhouse.gfk import * - from .lookup import CALookup, RequestLookup, CertificateLookup from .models.ssh import SSHAuthority diff --git a/ca_manager/models/authority.py b/ca_manager/models/authority.py index d819bb9..96ffd92 100755 --- a/ca_manager/models/authority.py +++ b/ca_manager/models/authority.py @@ -1,8 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -from playhouse.gfk import * - from datetime import datetime import os @@ -11,6 +9,7 @@ from .customModel import CustomModel from .certificate import Certificate +from ..gfk import * from ..paths import * __doc__ = """ @@ -86,5 +85,5 @@ def sign(self, request): def generate_certificate(self, request): raise NotImplementedError() - def __repr__(self): + def __str__(self): return ('%s %s (%s), created on %s' % (self.__class__.__name__, self.ca_id, self.name, self.creation_date)) diff --git a/ca_manager/models/certificate.py b/ca_manager/models/certificate.py index 23cb29d..de7031e 100755 --- a/ca_manager/models/certificate.py +++ b/ca_manager/models/certificate.py @@ -1,13 +1,12 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -from playhouse.gfk import * - import os import json from .customModel import CustomModel +from ..gfk import * from ..paths import * @@ -51,7 +50,7 @@ class Certificate(CustomModel): help_text='certificate lifecycle state', ) - def __repr__(self): + def __str__(self): msg = """<%s:%s> for %s signed %s by %s""" return ( diff --git a/ca_manager/models/customModel.py b/ca_manager/models/customModel.py index c6c99b0..9d18e17 100644 --- a/ca_manager/models/customModel.py +++ b/ca_manager/models/customModel.py @@ -1,6 +1,6 @@ -from playhouse.gfk import * import os +from ..gfk import * from ..paths import * custom_db = SqliteDatabase(os.path.join(MANAGER_PATH, 'ca_manager.db')) diff --git a/ca_manager/models/ssh.py b/ca_manager/models/ssh.py index 0e6b607..f2a119b 100644 --- a/ca_manager/models/ssh.py +++ b/ca_manager/models/ssh.py @@ -1,8 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -from playhouse.gfk import * - import os.path import subprocess diff --git a/ca_manager/models/ssl.py b/ca_manager/models/ssl.py index e4d93d8..9321112 100644 --- a/ca_manager/models/ssl.py +++ b/ca_manager/models/ssl.py @@ -1,8 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -from playhouse.gfk import * - import os from inspect import getsourcefile import subprocess From 69557b6aa80a63f768d76a4ab2e73b2bf3da20ce Mon Sep 17 00:00:00 2001 From: Zolfa Date: Tue, 31 Mar 2020 17:22:43 +0200 Subject: [PATCH 4/6] update requirements --- requirements.txt | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index e41b35e..5bde15b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ appdirs==1.4.0 packaging==16.8 -peewee==2.8.5 +peewee==3.13.2 six==1.10.0 diff --git a/setup.py b/setup.py index 9f4ac05..a5b1373 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ ], install_requires=[ 'fqdn', - 'peewee<3', + 'peewee>=3', ], scripts=[ 'bin/ca-server', From bed0274c1cf68bf0edc1781853cd5248dec18d0f Mon Sep 17 00:00:00 2001 From: Zolfa Date: Tue, 31 Mar 2020 17:23:02 +0200 Subject: [PATCH 5/6] remove unnecessary dependencies --- requirements.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index 5bde15b..27faf2d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1 @@ -appdirs==1.4.0 -packaging==16.8 peewee==3.13.2 -six==1.10.0 From 8360b010bbf766e6cd0e945a8a9fee9adccac61f Mon Sep 17 00:00:00 2001 From: Zolfa Date: Mon, 13 Apr 2020 20:26:57 +0200 Subject: [PATCH 6/6] fix problems with __bool__ values --- ca_manager/models/authority.py | 4 ++-- ca_manager/shell.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ca_manager/models/authority.py b/ca_manager/models/authority.py index 96ffd92..8d8f2f6 100755 --- a/ca_manager/models/authority.py +++ b/ca_manager/models/authority.py @@ -48,8 +48,8 @@ class Authority(CustomModel): help_text='is root authority?', ) - def __bool__(self): - return os.path.exists(self.path) +# def __bool__(self): +# return os.path.exists(self.path) @property def path(self): diff --git a/ca_manager/shell.py b/ca_manager/shell.py index 1cb3931..4fc7f15 100755 --- a/ca_manager/shell.py +++ b/ca_manager/shell.py @@ -51,7 +51,7 @@ def do_describe_ca(self, l): ca = self.ca_manager.ca[argv[0]] - if ca: + if ca != None: ca_description = """ Certification authority: %s --------------------------------------------------