From c206c4ec0a583731825457925dddf901fd4ea6a6 Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Sun, 27 Oct 2024 18:43:11 +0800 Subject: [PATCH 001/108] feat: init model and db --- .../artifacts_corepy/db/__init__.py | 3 + .../artifacts_corepy/db/models.py | 63 +++++++++++ .../artifacts_corepy/db/resource.py | 107 ++++++++++++++++++ .../artifacts_corepy/db/validator.py | 76 +++++++++++++ init.sql | 29 +++++ 5 files changed, 278 insertions(+) create mode 100644 artifacts-corepy/artifacts_corepy/db/__init__.py create mode 100644 artifacts-corepy/artifacts_corepy/db/models.py create mode 100644 artifacts-corepy/artifacts_corepy/db/resource.py create mode 100644 artifacts-corepy/artifacts_corepy/db/validator.py create mode 100644 init.sql diff --git a/artifacts-corepy/artifacts_corepy/db/__init__.py b/artifacts-corepy/artifacts_corepy/db/__init__.py new file mode 100644 index 00000000..2825e7a3 --- /dev/null +++ b/artifacts-corepy/artifacts_corepy/db/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from __future__ import absolute_import diff --git a/artifacts-corepy/artifacts_corepy/db/models.py b/artifacts-corepy/artifacts_corepy/db/models.py new file mode 100644 index 00000000..55ae9a1c --- /dev/null +++ b/artifacts-corepy/artifacts_corepy/db/models.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import + +from talos.db.dictbase import DictBase +from sqlalchemy import Column, DateTime, ForeignKey, String, text, Text, func, create_engine +from sqlalchemy.dialects.mysql import BIGINT, TINYINT +from sqlalchemy.orm import relationship +from sqlalchemy.ext.declarative import declarative_base + +Base = declarative_base() +metadata = Base.metadata + + +class DiffConfTemplate(Base, DictBase): + __tablename__ = 'diff_conf_template' + attributes = [ + 'id', 'type', 'code', 'value', 'description', 'create_user', 'created_time', 'update_user', 'updated_time', + 'is_deleted', 'roles', + ] + + id = Column(BIGINT, primary_key=True, index=True) + type = Column(String(16), nullable=False, comment='类型:应用-app,数据库-db') + code = Column(String(36), nullable=False, comment='编码') + value = Column(Text, comment='文本值') + description = Column(String(128), server_default=text("''")) + create_user = Column(String(36)) + created_time = Column(DateTime, default=func.now()) + update_user = Column(String(36)) + update_time = Column(DateTime, onupdate=func.now()) + is_deleted = Column(TINYINT, nullable=False, default=0, comment='软删除:0,1') + + roles = relationship("RoleDiffConfTemplate", back_populates="diff_conf_template", uselist=True) + + @property + def as_dict(self): + return {c.name: getattr(self, c.name) for c in self.__table__.columns} + + def __repr__(self): + return "{0}:{1}".format(self.type, self.code) + + +class DiffConfTemplateRole(Base, DictBase): + __tablename__ = 'diff_conf_template_role' + attributes = ['role', 'permission'] + + id = Column(BIGINT, primary_key=True, nullable=False) + permission = Column(String(16), nullable=False, comment='权限:MGMT,USE') + role = Column(String(64), nullable=False, comment='角色') + + diff_conf_template_id = Column(ForeignKey('diff_conf_template.id', ondelete='CASCADE')) + diff_conf_template = relationship('DiffConfTemplate', lazy=True) + + @property + def as_dict(self): + return {c.name: getattr(self, c.name) for c in self.__table__.columns} + + def __repr__(self): + return "{0}:{1}".format(self.role, self.diff_conf_template_id) + + +if __name__ == '__main__': + engine = create_engine("mysql+pymysql://root:miya.12345@127.0.0.1:3306/artifacts", encoding="utf-8") + Base.metadata.create_all(engine) diff --git a/artifacts-corepy/artifacts_corepy/db/resource.py b/artifacts-corepy/artifacts_corepy/db/resource.py new file mode 100644 index 00000000..372a8ae4 --- /dev/null +++ b/artifacts-corepy/artifacts_corepy/db/resource.py @@ -0,0 +1,107 @@ +# -*- coding: utf-8 -*- + +from __future__ import absolute_import + +from talos.core import utils +from talos.db import crud, validator +from talos.utils import scoped_globals + +from artifacts_corepy.db import validator as my_validator +from artifacts_corepy.db import models + + +class MetaCRUD(crud.ResourceBase): + _id_prefix = '' + _remove_fields = [] + + def _before_create(self, resource, validate): + if 'id' not in resource and self._id_prefix: + resource['id'] = utils.generate_prefix_uuid(self._id_prefix) + resource['created_user'] = scoped_globals.GLOBALS.request.auth_user or None + + def _before_update(self, rid, resource, validate): + resource['updated_user'] = scoped_globals.GLOBALS.request.auth_user or None + + def create(self, resource, validate=True, detail=True): + ref = super().create(resource, validate=validate, detail=detail) + if ref and self._remove_fields: + for field in self._remove_fields: + del ref[field] + return ref + + def list(self, filters=None, orders=None, offset=None, limit=None, hooks=None): + refs = super().list(filters=filters, orders=orders, offset=offset, limit=limit, hooks=hooks) + if self._remove_fields: + for ref in refs: + for field in self._remove_fields: + del ref[field] + return refs + + def get(self, rid): + ref = super().get(rid) + if ref and self._remove_fields: + for field in self._remove_fields: + del ref[field] + return ref + + def update(self, rid, resource, filters=None, validate=True, detail=True): + before_update, after_update = super().update(rid, resource, filters=filters, validate=validate, detail=detail) + if before_update and self._remove_fields: + for field in self._remove_fields: + del before_update[field] + if after_update and self._remove_fields: + for field in self._remove_fields: + del after_update[field] + return (before_update, after_update) + + def delete(self, rid, filters=None, detail=True): + num_ref, refs = super().delete(rid, filters=filters, detail=detail) + for ref in refs: + if self._remove_fields: + for field in self._remove_fields: + del ref[field] + return (num_ref, refs) + + def delete_all(self, filters=None): + num_ref, refs = super().delete_all(filters=filters) + for ref in refs: + if self._remove_fields: + for field in self._remove_fields: + del ref[field] + return (num_ref, refs) + + +class DiffConfTemplate(MetaCRUD): + orm_meta = models.DiffConfTemplate + _default_order = ['-id'] + + _validate = [ + crud.ColumnValidator(field='type', + rule=my_validator.LengthValidator(1, 16), + validate_on=('create:M', 'update:O')), + crud.ColumnValidator(field='code', + rule=my_validator.LengthValidator(1, 36), + validate_on=('create:M', 'update:M'), + nullable=False), + crud.ColumnValidator(field='value', + rule=my_validator.LengthValidator(1, 40960), + validate_on=('create:M', 'update:M')), + crud.ColumnValidator(field='description', + rule=my_validator.LengthValidator(0, 128), + validate_on=('create:O', 'update:O'), + nullable=True), + crud.ColumnValidator(field='created_user', validate_on=('*:O',), nullable=True), + crud.ColumnValidator(field='created_time', validate_on=('*:O',), nullable=True), + crud.ColumnValidator(field='updated_user', validate_on=('*:O',), nullable=True), + crud.ColumnValidator(field='updated_time', validate_on=('*:O',), nullable=True), + crud.ColumnValidator(field='roles', + rule=validator.TypeValidator(dict), + validate_on=('create:M', 'update:O'), + orm_required=False), + ] + + +class RoleDiffConfTemplate(crud.ResourceBase): + orm_meta = models.RoleDiffConfTemplate + _default_order = ['-id'] + diff --git a/artifacts-corepy/artifacts_corepy/db/validator.py b/artifacts-corepy/artifacts_corepy/db/validator.py new file mode 100644 index 00000000..b0c0db29 --- /dev/null +++ b/artifacts-corepy/artifacts_corepy/db/validator.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- + +from __future__ import absolute_import + +import re +import ipaddress +from talos.core import utils +from talos.core.i18n import _ +from talos.db import validator + + +class LengthValidator(validator.NullValidator): + def __init__(self, minimum, maximum): + self._minimum = minimum + self._maximum = maximum + + def validate(self, value): + if not utils.is_string_type(value): + return _('expected string, not %(type)s ') % {'type': type(value).__name__} + if self._minimum <= len(value) and len(value) <= self._maximum: + return True + return _('length required: %(min)d <= %(value)d <= %(max)d') % { + 'min': self._minimum, + 'value': len(value), + 'max': self._maximum + } + + +class BackRefValidator(validator.NullValidator): + def __init__(self, cls_res): + self.cls_res = cls_res + + def validate(self, value): + if self.cls_res().count(filters={'id': value}) == 0: + return _('reference of %(resource)s(%(id)s) not found') % {'resource': self.cls_res.__name__, 'id': value} + return True + + +TypeValidator = validator.TypeValidator + + +class RepeatableValidator(validator.NullValidator): + def validate(self, value): + choices = ['?', '+', '*'] + if utils.is_string_type(value): + if value not in choices: + return _('expected %(choices)s, not %(value)s') % {'choices': choices, 'value': value} + elif isinstance(value, int): + if value < 1: + return _('value should be >= 1, not %(value)s') % {'value': value} + else: + return _('expected string in %(choices)s or int(>=1), not %(type)s ') % { + 'choices': choices, + 'type': type(value).__name__ + } + return True + + +class ConcatCIDRValidator(validator.NullValidator): + def __init__(self, splitter=r',|\||;'): + self._splitter = splitter + + def validate(self, value): + if not utils.is_string_type(value): + return _('expected string, not %(type)s ') % {'type': type(value).__name__} + cidrs = re.split(self._splitter, value) + cidrs = [x for x in cidrs if x] + errors = [] + for cidr in cidrs: + try: + ipaddress.IPv4Network(cidr) + except Exception: + errors.append(_('invalid cidr: %(cidr)s') % {'cidr': cidr}) + if errors: + return ','.join(errors) + return True diff --git a/init.sql b/init.sql new file mode 100644 index 00000000..859adbdc --- /dev/null +++ b/init.sql @@ -0,0 +1,29 @@ +#@v0.1.0.1-begin@; +CREATE TABLE diff_conf_template ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + type VARCHAR(16) NOT NULL COMMENT '类型:应用-app,数据库-db', + code VARCHAR(36) NOT NULL COMMENT '编码', + value TEXT NULL COMMENT '文本值', + description VARCHAR(128) DEFAULT '' COMMENT '描述', + create_user VARCHAR(36) DEFAULT NULL, + created_time DATETIME DEFAULT CURRENT_TIMESTAMP, + update_user VARCHAR(36) DEFAULT NULL, + update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + is_deleted TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0,1', + INDEX (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE diff_conf_template_role ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + permission VARCHAR(16) NOT NULL COMMENT '权限:MGMT,USE', + role VARCHAR(64) NOT NULL COMMENT '角色', + diff_conf_template_id BIGINT, + CONSTRAINT fk_diff_conf_template + FOREIGN KEY (diff_conf_template_id) + REFERENCES diff_conf_template (id) + ON DELETE CASCADE, + INDEX (diff_conf_template_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +#@v0.1.0.1-end@; + + From b6fa4ba66cde8470a29e81d7612389dfc43f2ec6 Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Thu, 31 Oct 2024 15:40:51 +0800 Subject: [PATCH 002/108] feat: add variable app for diff conf template --- .../apps/variable/__init__.py | 5 + .../artifacts_corepy/apps/variable/api.py | 121 ++++++++++++++ .../apps/variable/controller.py | 34 ++++ .../artifacts_corepy/apps/variable/route.py | 10 ++ .../common/model_controller.py | 150 ++++++++++++++++++ .../artifacts_corepy/db/resource.py | 2 +- 6 files changed, 321 insertions(+), 1 deletion(-) create mode 100644 artifacts-corepy/artifacts_corepy/apps/variable/__init__.py create mode 100644 artifacts-corepy/artifacts_corepy/apps/variable/api.py create mode 100644 artifacts-corepy/artifacts_corepy/apps/variable/controller.py create mode 100644 artifacts-corepy/artifacts_corepy/apps/variable/route.py create mode 100644 artifacts-corepy/artifacts_corepy/common/model_controller.py diff --git a/artifacts-corepy/artifacts_corepy/apps/variable/__init__.py b/artifacts-corepy/artifacts_corepy/apps/variable/__init__.py new file mode 100644 index 00000000..a094396d --- /dev/null +++ b/artifacts-corepy/artifacts_corepy/apps/variable/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- + +from __future__ import absolute_import + +from artifacts_corepy.apps.variable import route diff --git a/artifacts-corepy/artifacts_corepy/apps/variable/api.py b/artifacts-corepy/artifacts_corepy/apps/variable/api.py new file mode 100644 index 00000000..fc408f6b --- /dev/null +++ b/artifacts-corepy/artifacts_corepy/apps/variable/api.py @@ -0,0 +1,121 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import + +import logging +from talos.core import config +from talos.core.i18n import _ +from talos.utils.scoped_globals import GLOBALS +from artifacts_corepy.db import resource + +from artifacts_corepy.common import exceptions + +CONF = config.CONF +LOG = logging.getLogger(__name__) +TOKEN_KEY = 'terminal_subsystem_token' + + +class DiffConfTemplate(resource.DiffConfTemplate): + def count(self, filters=None, offset=None, limit=None, hooks=None): + auth_roles = GLOBALS.request.auth_permissions + filters = filters or {} + filters['roles.role'] = {'in': list(auth_roles)} + return super().count(filters=filters, offset=offset, limit=limit, hooks=hooks) + + def list(self, filters=None, orders=None, offset=None, limit=None, hooks=None): + auth_roles = GLOBALS.request.auth_permissions + filters = filters or {} + filters['roles.role'] = {'in': list(auth_roles)} + refs = super().list(filters=filters, orders=orders, offset=offset, limit=limit, hooks=hooks) + for ref in refs: + # process roles + role_mapping = {'owner': [], 'executor': []} + for role in ref['roles']: + role_mapping[role['type']].append(role['role']) + if set(role_mapping['owner']) & auth_roles: + ref['is_owner'] = 1 + else: + ref['is_owner'] = 0 + ref['roles'] = role_mapping + return refs + + def _addtional_create(self, session, data, created): + if 'roles' in data: + refs = data['roles'] + role_owner = refs.get('owner', []) or [] + role_executor = refs.get('executor', []) or [] + ref_groups = [(role_owner, 'owner', resource.BookmarkRole), + (role_executor, 'executor', resource.BookmarkRole)] + for role_refs, ref_type, resource_type in ref_groups: + reduce_refs = list(set(role_refs)) + reduce_refs.sort(key=role_refs.index) + if ref_type == 'owner' and len(reduce_refs) == 0: + raise exceptions.ValidationError(message=_('length of roles.owner must be >= 1')) + for ref in reduce_refs: + new_ref = {} + new_ref['bookmark_id'] = created['id'] + new_ref['role'] = ref + new_ref['type'] = ref_type + resource_type(transaction=session).create(new_ref) + + def _addtional_update(self, session, rid, data, before_updated, after_updated): + if 'roles' in data: + refs = data['roles'] + role_owner = refs.get('owner', None) + role_executor = refs.get('executor', None) + ref_groups = [(role_owner, 'owner', resource.BookmarkRole), + (role_executor, 'executor', resource.BookmarkRole)] + for role_refs, ref_type, resource_type in ref_groups: + if role_refs is None: + continue + reduce_refs = list(set(role_refs)) + reduce_refs.sort(key=role_refs.index) + if ref_type == 'owner' and len(reduce_refs) == 0: + raise exceptions.ValidationError(message=_('length of roles.owner must be >= 1')) + old_refs = [ + result['role'] for result in resource_type(session=session).list(filters={ + 'bookmark_id': before_updated['id'], + 'type': ref_type + }) + ] + create_refs = list(set(reduce_refs) - set(old_refs)) + create_refs.sort(key=reduce_refs.index) + delete_refs = set(old_refs) - set(reduce_refs) + + if delete_refs: + resource_type(transaction=session).delete_all(filters={ + 'bookmark_id': before_updated['id'], + 'type': ref_type + }) + for ref in create_refs: + new_ref = {} + new_ref['bookmark_id'] = before_updated['id'] + new_ref['role'] = ref + new_ref['type'] = ref_type + resource_type(transaction=session).create(new_ref) + + def update(self, rid, data, filters=None, validate=True, detail=True): + auth_roles = GLOBALS.request.auth_permissions + if super().count({'id': rid}) and resource.BookmarkRole().count({ + 'bookmark_id': rid, + 'role': { + 'in': list(auth_roles) + }, + 'type': 'owner' + }) == 0: + raise exceptions.ValidationError(message=_('the resource(%(resource)s) does not belong to you') % + {'resource': 'Bookmark[%s]' % rid}) + return super().update(rid, data, filters=filters, validate=validate, detail=detail) + + def delete(self, rid, filters=None, detail=True): + auth_roles = GLOBALS.request.auth_permissions + if super().count({'id': rid}) and resource.BookmarkRole().count({ + 'bookmark_id': rid, + 'role': { + 'in': list(auth_roles) + }, + 'type': 'owner' + }) == 0: + raise exceptions.ValidationError(message=_('the resource(%(resource)s) does not belong to you') % + {'resource': 'Bookmark[%s]' % rid}) + return super().delete(rid, filters=filters, detail=detail) + diff --git a/artifacts-corepy/artifacts_corepy/apps/variable/controller.py b/artifacts-corepy/artifacts_corepy/apps/variable/controller.py new file mode 100644 index 00000000..26fb37a3 --- /dev/null +++ b/artifacts-corepy/artifacts_corepy/apps/variable/controller.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import + +from artifacts_corepy.common import exceptions +from artifacts_corepy.common.model_controller import Collection, Item +from artifacts_corepy.apps.variable import api + + +class CollectionDiffConfTemplates(Collection): + name = 'artifacts.diff_conf_templates' + resource = api.DiffConfTemplate + + def on_get(self, req, resp, **kwargs): + self._validate_method(req) + refs = [] + count = 0 + criteria = self._build_criteria(req) + if criteria: + refs = self.list_query(req, criteria, **kwargs) + for r in refs: + # remove password info + r.pop('password', None) + count = len(refs) + resp.json = {'code': 200, 'status': 'OK', 'data': {'count': count, 'data': refs}, 'message': 'success'} + + def list_query(self, req, criteria, **kwargs): + criteria.pop('fields', None) + refs = self.make_resource(req).list_query(**criteria) + return refs + + +class ItemDiffConfTemplate(Item): + name = 'artifacts.diff_conf_templates' + resource = api.DiffConfTemplate diff --git a/artifacts-corepy/artifacts_corepy/apps/variable/route.py b/artifacts-corepy/artifacts_corepy/apps/variable/route.py new file mode 100644 index 00000000..6b86694d --- /dev/null +++ b/artifacts-corepy/artifacts_corepy/apps/variable/route.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- + +from __future__ import absolute_import + +from artifacts_corepy.apps.variable import controller + + +def add_routes(api): + api.add_route('/artifacts/v1/diff-conf-templates', controller.CollectionDiffConfTemplates()) + api.add_route('/artifacts/v1/diff-conf-templates/{rid}', controller.ItemDiffConfTemplate()) diff --git a/artifacts-corepy/artifacts_corepy/common/model_controller.py b/artifacts-corepy/artifacts_corepy/common/model_controller.py new file mode 100644 index 00000000..d03335a9 --- /dev/null +++ b/artifacts-corepy/artifacts_corepy/common/model_controller.py @@ -0,0 +1,150 @@ +# -*- coding: utf-8 -*- + +from __future__ import absolute_import + +import falcon +from talos.common.controller import CollectionController +from talos.common.controller import ItemController +from talos.common.controller import Controller as BaseController +from talos.core import exceptions as base_ex +from talos.core import utils +from talos.core.i18n import _ + +from artifacts_corepy.common import exceptions + + +class Controller(BaseController): + allow_methods = ( + 'GET', + 'POST', + ) + + def on_post(self, req, resp, **kwargs): + self._validate_method(req) + self._validate_data(req) + data = req.json + resp.json = {'code': 200, 'status': 'OK', 'data': self.create(req, data, **kwargs), 'message': 'success'} + resp.status = falcon.HTTP_200 + + def create(self, req, data, **kwargs): + return self.make_resource(req).create(data, **kwargs) + + def on_get(self, req, resp, **kwargs): + self._validate_method(req) + resp.json = {'code': 200, 'status': 'OK', 'data': self.get(req, **kwargs), 'message': 'success'} + resp.status = falcon.HTTP_200 + + def get(self, req, **kwargs): + return self.make_resource(req).get(**kwargs) + + +class Collection(CollectionController): + def on_get(self, req, resp, **kwargs): + self._validate_method(req) + refs = [] + count = 0 + criteria = self._build_criteria(req) + if criteria: + refs = self.list(req, criteria, **kwargs) + count = self.count(req, criteria, results=refs, **kwargs) + resp.json = {'code': 200, 'status': 'OK', 'data': {'count': count, 'data': refs}, 'message': 'success'} + + def on_post(self, req, resp, **kwargs): + self._validate_method(req) + self._validate_data(req) + datas = req.json + if not utils.is_list_type(datas): + raise exceptions.PluginError(_('data must be list type')) + rets = [] + ex_rets = [] + for idx, data in enumerate(datas): + try: + rets.append(self.create(req, data, **kwargs)) + except base_ex.Error as e: + ex_rets.append({'index': idx + 1, 'message': str(e)}) + if len(ex_rets): + raise exceptions.BatchPartialError(num=len(ex_rets), action='create', exception_data={'data': ex_rets}) + resp.json = {'code': 200, 'status': 'OK', 'data': rets, 'message': 'success'} + resp.status = falcon.HTTP_200 + + def on_patch(self, req, resp, **kwargs): + self._validate_method(req) + self._validate_data(req) + datas = req.json + if not utils.is_list_type(datas): + raise exceptions.PluginError(_('data must be list type')) + rets = [] + ex_rets = [] + for idx, data in enumerate(datas): + try: + res_instance = self.make_resource(req) + if res_instance.primary_keys not in data: + raise exceptions.FieldRequired(attribute=res_instance.primary_keys) + rid = data.pop(res_instance.primary_keys) + before_update, after_update = self.update(req, data, rid=rid) + if after_update is None: + raise exceptions.NotFoundError(resource='%s[%s]' % (self.resource.__name__, rid)) + rets.append(after_update) + except base_ex.Error as e: + ex_rets.append({'index': idx + 1, 'message': str(e)}) + if len(ex_rets): + raise exceptions.BatchPartialError(num=len(ex_rets), action='update', exception_data={'data': ex_rets}) + resp.json = {'code': 200, 'status': 'OK', 'data': rets, 'message': 'success'} + resp.status = falcon.HTTP_200 + + def update(self, req, data, **kwargs): + rid = kwargs.pop('rid') + return self.make_resource(req).update(rid, data) + + def on_delete(self, req, resp, **kwargs): + self._validate_method(req) + self._validate_data(req) + datas = req.json + if not utils.is_list_type(datas): + raise exceptions.PluginError(_('data must be list type')) + rets = [] + ex_rets = [] + for idx, data in enumerate(datas): + try: + res_instance = self.make_resource(req) + ref_count, ref_details = self.delete(req, rid=data) + rets.append(ref_details[0]) + except base_ex.Error as e: + ex_rets.append({'index': idx + 1, 'message': str(e)}) + if len(ex_rets): + raise exceptions.BatchPartialError(num=len(ex_rets), action='delete', exception_data={'data': ex_rets}) + resp.json = {'code': 200, 'status': 'OK', 'data': rets, 'message': 'success'} + resp.status = falcon.HTTP_200 + + def delete(self, req, **kwargs): + return self.make_resource(req).delete(**kwargs) + + +class Item(ItemController): + def on_get(self, req, resp, **kwargs): + self._validate_method(req) + ref = self.get(req, **kwargs) + if ref is not None: + resp.json = {'code': 200, 'status': 'OK', 'data': ref, 'message': 'success'} + else: + raise exceptions.NotFoundError(resource='%s[%s]' % (self.resource.__name__, kwargs.get('rid', '-'))) + + def on_patch(self, req, resp, **kwargs): + self._validate_method(req) + self._validate_data(req) + data = req.json + if data is not None and not isinstance(data, dict): + raise exceptions.PluginError(_('data must be dict type')) + ref_before, ref_after = self.update(req, data, **kwargs) + if ref_after is not None: + resp.json = {'code': 200, 'status': 'OK', 'data': ref_after, 'message': 'success'} + else: + raise exceptions.NotFoundError(resource='%s[%s]' % (self.resource.__name__, kwargs.get('rid', '-'))) + + def on_delete(self, req, resp, **kwargs): + self._validate_method(req) + ref, details = self.delete(req, **kwargs) + if ref: + resp.json = {'code': 200, 'status': 'OK', 'data': {'count': ref, 'data': details}, 'message': 'success'} + else: + raise exceptions.NotFoundError(resource='%s[%s]' % (self.resource.__name__, kwargs.get('rid', '-'))) diff --git a/artifacts-corepy/artifacts_corepy/db/resource.py b/artifacts-corepy/artifacts_corepy/db/resource.py index 372a8ae4..b90aa810 100644 --- a/artifacts-corepy/artifacts_corepy/db/resource.py +++ b/artifacts-corepy/artifacts_corepy/db/resource.py @@ -102,6 +102,6 @@ class DiffConfTemplate(MetaCRUD): class RoleDiffConfTemplate(crud.ResourceBase): - orm_meta = models.RoleDiffConfTemplate + orm_meta = models.DiffConfTemplateRole _default_order = ['-id'] From ac08e4851ee07c67240abcd0846ac15d870d1b2a Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Thu, 31 Oct 2024 18:27:52 +0800 Subject: [PATCH 003/108] feat: add _addtional_create --- .../artifacts_corepy/apps/variable/api.py | 111 +----------------- .../apps/variable/controller.py | 28 +---- .../artifacts_corepy/apps/variable/route.py | 4 +- .../common/{model_controller.py => mixin.py} | 9 +- .../artifacts_corepy/db/models.py | 6 +- .../artifacts_corepy/db/resource.py | 64 ++-------- 6 files changed, 29 insertions(+), 193 deletions(-) rename artifacts-corepy/artifacts_corepy/common/{model_controller.py => mixin.py} (95%) diff --git a/artifacts-corepy/artifacts_corepy/apps/variable/api.py b/artifacts-corepy/artifacts_corepy/apps/variable/api.py index fc408f6b..40470c42 100644 --- a/artifacts-corepy/artifacts_corepy/apps/variable/api.py +++ b/artifacts-corepy/artifacts_corepy/apps/variable/api.py @@ -3,119 +3,20 @@ import logging from talos.core import config -from talos.core.i18n import _ -from talos.utils.scoped_globals import GLOBALS from artifacts_corepy.db import resource from artifacts_corepy.common import exceptions CONF = config.CONF LOG = logging.getLogger(__name__) -TOKEN_KEY = 'terminal_subsystem_token' class DiffConfTemplate(resource.DiffConfTemplate): - def count(self, filters=None, offset=None, limit=None, hooks=None): - auth_roles = GLOBALS.request.auth_permissions - filters = filters or {} - filters['roles.role'] = {'in': list(auth_roles)} - return super().count(filters=filters, offset=offset, limit=limit, hooks=hooks) - - def list(self, filters=None, orders=None, offset=None, limit=None, hooks=None): - auth_roles = GLOBALS.request.auth_permissions - filters = filters or {} - filters['roles.role'] = {'in': list(auth_roles)} - refs = super().list(filters=filters, orders=orders, offset=offset, limit=limit, hooks=hooks) - for ref in refs: - # process roles - role_mapping = {'owner': [], 'executor': []} - for role in ref['roles']: - role_mapping[role['type']].append(role['role']) - if set(role_mapping['owner']) & auth_roles: - ref['is_owner'] = 1 - else: - ref['is_owner'] = 0 - ref['roles'] = role_mapping - return refs - def _addtional_create(self, session, data, created): if 'roles' in data: - refs = data['roles'] - role_owner = refs.get('owner', []) or [] - role_executor = refs.get('executor', []) or [] - ref_groups = [(role_owner, 'owner', resource.BookmarkRole), - (role_executor, 'executor', resource.BookmarkRole)] - for role_refs, ref_type, resource_type in ref_groups: - reduce_refs = list(set(role_refs)) - reduce_refs.sort(key=role_refs.index) - if ref_type == 'owner' and len(reduce_refs) == 0: - raise exceptions.ValidationError(message=_('length of roles.owner must be >= 1')) - for ref in reduce_refs: - new_ref = {} - new_ref['bookmark_id'] = created['id'] - new_ref['role'] = ref - new_ref['type'] = ref_type - resource_type(transaction=session).create(new_ref) - - def _addtional_update(self, session, rid, data, before_updated, after_updated): - if 'roles' in data: - refs = data['roles'] - role_owner = refs.get('owner', None) - role_executor = refs.get('executor', None) - ref_groups = [(role_owner, 'owner', resource.BookmarkRole), - (role_executor, 'executor', resource.BookmarkRole)] - for role_refs, ref_type, resource_type in ref_groups: - if role_refs is None: - continue - reduce_refs = list(set(role_refs)) - reduce_refs.sort(key=role_refs.index) - if ref_type == 'owner' and len(reduce_refs) == 0: - raise exceptions.ValidationError(message=_('length of roles.owner must be >= 1')) - old_refs = [ - result['role'] for result in resource_type(session=session).list(filters={ - 'bookmark_id': before_updated['id'], - 'type': ref_type - }) - ] - create_refs = list(set(reduce_refs) - set(old_refs)) - create_refs.sort(key=reduce_refs.index) - delete_refs = set(old_refs) - set(reduce_refs) - - if delete_refs: - resource_type(transaction=session).delete_all(filters={ - 'bookmark_id': before_updated['id'], - 'type': ref_type - }) - for ref in create_refs: - new_ref = {} - new_ref['bookmark_id'] = before_updated['id'] - new_ref['role'] = ref - new_ref['type'] = ref_type - resource_type(transaction=session).create(new_ref) - - def update(self, rid, data, filters=None, validate=True, detail=True): - auth_roles = GLOBALS.request.auth_permissions - if super().count({'id': rid}) and resource.BookmarkRole().count({ - 'bookmark_id': rid, - 'role': { - 'in': list(auth_roles) - }, - 'type': 'owner' - }) == 0: - raise exceptions.ValidationError(message=_('the resource(%(resource)s) does not belong to you') % - {'resource': 'Bookmark[%s]' % rid}) - return super().update(rid, data, filters=filters, validate=validate, detail=detail) - - def delete(self, rid, filters=None, detail=True): - auth_roles = GLOBALS.request.auth_permissions - if super().count({'id': rid}) and resource.BookmarkRole().count({ - 'bookmark_id': rid, - 'role': { - 'in': list(auth_roles) - }, - 'type': 'owner' - }) == 0: - raise exceptions.ValidationError(message=_('the resource(%(resource)s) does not belong to you') % - {'resource': 'Bookmark[%s]' % rid}) - return super().delete(rid, filters=filters, detail=detail) - + for perm_role in data['roles']: + resource.DiffConfTemplateRole(transaction=session).create({ + 'diff_conf_template_id': created['id'], + 'role': perm_role['role'], + 'permission': perm_role['permission'] + }) diff --git a/artifacts-corepy/artifacts_corepy/apps/variable/controller.py b/artifacts-corepy/artifacts_corepy/apps/variable/controller.py index 26fb37a3..ff84a5ff 100644 --- a/artifacts-corepy/artifacts_corepy/apps/variable/controller.py +++ b/artifacts-corepy/artifacts_corepy/apps/variable/controller.py @@ -1,34 +1,18 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import +# from talos.common.controller import CollectionController, ItemController + from artifacts_corepy.common import exceptions -from artifacts_corepy.common.model_controller import Collection, Item +from artifacts_corepy.common.mixin import CollectionController, ItemController from artifacts_corepy.apps.variable import api -class CollectionDiffConfTemplates(Collection): +class CollectionDiffConfTemplates(CollectionController): name = 'artifacts.diff_conf_templates' resource = api.DiffConfTemplate - def on_get(self, req, resp, **kwargs): - self._validate_method(req) - refs = [] - count = 0 - criteria = self._build_criteria(req) - if criteria: - refs = self.list_query(req, criteria, **kwargs) - for r in refs: - # remove password info - r.pop('password', None) - count = len(refs) - resp.json = {'code': 200, 'status': 'OK', 'data': {'count': count, 'data': refs}, 'message': 'success'} - - def list_query(self, req, criteria, **kwargs): - criteria.pop('fields', None) - refs = self.make_resource(req).list_query(**criteria) - return refs - -class ItemDiffConfTemplate(Item): - name = 'artifacts.diff_conf_templates' +class ItemDiffConfTemplate(ItemController): + name = 'artifacts.diff_conf_template' resource = api.DiffConfTemplate diff --git a/artifacts-corepy/artifacts_corepy/apps/variable/route.py b/artifacts-corepy/artifacts_corepy/apps/variable/route.py index 6b86694d..fe4e3042 100644 --- a/artifacts-corepy/artifacts_corepy/apps/variable/route.py +++ b/artifacts-corepy/artifacts_corepy/apps/variable/route.py @@ -6,5 +6,5 @@ def add_routes(api): - api.add_route('/artifacts/v1/diff-conf-templates', controller.CollectionDiffConfTemplates()) - api.add_route('/artifacts/v1/diff-conf-templates/{rid}', controller.ItemDiffConfTemplate()) + api.add_route('/artifacts/api/v1/diff-conf-templates', controller.CollectionDiffConfTemplates()) + api.add_route('/artifacts/api/v1/diff-conf-templates/{rid}', controller.ItemDiffConfTemplate()) diff --git a/artifacts-corepy/artifacts_corepy/common/model_controller.py b/artifacts-corepy/artifacts_corepy/common/mixin.py similarity index 95% rename from artifacts-corepy/artifacts_corepy/common/model_controller.py rename to artifacts-corepy/artifacts_corepy/common/mixin.py index d03335a9..5c31852f 100644 --- a/artifacts-corepy/artifacts_corepy/common/model_controller.py +++ b/artifacts-corepy/artifacts_corepy/common/mixin.py @@ -3,8 +3,8 @@ from __future__ import absolute_import import falcon -from talos.common.controller import CollectionController -from talos.common.controller import ItemController +from talos.common.controller import CollectionController as BaseCollectionController +from talos.common.controller import ItemController as BaseItemController from talos.common.controller import Controller as BaseController from talos.core import exceptions as base_ex from talos.core import utils @@ -38,7 +38,7 @@ def get(self, req, **kwargs): return self.make_resource(req).get(**kwargs) -class Collection(CollectionController): +class CollectionController(BaseCollectionController): def on_get(self, req, resp, **kwargs): self._validate_method(req) refs = [] @@ -61,6 +61,7 @@ def on_post(self, req, resp, **kwargs): try: rets.append(self.create(req, data, **kwargs)) except base_ex.Error as e: + print(e) ex_rets.append({'index': idx + 1, 'message': str(e)}) if len(ex_rets): raise exceptions.BatchPartialError(num=len(ex_rets), action='create', exception_data={'data': ex_rets}) @@ -120,7 +121,7 @@ def delete(self, req, **kwargs): return self.make_resource(req).delete(**kwargs) -class Item(ItemController): +class ItemController(BaseItemController): def on_get(self, req, resp, **kwargs): self._validate_method(req) ref = self.get(req, **kwargs) diff --git a/artifacts-corepy/artifacts_corepy/db/models.py b/artifacts-corepy/artifacts_corepy/db/models.py index 55ae9a1c..36186d9f 100644 --- a/artifacts-corepy/artifacts_corepy/db/models.py +++ b/artifacts-corepy/artifacts_corepy/db/models.py @@ -14,7 +14,7 @@ class DiffConfTemplate(Base, DictBase): __tablename__ = 'diff_conf_template' attributes = [ - 'id', 'type', 'code', 'value', 'description', 'create_user', 'created_time', 'update_user', 'updated_time', + 'id', 'type', 'code', 'value', 'description', 'create_user', 'create_time', 'update_user', 'update_time', 'is_deleted', 'roles', ] @@ -24,12 +24,12 @@ class DiffConfTemplate(Base, DictBase): value = Column(Text, comment='文本值') description = Column(String(128), server_default=text("''")) create_user = Column(String(36)) - created_time = Column(DateTime, default=func.now()) + create_time = Column(DateTime, default=func.now()) update_user = Column(String(36)) update_time = Column(DateTime, onupdate=func.now()) is_deleted = Column(TINYINT, nullable=False, default=0, comment='软删除:0,1') - roles = relationship("RoleDiffConfTemplate", back_populates="diff_conf_template", uselist=True) + roles = relationship("DiffConfTemplateRole", back_populates="diff_conf_template") @property def as_dict(self): diff --git a/artifacts-corepy/artifacts_corepy/db/resource.py b/artifacts-corepy/artifacts_corepy/db/resource.py index b90aa810..365c24ff 100644 --- a/artifacts-corepy/artifacts_corepy/db/resource.py +++ b/artifacts-corepy/artifacts_corepy/db/resource.py @@ -17,63 +17,15 @@ class MetaCRUD(crud.ResourceBase): def _before_create(self, resource, validate): if 'id' not in resource and self._id_prefix: resource['id'] = utils.generate_prefix_uuid(self._id_prefix) - resource['created_user'] = scoped_globals.GLOBALS.request.auth_user or None + resource['create_user'] = scoped_globals.GLOBALS.request.auth_user or None def _before_update(self, rid, resource, validate): - resource['updated_user'] = scoped_globals.GLOBALS.request.auth_user or None - - def create(self, resource, validate=True, detail=True): - ref = super().create(resource, validate=validate, detail=detail) - if ref and self._remove_fields: - for field in self._remove_fields: - del ref[field] - return ref - - def list(self, filters=None, orders=None, offset=None, limit=None, hooks=None): - refs = super().list(filters=filters, orders=orders, offset=offset, limit=limit, hooks=hooks) - if self._remove_fields: - for ref in refs: - for field in self._remove_fields: - del ref[field] - return refs - - def get(self, rid): - ref = super().get(rid) - if ref and self._remove_fields: - for field in self._remove_fields: - del ref[field] - return ref - - def update(self, rid, resource, filters=None, validate=True, detail=True): - before_update, after_update = super().update(rid, resource, filters=filters, validate=validate, detail=detail) - if before_update and self._remove_fields: - for field in self._remove_fields: - del before_update[field] - if after_update and self._remove_fields: - for field in self._remove_fields: - del after_update[field] - return (before_update, after_update) - - def delete(self, rid, filters=None, detail=True): - num_ref, refs = super().delete(rid, filters=filters, detail=detail) - for ref in refs: - if self._remove_fields: - for field in self._remove_fields: - del ref[field] - return (num_ref, refs) - - def delete_all(self, filters=None): - num_ref, refs = super().delete_all(filters=filters) - for ref in refs: - if self._remove_fields: - for field in self._remove_fields: - del ref[field] - return (num_ref, refs) + resource['update_user'] = scoped_globals.GLOBALS.request.auth_user or None class DiffConfTemplate(MetaCRUD): orm_meta = models.DiffConfTemplate - _default_order = ['-id'] + _default_order = ['-id', 'update_time', 'create_time'] _validate = [ crud.ColumnValidator(field='type', @@ -90,18 +42,16 @@ class DiffConfTemplate(MetaCRUD): rule=my_validator.LengthValidator(0, 128), validate_on=('create:O', 'update:O'), nullable=True), - crud.ColumnValidator(field='created_user', validate_on=('*:O',), nullable=True), - crud.ColumnValidator(field='created_time', validate_on=('*:O',), nullable=True), - crud.ColumnValidator(field='updated_user', validate_on=('*:O',), nullable=True), - crud.ColumnValidator(field='updated_time', validate_on=('*:O',), nullable=True), + # crud.ColumnValidator(field='updated_user', validate_on=('*:O',), nullable=True), + # crud.ColumnValidator(field='updated_time', validate_on=('*:O',), nullable=True), crud.ColumnValidator(field='roles', - rule=validator.TypeValidator(dict), + rule=validator.TypeValidator(list), validate_on=('create:M', 'update:O'), orm_required=False), ] -class RoleDiffConfTemplate(crud.ResourceBase): +class DiffConfTemplateRole(crud.ResourceBase): orm_meta = models.DiffConfTemplateRole _default_order = ['-id'] From 88c73266fcfb1a2e4d2309a79061ee3deb803dac Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Thu, 31 Oct 2024 21:45:29 +0800 Subject: [PATCH 004/108] feat: add soft delete support --- .../artifacts_corepy/apps/variable/api.py | 15 +++++++------ .../artifacts_corepy/common/mixin.py | 14 +++++++++++-- .../artifacts_corepy/db/models.py | 6 +++++- .../artifacts_corepy/db/resource.py | 21 ++++++++++++++----- .../artifacts_corepy/server/wsgi_server.py | 2 +- 5 files changed, 43 insertions(+), 15 deletions(-) diff --git a/artifacts-corepy/artifacts_corepy/apps/variable/api.py b/artifacts-corepy/artifacts_corepy/apps/variable/api.py index 40470c42..e2515b5c 100644 --- a/artifacts-corepy/artifacts_corepy/apps/variable/api.py +++ b/artifacts-corepy/artifacts_corepy/apps/variable/api.py @@ -14,9 +14,12 @@ class DiffConfTemplate(resource.DiffConfTemplate): def _addtional_create(self, session, data, created): if 'roles' in data: - for perm_role in data['roles']: - resource.DiffConfTemplateRole(transaction=session).create({ - 'diff_conf_template_id': created['id'], - 'role': perm_role['role'], - 'permission': perm_role['permission'] - }) + for perm, perm_roles in data['roles'].items(): + for perm_role in perm_roles: + if not perm_role: + continue + resource.DiffConfTemplateRole(transaction=session).create({ + 'diff_conf_template_id': created['id'], + 'role': perm_role, + 'permission': perm + }) diff --git a/artifacts-corepy/artifacts_corepy/common/mixin.py b/artifacts-corepy/artifacts_corepy/common/mixin.py index 5c31852f..6653ccfe 100644 --- a/artifacts-corepy/artifacts_corepy/common/mixin.py +++ b/artifacts-corepy/artifacts_corepy/common/mixin.py @@ -61,7 +61,6 @@ def on_post(self, req, resp, **kwargs): try: rets.append(self.create(req, data, **kwargs)) except base_ex.Error as e: - print(e) ex_rets.append({'index': idx + 1, 'message': str(e)}) if len(ex_rets): raise exceptions.BatchPartialError(num=len(ex_rets), action='create', exception_data={'data': ex_rets}) @@ -118,10 +117,15 @@ def on_delete(self, req, resp, **kwargs): resp.status = falcon.HTTP_200 def delete(self, req, **kwargs): - return self.make_resource(req).delete(**kwargs) + # return self.make_resource(req).delete(**kwargs) + before, after = self.make_resource(req).update( + rid=kwargs.get('rid'), resource={"is_deleted": 1}, validate=False + ) + return 1 if before else 0, [after] class ItemController(BaseItemController): + def on_get(self, req, resp, **kwargs): self._validate_method(req) ref = self.get(req, **kwargs) @@ -149,3 +153,9 @@ def on_delete(self, req, resp, **kwargs): resp.json = {'code': 200, 'status': 'OK', 'data': {'count': ref, 'data': details}, 'message': 'success'} else: raise exceptions.NotFoundError(resource='%s[%s]' % (self.resource.__name__, kwargs.get('rid', '-'))) + + def delete(self, req, **kwargs): + before, after = self.make_resource(req).update( + rid=kwargs.get('rid'), resource={"is_deleted": 1}, validate=False + ) + return 1 if before else 0, [after] diff --git a/artifacts-corepy/artifacts_corepy/db/models.py b/artifacts-corepy/artifacts_corepy/db/models.py index 36186d9f..c91559c9 100644 --- a/artifacts-corepy/artifacts_corepy/db/models.py +++ b/artifacts-corepy/artifacts_corepy/db/models.py @@ -2,7 +2,7 @@ from __future__ import absolute_import from talos.db.dictbase import DictBase -from sqlalchemy import Column, DateTime, ForeignKey, String, text, Text, func, create_engine +from sqlalchemy import Column, DateTime, ForeignKey, String, text, Text, func, create_engine, UniqueConstraint from sqlalchemy.dialects.mysql import BIGINT, TINYINT from sqlalchemy.orm import relationship from sqlalchemy.ext.declarative import declarative_base @@ -13,6 +13,10 @@ class DiffConfTemplate(Base, DictBase): __tablename__ = 'diff_conf_template' + __table_args__ = ( + UniqueConstraint('type', 'code', name='uk_type_code'), + ) + attributes = [ 'id', 'type', 'code', 'value', 'description', 'create_user', 'create_time', 'update_user', 'update_time', 'is_deleted', 'roles', diff --git a/artifacts-corepy/artifacts_corepy/db/resource.py b/artifacts-corepy/artifacts_corepy/db/resource.py index 365c24ff..e43e9149 100644 --- a/artifacts-corepy/artifacts_corepy/db/resource.py +++ b/artifacts-corepy/artifacts_corepy/db/resource.py @@ -22,6 +22,19 @@ def _before_create(self, resource, validate): def _before_update(self, rid, resource, validate): resource['update_user'] = scoped_globals.GLOBALS.request.auth_user or None + def _apply_primary_key_filter(self, query, rid): + query = super()._apply_primary_key_filter(query, rid) + + if hasattr(self.orm_meta, 'is_deleted'): + query = query.filter(self.orm_meta.is_deleted == 0) + + return query + + def _addtional_list(self, query, filters): + if hasattr(self.orm_meta, 'is_deleted'): + query = query.filter(self.orm_meta.is_deleted == 0) + return query + class DiffConfTemplate(MetaCRUD): orm_meta = models.DiffConfTemplate @@ -29,7 +42,7 @@ class DiffConfTemplate(MetaCRUD): _validate = [ crud.ColumnValidator(field='type', - rule=my_validator.LengthValidator(1, 16), + rule=my_validator.validator.InValidator(['app', 'db']), validate_on=('create:M', 'update:O')), crud.ColumnValidator(field='code', rule=my_validator.LengthValidator(1, 36), @@ -42,10 +55,9 @@ class DiffConfTemplate(MetaCRUD): rule=my_validator.LengthValidator(0, 128), validate_on=('create:O', 'update:O'), nullable=True), - # crud.ColumnValidator(field='updated_user', validate_on=('*:O',), nullable=True), - # crud.ColumnValidator(field='updated_time', validate_on=('*:O',), nullable=True), + crud.ColumnValidator(field='create_user', validate_on=('*:O',), nullable=True), crud.ColumnValidator(field='roles', - rule=validator.TypeValidator(list), + rule=validator.TypeValidator(dict), validate_on=('create:M', 'update:O'), orm_required=False), ] @@ -54,4 +66,3 @@ class DiffConfTemplate(MetaCRUD): class DiffConfTemplateRole(crud.ResourceBase): orm_meta = models.DiffConfTemplateRole _default_order = ['-id'] - diff --git a/artifacts-corepy/artifacts_corepy/server/wsgi_server.py b/artifacts-corepy/artifacts_corepy/server/wsgi_server.py index c754ba17..3091e7ca 100644 --- a/artifacts-corepy/artifacts_corepy/server/wsgi_server.py +++ b/artifacts-corepy/artifacts_corepy/server/wsgi_server.py @@ -26,7 +26,7 @@ def error_serializer(req, resp, exception): if 'error_code' in representation: representation['code'] = representation.pop('error_code') representation['status'] = 'ERROR' - representation['data'] = None + representation['data'] = representation.get('data') or None representation['message'] = representation.pop('description', '') resp.body = json.dumps(representation, cls=utils.ComplexEncoder) resp.content_type = 'application/json' From b8bdb3aabc09bfd1683d86b60b11dfa977eef030 Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Thu, 31 Oct 2024 21:56:18 +0800 Subject: [PATCH 005/108] feat: clean code --- artifacts-corepy/artifacts_corepy/apps/variable/controller.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/artifacts-corepy/artifacts_corepy/apps/variable/controller.py b/artifacts-corepy/artifacts_corepy/apps/variable/controller.py index ff84a5ff..29232a88 100644 --- a/artifacts-corepy/artifacts_corepy/apps/variable/controller.py +++ b/artifacts-corepy/artifacts_corepy/apps/variable/controller.py @@ -1,9 +1,6 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import -# from talos.common.controller import CollectionController, ItemController - -from artifacts_corepy.common import exceptions from artifacts_corepy.common.mixin import CollectionController, ItemController from artifacts_corepy.apps.variable import api From 35306a890c4cb67b0fd8c0606d70ca4156f30bdc Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Fri, 1 Nov 2024 16:03:25 +0800 Subject: [PATCH 006/108] feat: add _addtional_list for role filter --- .../artifacts_corepy/apps/variable/api.py | 13 +++++++++++++ artifacts-corepy/artifacts_corepy/db/resource.py | 1 + 2 files changed, 14 insertions(+) diff --git a/artifacts-corepy/artifacts_corepy/apps/variable/api.py b/artifacts-corepy/artifacts_corepy/apps/variable/api.py index e2515b5c..aad0d540 100644 --- a/artifacts-corepy/artifacts_corepy/apps/variable/api.py +++ b/artifacts-corepy/artifacts_corepy/apps/variable/api.py @@ -3,6 +3,8 @@ import logging from talos.core import config +from talos.utils.scoped_globals import GLOBALS + from artifacts_corepy.db import resource from artifacts_corepy.common import exceptions @@ -23,3 +25,14 @@ def _addtional_create(self, session, data, created): 'role': perm_role, 'permission': perm }) + + def _addtional_list(self, query, filters): + """权限控制,角色数据过滤""" + query = super()._addtional_list(query, filters) + permission_filters = {"roles.role": {'in': list(GLOBALS.request.auth_permissions)}} + query = self._apply_filters(query, self.orm_meta, permission_filters) + # print(str(query)) + # print(query.statement.compile(), query.statement.compile().params) + return query + + diff --git a/artifacts-corepy/artifacts_corepy/db/resource.py b/artifacts-corepy/artifacts_corepy/db/resource.py index e43e9149..0aa13b94 100644 --- a/artifacts-corepy/artifacts_corepy/db/resource.py +++ b/artifacts-corepy/artifacts_corepy/db/resource.py @@ -31,6 +31,7 @@ def _apply_primary_key_filter(self, query, rid): return query def _addtional_list(self, query, filters): + query = super()._addtional_list(query, filters) if hasattr(self.orm_meta, 'is_deleted'): query = query.filter(self.orm_meta.is_deleted == 0) return query From 5ccd821123676042c68aa23fb943cc06f6277789 Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Mon, 4 Nov 2024 16:55:21 +0800 Subject: [PATCH 007/108] feat: add artifacts.diff-conf-templates and auth app --- .../artifacts_corepy/apps/auth/__init__.py | 4 + .../artifacts_corepy/apps/auth/api.py | 208 ++++++++++++++++++ .../artifacts_corepy/apps/auth/controller.py | 131 +++++++++++ .../artifacts_corepy/apps/auth/route.py | 25 +++ .../apps/variable/controller.py | 4 +- .../artifacts_corepy/db/models.py | 109 ++++++++- artifacts-corepy/etc/artifacts_corepy.conf | 12 +- 7 files changed, 487 insertions(+), 6 deletions(-) create mode 100644 artifacts-corepy/artifacts_corepy/apps/auth/__init__.py create mode 100644 artifacts-corepy/artifacts_corepy/apps/auth/api.py create mode 100644 artifacts-corepy/artifacts_corepy/apps/auth/controller.py create mode 100644 artifacts-corepy/artifacts_corepy/apps/auth/route.py diff --git a/artifacts-corepy/artifacts_corepy/apps/auth/__init__.py b/artifacts-corepy/artifacts_corepy/apps/auth/__init__.py new file mode 100644 index 00000000..8e8d92d4 --- /dev/null +++ b/artifacts-corepy/artifacts_corepy/apps/auth/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import + +from artifacts_corepy.apps.auth import route diff --git a/artifacts-corepy/artifacts_corepy/apps/auth/api.py b/artifacts-corepy/artifacts_corepy/apps/auth/api.py new file mode 100644 index 00000000..9048cbd9 --- /dev/null +++ b/artifacts-corepy/artifacts_corepy/apps/auth/api.py @@ -0,0 +1,208 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import + +import logging +import time + +import jwt +from talos.core import config, utils +from talos.core.i18n import _ +from talos.core import exceptions as core_ex + +from artifacts_corepy.common import exceptions +from artifacts_corepy.common import utils as terminal_utils +from artifacts_corepy.db import resource as db_resource + +CONF = config.CONF +LOG = logging.getLogger(__name__) + + +class SysUser(db_resource.SysUser): + def generate_tokens(self, rid): + roles = self.get_roles(rid) + tokens = [] + current_time = int(time.time() * 1000) + access_token_iat = int(current_time / 1000) + access_token_exp = access_token_iat + CONF.access_token_exipres + refresh_token_exp = access_token_iat + CONF.refresh_token_exipres + decoded_secret = terminal_utils.b64decode_key(CONF.jwt_signing_key) + tokens.append({ + "expiration": + str(current_time + CONF.access_token_exipres * 1000), + "token": + jwt.encode( + { + "sub": rid, + "iat": access_token_iat, + "type": "accessToken", + "clientType": "USER", + "exp": access_token_exp, + "authority": "[" + ','.join([r['id'] for r in roles]) + "]" + }, + decoded_secret, + "HS512", + ).decode(), + "tokenType": + "accessToken" + }) + tokens.append({ + "expiration": + str(current_time + CONF.refresh_token_exipres * 1000), + "token": + jwt.encode( + { + "sub": rid, + "iat": access_token_iat, + "type": "refreshToken", + "clientType": "USER", + "exp": refresh_token_exp + }, + decoded_secret, + "HS512", + ).decode(), + "tokenType": + "refreshToken" + }) + return tokens + + def login(self, username, password): + with self.get_session(): + if self.check_password(username, password): + return self.generate_tokens(username) + else: + raise core_ex.LoginError() + + def refresh(self, token): + with self.get_session(): + try: + decoded_secret = terminal_utils.b64decode_key(CONF.jwt_signing_key) + info = jwt.decode(token, key=decoded_secret, verify=True) + if info['type'] != 'refreshToken': + raise core_ex.AuthError() + return self.generate_tokens(info['sub']) + except jwt.exceptions.ExpiredSignatureError: + raise core_ex.AuthError() + except jwt.exceptions.DecodeError: + raise core_ex.AuthError() + + def get_menus(self, rid): + menus = [] + exists = {} + roles = self.get_roles(rid) + for role in roles: + for menu in role['menus']: + if menu['is_active'] == 'yes' and menu['id'] not in exists: + menus.append(menu) + exists[menu['id']] = True + return menus + + def get_roles(self, rid): + ref = self.get(rid) + if ref: + return ref['roles'] + return [] + + def create(self, resource, validate=True, detail=True): + resource['salt'] = utils.generate_salt(16) + password = utils.generate_salt(16) + resource['password'] = utils.encrypt_password(password, resource['salt']) + ref = super().create(resource, validate=validate, detail=detail) + ref['password'] = password + return ref + + def reset_password(self, rid, password=None): + resource = {} + resource['salt'] = utils.generate_salt(16) + password = password or utils.generate_salt(16) + resource['password'] = utils.encrypt_password(password, resource['salt']) + before_update, after_update = self.update(rid, resource, validate=False) + if after_update: + after_update['password'] = password + return after_update + + def check_password(self, rid, password): + refs = self.list_internal({'id': rid}) + if refs: + return utils.check_password(refs[0]['password'], password, refs[0]['salt']) + return False + + def update_password(self, rid, password, origin_password): + if not password: + raise exceptions.PluginError(message=_('unabled to set empty password')) + if self.check_password(rid, origin_password): + resource = {} + resource['salt'] = utils.generate_salt(16) + password = password or utils.generate_salt(16) + resource['password'] = utils.encrypt_password(password, resource['salt']) + before_update, after_update = self.update(rid, resource, validate=False) + return after_update + else: + raise exceptions.PluginError(message=_('faild to set new password: incorrect origin password')) + + def delete(self, rid, filters=None, detail=True): + refs = self.list({'id': rid}) + if refs and refs[0]['is_system'] == 'yes': + raise exceptions.PluginError(message=_('unable to delete system user')) + with self.transaction() as session: + db_resource.SysRoleUser(transaction=session).delete_all({'user_id': rid}) + return super().delete(rid, filters=filters, detail=detail) + + +class SysRole(db_resource.SysRole): + def get_users(self, rid): + ref = self.get(rid) + if ref: + return ref['users'] + return [] + + def _update_intersect_refs(self, rid, self_field, ref_field, resource_type, refs, session): + old_refs = [result[ref_field] for result in resource_type(session=session).list(filters={self_field: rid})] + create_refs = list(set(refs) - set(old_refs)) + create_refs.sort(key=refs.index) + delete_refs = set(old_refs) - set(refs) + if delete_refs: + resource_type(transaction=session).delete_all(filters={ + self_field: rid, + ref_field: { + 'in': list(delete_refs) + } + }) + for ref in create_refs: + new_ref = {} + new_ref[self_field] = rid + new_ref[ref_field] = ref + resource_type(transaction=session).create(new_ref) + + def set_users(self, rid, users): + with self.transaction() as session: + self._update_intersect_refs(rid, 'role_id', 'user_id', db_resource.SysRoleUser, users, session) + return self.get_users(rid) + + def get_menus(self, rid, is_active=True): + ref = self.get(rid) + if ref: + if is_active: + return [menu for menu in ref['menus'] if menu['is_active'] == 'yes'] + else: + return ref['menus'] + return [] + + def set_menus(self, rid, menus): + with self.transaction() as session: + self._update_intersect_refs(rid, 'role_id', 'menu_id', db_resource.SysRoleMenu, menus, session) + return self.get_menus(rid, is_active=False) + + def delete(self, rid, filters=None, detail=True): + refs = self.list({'id': rid}) + if refs and refs[0]['is_system'] == 'yes': + raise exceptions.PluginError(message=_('unable to delete system role')) + with self.transaction() as session: + bindings = db_resource.SysRoleUser(transaction=session).list({'role_id': rid}) + if len(bindings) > 0: + users = ','.join([bind['user_id'] for bind in bindings]) + raise exceptions.PluginError(message=_('role binds with %(users)s') % {'users': users}) + return super().delete(rid, filters=filters, detail=detail) + + +class SysMenu(db_resource.SysMenu): + pass diff --git a/artifacts-corepy/artifacts_corepy/apps/auth/controller.py b/artifacts-corepy/artifacts_corepy/apps/auth/controller.py new file mode 100644 index 00000000..ddcb2226 --- /dev/null +++ b/artifacts-corepy/artifacts_corepy/apps/auth/controller.py @@ -0,0 +1,131 @@ +# coding=utf-8 + +from __future__ import absolute_import + +from talos.core.i18n import _ +from talos.utils.scoped_globals import GLOBALS + +from artifacts_corepy.common import exceptions +from artifacts_corepy.common.mixin import Controller, CollectionController as Collection, ItemController as Item +from artifacts_corepy.apps.auth import api as auth_api + + +class Token(Controller): + name = 'auth.token' + resource = auth_api.SysUser + allow_methods = ('POST', ) + + def create(self, req, data, **kwargs): + return self.make_resource(req).login(data.get('username', ''), data.get('password', '')) + + +class TokenRefresh(Controller): + name = 'auth.token' + resource = auth_api.SysUser + allow_methods = ('GET', ) + + def get(self, req, **kwargs): + return self.make_resource(req).refresh(GLOBALS.request.auth_token) + + +class UserMenus(Controller): + name = 'auth.user-menus' + resource = auth_api.SysUser + allow_methods = ('GET', ) + + def get(self, req, **kwargs): + return self.make_resource(req).get_menus(GLOBALS.request.auth_user) + + +class UserPassword(Controller): + name = 'auth.user-password' + resource = auth_api.SysUser + allow_methods = ('POST', ) + + def create(self, req, data, **kwargs): + return self.make_resource(req).update_password(GLOBALS.request.auth_user, data.get('newPassword', ''), + data.get('oldPassword', '')) + + +class User(Collection): + name = 'auth.users' + resource = auth_api.SysUser + + +class UserItem(Item): + name = 'auth.users' + resource = auth_api.SysUser + + +class UserItemMenu(Item): + name = 'auth.users' + resource = auth_api.SysUser + allow_methods = ('GET', ) + + def get(self, req, rid): + return self.make_resource(req).get_menus(rid) + + +class UserItemResetPassword(Controller): + name = 'auth.users.password' + resource = auth_api.SysUser + allow_methods = ('POST', ) + + def create(self, req, data, rid): + data = data or {} + return self.make_resource(req).reset_password(rid, password=data.get('password', None)) + + +class UserItemRole(Item): + name = 'auth.users' + resource = auth_api.SysUser + allow_methods = ('GET', ) + + def get(self, req, rid): + return self.make_resource(req).get_roles(rid) + + +class Role(Collection): + name = 'auth.roles' + resource = auth_api.SysRole + + +class RoleItem(Item): + name = 'auth.roles' + resource = auth_api.SysRole + + +class RoleItemMenu(Controller): + name = 'auth.roles' + resource = auth_api.SysRole + allow_methods = ('GET', 'POST') + + def get(self, req, rid): + return self.make_resource(req).get_menus(rid) + + def create(self, req, data, rid): + return self.make_resource(req).set_menus(rid, data) + + +class RoleItemUser(Controller): + name = 'auth.roles' + resource = auth_api.SysRole + allow_methods = ('GET', 'POST') + + def get(self, req, rid): + return self.make_resource(req).get_users(rid) + + def create(self, req, data, rid): + return self.make_resource(req).set_users(rid, data) + + +class Menu(Collection): + name = 'auth.menus' + resource = auth_api.SysMenu + allow_methods = ('GET', ) + + +class MenuItem(Item): + name = 'auth.menus' + resource = auth_api.SysMenu + allow_methods = ('GET', ) diff --git a/artifacts-corepy/artifacts_corepy/apps/auth/route.py b/artifacts-corepy/artifacts_corepy/apps/auth/route.py new file mode 100644 index 00000000..80743e44 --- /dev/null +++ b/artifacts-corepy/artifacts_corepy/apps/auth/route.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import + +from artifacts_corepy.apps.auth import controller + + +def add_routes(api): + # 不需要登陆 + api.add_route('/artifacts/v1/login', controller.Token()) + api.add_route('/artifacts/v1/refresh-token', controller.TokenRefresh()) + # 登陆但不需要权限校验 + api.add_route('/artifacts/v1/user-menus', controller.UserMenus()) + api.add_route('/artifacts/v1/user-password', controller.UserPassword()) + # 登陆且经过权限校验 + api.add_route('/artifacts/v1/users', controller.User()) + api.add_route('/artifacts/v1/users/{rid}', controller.UserItem()) + api.add_route('/artifacts/v1/users/{rid}/reset-password', controller.UserItemResetPassword()) + api.add_route('/artifacts/v1/users/{rid}/menus', controller.UserItemMenu()) + api.add_route('/artifacts/v1/users/{rid}/roles', controller.UserItemRole()) + api.add_route('/artifacts/v1/roles', controller.Role()) + api.add_route('/artifacts/v1/roles/{rid}', controller.RoleItem()) + api.add_route('/artifacts/v1/roles/{rid}/menus', controller.RoleItemMenu()) + api.add_route('/artifacts/v1/roles/{rid}/users', controller.RoleItemUser()) + api.add_route('/artifacts/v1/menus', controller.Menu()) + # api.add_route('/artifacts/v1/menus/{rid}', controller.MenuItem()) diff --git a/artifacts-corepy/artifacts_corepy/apps/variable/controller.py b/artifacts-corepy/artifacts_corepy/apps/variable/controller.py index 29232a88..80608901 100644 --- a/artifacts-corepy/artifacts_corepy/apps/variable/controller.py +++ b/artifacts-corepy/artifacts_corepy/apps/variable/controller.py @@ -6,10 +6,10 @@ class CollectionDiffConfTemplates(CollectionController): - name = 'artifacts.diff_conf_templates' + name = 'artifacts.diff-conf-templates' resource = api.DiffConfTemplate class ItemDiffConfTemplate(ItemController): - name = 'artifacts.diff_conf_template' + name = 'artifacts.diff-conf-templates' resource = api.DiffConfTemplate diff --git a/artifacts-corepy/artifacts_corepy/db/models.py b/artifacts-corepy/artifacts_corepy/db/models.py index c91559c9..3089e63d 100644 --- a/artifacts-corepy/artifacts_corepy/db/models.py +++ b/artifacts-corepy/artifacts_corepy/db/models.py @@ -2,8 +2,10 @@ from __future__ import absolute_import from talos.db.dictbase import DictBase -from sqlalchemy import Column, DateTime, ForeignKey, String, text, Text, func, create_engine, UniqueConstraint -from sqlalchemy.dialects.mysql import BIGINT, TINYINT +from sqlalchemy import ( + Column, DateTime, ForeignKey, String, text, Text, func, create_engine, UniqueConstraint, +) +from sqlalchemy.dialects.mysql import BIGINT, TINYINT, INTEGER from sqlalchemy.orm import relationship from sqlalchemy.ext.declarative import declarative_base @@ -62,6 +64,109 @@ def __repr__(self): return "{0}:{1}".format(self.role, self.diff_conf_template_id) +class SysMenu(Base, DictBase): + __tablename__ = 'sys_menu' + attributes = [ + 'id', 'display_name', 'url', 'seq_no', 'parent', 'is_active', 'created_by', 'created_time', 'updated_by', + 'updated_time' + ] + summary_attributes = ['id', 'display_name', 'url', 'seq_no', 'parent', 'is_active'] + + id = Column(String(36), primary_key=True, comment='主键') + display_name = Column(String(64), comment='显示名') + url = Column(String(255), comment='访问路径') + seq_no = Column(INTEGER(11), server_default=text("'0'"), comment='排序号') + parent = Column(String(36), comment='父菜单') + is_active = Column(String(8), server_default=text("'yes'"), comment='状态') + created_by = Column(String(36)) + created_time = Column(DateTime) + updated_by = Column(String(36)) + updated_time = Column(DateTime) + + roles = relationship("SysRole", secondary="sys_role_menu", back_populates="menus", uselist=True, viewonly=True) + + +class SysRole(Base, DictBase): + __tablename__ = 'sys_role' + attributes = [ + 'id', 'description', 'role_type', 'is_system', 'created_by', 'created_time', 'updated_by', 'updated_time', + 'menus' + ] + summary_attributes = ['id', 'description', 'role_type', 'is_system'] + detail_attributes = [ + 'id', 'description', 'role_type', 'is_system', 'created_by', 'created_time', 'updated_by', 'updated_time', + 'users', 'menus' + ] + + id = Column(String(36), primary_key=True, comment='主键') + description = Column(String(255), comment='描述') + role_type = Column(String(32), comment='角色类型') + is_system = Column(String(8), server_default=text("'no'"), comment='是否系统角色') + created_by = Column(String(36)) + created_time = Column(DateTime) + updated_by = Column(String(36)) + updated_time = Column(DateTime) + + users = relationship("SysUser", secondary="sys_role_user", back_populates="roles", uselist=True, viewonly=True) + menus = relationship("SysMenu", secondary="sys_role_menu", back_populates="roles", uselist=True, viewonly=True) + + +class SysUser(Base, DictBase): + __tablename__ = 'sys_user' + attributes = [ + 'id', 'display_name', 'password', 'salt', 'description', 'is_system', 'created_by', 'created_time', + 'updated_by', 'updated_time' + ] + summary_attributes = ['id', 'display_name', 'description', 'is_system'] + detail_attributes = [ + 'id', 'display_name', 'password', 'salt', 'description', 'is_system', 'created_by', 'created_time', + 'updated_by', 'updated_time', 'roles' + ] + + id = Column(String(36), primary_key=True, comment='主键') + display_name = Column(String(64), comment='显示名') + password = Column(String(128), comment='加密密钥') + salt = Column(String(36), comment='加密盐') + description = Column(String(255), comment='描述') + is_system = Column(String(8), server_default=text("'no'"), comment='是否系统用户') + created_by = Column(String(36)) + created_time = Column(DateTime) + updated_by = Column(String(36)) + updated_time = Column(DateTime) + + roles = relationship("SysRole", secondary="sys_role_user", back_populates="users", uselist=True, viewonly=True) + + +class SysRoleMenu(Base, DictBase): + __tablename__ = 'sys_role_menu' + + id = Column(BIGINT(20), primary_key=True) + role_id = Column(ForeignKey('sys_role.id'), index=True, comment='角色id') + menu_id = Column(ForeignKey('sys_menu.id'), index=True, comment='菜单id') + created_by = Column(String(36)) + created_time = Column(DateTime) + updated_by = Column(String(36)) + updated_time = Column(DateTime) + + menu = relationship('SysMenu') + role = relationship('SysRole') + + +class SysRoleUser(Base, DictBase): + __tablename__ = 'sys_role_user' + + id = Column(BIGINT(20), primary_key=True) + role_id = Column(ForeignKey('sys_role.id'), index=True, comment='角色id') + user_id = Column(ForeignKey('sys_user.id'), index=True, comment='用户id') + created_by = Column(String(36)) + created_time = Column(DateTime) + updated_by = Column(String(36)) + updated_time = Column(DateTime) + + role = relationship('SysRole') + user = relationship('SysUser') + + if __name__ == '__main__': engine = create_engine("mysql+pymysql://root:miya.12345@127.0.0.1:3306/artifacts", encoding="utf-8") Base.metadata.create_all(engine) diff --git a/artifacts-corepy/etc/artifacts_corepy.conf b/artifacts-corepy/etc/artifacts_corepy.conf index 57e798c8..f227498d 100644 --- a/artifacts-corepy/etc/artifacts_corepy.conf +++ b/artifacts-corepy/etc/artifacts_corepy.conf @@ -81,7 +81,10 @@ "max_overflow": 5 }, "application": { - "names": ["artifacts_corepy.apps.package", "artifacts_corepy.apps.plugin"] + "names": [ + "artifacts_corepy.apps.package", "artifacts_corepy.apps.plugin", + "artifacts_corepy.apps.variable", "artifacts_corepy.apps.auth" + ] }, "rate_limit": { "enabled": false, @@ -180,8 +183,13 @@ "artifacts.pushcomposepackage": ["SUB_SYSTEM", "IMPLEMENTATION_ARTIFACT_MANAGEMENT"], "artifacts.systemconfig": ["SUB_SYSTEM", "IMPLEMENTATION_ARTIFACT_MANAGEMENT"], "artifacts.unit-design.nexus.path": ["SUB_SYSTEM", "IMPLEMENTATION_ARTIFACT_MANAGEMENT"], - "artifacts.process.defs": ["SUB_SYSTEM", "IMPLEMENTATION_ARTIFACT_MANAGEMENT"] + "artifacts.process.defs": ["SUB_SYSTEM", "IMPLEMENTATION_ARTIFACT_MANAGEMENT"], + "artifacts.diff-conf-templates": ["SUB_SYSTEM", "IMPLEMENTATION_ARTIFACT_MANAGEMENT"] }, + "permission_passthrough": ["auth.token", "auth.user-menus", "auth.user-password"], + "menu_permissions": { + "system_authorization": ["auth.users", "auth.users.password", "auth.roles", "auth.menus"] + }, "plugin_permissions": [ "artifacts.plugins.fromimage" ] From 5c31d04af1531a997ce19c2b9bfa5f7ab79bacb5 Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Mon, 4 Nov 2024 16:57:59 +0800 Subject: [PATCH 008/108] feat: add artifacts.diff-conf-templates and auth app --- .../artifacts_corepy/db/resource.py | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/artifacts-corepy/artifacts_corepy/db/resource.py b/artifacts-corepy/artifacts_corepy/db/resource.py index 0aa13b94..cc841bf1 100644 --- a/artifacts-corepy/artifacts_corepy/db/resource.py +++ b/artifacts-corepy/artifacts_corepy/db/resource.py @@ -67,3 +67,99 @@ class DiffConfTemplate(MetaCRUD): class DiffConfTemplateRole(crud.ResourceBase): orm_meta = models.DiffConfTemplateRole _default_order = ['-id'] + + +class SysRoleMenu(MetaCRUD): + orm_meta = models.SysRoleMenu + _default_order = ['-created_time'] + + +class SysRoleUser(MetaCRUD): + orm_meta = models.SysRoleUser + _default_order = ['-created_time'] + + +class SysMenu(MetaCRUD): + orm_meta = models.SysMenu + _default_order = ['seq_no'] + _id_prefix = 'menu-' + + _validate = [ + crud.ColumnValidator(field='id', validate_on=('create:M', 'update:O')), + crud.ColumnValidator(field='display_name', + rule=my_validator.LengthValidator(1, 64), + validate_on=('create:M', 'update:O')), + crud.ColumnValidator(field='url', + rule=my_validator.LengthValidator(0, 255), + validate_on=('create:O', 'update:O'), + nullable=True), + crud.ColumnValidator(field='seq_no', + rule=my_validator.validator.NumberValidator(int, range_min=0, range_max=65535), + validate_on=('create:O', 'update:O')), + crud.ColumnValidator(field='parent', + rule=my_validator.BackRefValidator(SysRoleMenu), + validate_on=('create:O', 'update:O'), + nullable=True), + crud.ColumnValidator(field='is_active', + rule=my_validator.validator.InValidator(['yes', 'no']), + validate_on=('create:O', 'update:O')), + crud.ColumnValidator(field='created_by', validate_on=('*:O',), nullable=True), + crud.ColumnValidator(field='created_time', validate_on=('*:O',), nullable=True), + crud.ColumnValidator(field='updated_by', validate_on=('*:O',), nullable=True), + crud.ColumnValidator(field='updated_time', validate_on=('*:O',), nullable=True), + ] + + +class SysRole(MetaCRUD): + orm_meta = models.SysRole + _default_order = ['-created_time'] + _id_prefix = 'role-' + _detail_relationship_as_summary = True + + _validate = [ + crud.ColumnValidator(field='id', validate_on=('create:M', 'update:O')), + crud.ColumnValidator(field='description', + rule=my_validator.LengthValidator(1, 255), + validate_on=('create:M', 'update:O')), + crud.ColumnValidator(field='role_type', + rule=my_validator.LengthValidator(0, 32), + validate_on=('create:O', 'update:O'), + nullable=True), + crud.ColumnValidator(field='is_system', + rule=my_validator.validator.InValidator(['yes', 'no']), + validate_on=('create:O', 'update:O')), + crud.ColumnValidator(field='created_by', validate_on=('*:O',), nullable=True), + crud.ColumnValidator(field='created_time', validate_on=('*:O',), nullable=True), + crud.ColumnValidator(field='updated_by', validate_on=('*:O',), nullable=True), + crud.ColumnValidator(field='updated_time', validate_on=('*:O',), nullable=True), + ] + + +class SysUser(MetaCRUD): + orm_meta = models.SysUser + _default_order = ['-created_time'] + _remove_fields = ['password', 'salt'] + _id_prefix = 'user-' + + _validate = [ + crud.ColumnValidator(field='id', validate_on=('create:M', 'update:O')), + crud.ColumnValidator(field='display_name', + rule=my_validator.LengthValidator(1, 64), + validate_on=('create:M', 'update:O')), + crud.ColumnValidator(field='password', rule=my_validator.LengthValidator(1, 128), validate_on=('create:M',)), + crud.ColumnValidator(field='salt', rule=my_validator.LengthValidator(1, 36), validate_on=('create:M',)), + crud.ColumnValidator(field='description', + rule=my_validator.LengthValidator(0, 255), + validate_on=('create:O', 'update:O'), + nullable=True), + crud.ColumnValidator(field='is_system', + rule=my_validator.validator.InValidator(['yes', 'no']), + validate_on=('create:O', 'update:O')), + crud.ColumnValidator(field='created_by', validate_on=('*:O',), nullable=True), + crud.ColumnValidator(field='created_time', validate_on=('*:O',), nullable=True), + crud.ColumnValidator(field='updated_by', validate_on=('*:O',), nullable=True), + crud.ColumnValidator(field='updated_time', validate_on=('*:O',), nullable=True), + ] + + def list_internal(self, filters=None, orders=None, offset=None, limit=None, hooks=None): + return super(MetaCRUD, self).list(filters=filters, orders=orders, offset=offset, limit=limit, hooks=hooks) From 649518b980368553b8a5413c763bee1ee40e04bb Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Mon, 4 Nov 2024 17:22:05 +0800 Subject: [PATCH 009/108] feat: add proxy mode to platform --- .../artifacts_corepy/apps/auth/apiv2.py | 31 +++++++++++++++++++ .../artifacts_corepy/apps/auth/controller.py | 25 ++++++++------- 2 files changed, 45 insertions(+), 11 deletions(-) create mode 100644 artifacts-corepy/artifacts_corepy/apps/auth/apiv2.py diff --git a/artifacts-corepy/artifacts_corepy/apps/auth/apiv2.py b/artifacts-corepy/artifacts_corepy/apps/auth/apiv2.py new file mode 100644 index 00000000..1c8d839f --- /dev/null +++ b/artifacts-corepy/artifacts_corepy/apps/auth/apiv2.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import + +import logging +from talos.core import config +from talos.utils import scoped_globals + +from artifacts_corepy.common import wecube + +LOG = logging.getLogger(__name__) +CONF = config.CONF + + +class WeCubeResource(object): + def __init__(self, server=None, token=None): + self.server = server or CONF.wecube.server + self.token = token or scoped_globals.GLOBALS.request.auth_token + + @property + def platform(self): + return wecube.WeCubeClient(self.server, self.token) + + def list(self, params): + pass + + +class SysUser(WeCubeResource): + def list(self, params): + params['filters'] = [] + params['paging'] = False + return self.platform.get(self.server + '/platform/v1/roles/retrieve', params) diff --git a/artifacts-corepy/artifacts_corepy/apps/auth/controller.py b/artifacts-corepy/artifacts_corepy/apps/auth/controller.py index ddcb2226..6434005a 100644 --- a/artifacts-corepy/artifacts_corepy/apps/auth/controller.py +++ b/artifacts-corepy/artifacts_corepy/apps/auth/controller.py @@ -1,5 +1,4 @@ -# coding=utf-8 - +# -*- coding: utf-8 -*- from __future__ import absolute_import from talos.core.i18n import _ @@ -10,10 +9,14 @@ from artifacts_corepy.apps.auth import api as auth_api +# proxy mode to platform +# from artifacts_corepy.apps.auth import apiv2 as auth_api + + class Token(Controller): name = 'auth.token' resource = auth_api.SysUser - allow_methods = ('POST', ) + allow_methods = ('POST',) def create(self, req, data, **kwargs): return self.make_resource(req).login(data.get('username', ''), data.get('password', '')) @@ -22,7 +25,7 @@ def create(self, req, data, **kwargs): class TokenRefresh(Controller): name = 'auth.token' resource = auth_api.SysUser - allow_methods = ('GET', ) + allow_methods = ('GET',) def get(self, req, **kwargs): return self.make_resource(req).refresh(GLOBALS.request.auth_token) @@ -31,7 +34,7 @@ def get(self, req, **kwargs): class UserMenus(Controller): name = 'auth.user-menus' resource = auth_api.SysUser - allow_methods = ('GET', ) + allow_methods = ('GET',) def get(self, req, **kwargs): return self.make_resource(req).get_menus(GLOBALS.request.auth_user) @@ -40,7 +43,7 @@ def get(self, req, **kwargs): class UserPassword(Controller): name = 'auth.user-password' resource = auth_api.SysUser - allow_methods = ('POST', ) + allow_methods = ('POST',) def create(self, req, data, **kwargs): return self.make_resource(req).update_password(GLOBALS.request.auth_user, data.get('newPassword', ''), @@ -60,7 +63,7 @@ class UserItem(Item): class UserItemMenu(Item): name = 'auth.users' resource = auth_api.SysUser - allow_methods = ('GET', ) + allow_methods = ('GET',) def get(self, req, rid): return self.make_resource(req).get_menus(rid) @@ -69,7 +72,7 @@ def get(self, req, rid): class UserItemResetPassword(Controller): name = 'auth.users.password' resource = auth_api.SysUser - allow_methods = ('POST', ) + allow_methods = ('POST',) def create(self, req, data, rid): data = data or {} @@ -79,7 +82,7 @@ def create(self, req, data, rid): class UserItemRole(Item): name = 'auth.users' resource = auth_api.SysUser - allow_methods = ('GET', ) + allow_methods = ('GET',) def get(self, req, rid): return self.make_resource(req).get_roles(rid) @@ -122,10 +125,10 @@ def create(self, req, data, rid): class Menu(Collection): name = 'auth.menus' resource = auth_api.SysMenu - allow_methods = ('GET', ) + allow_methods = ('GET',) class MenuItem(Item): name = 'auth.menus' resource = auth_api.SysMenu - allow_methods = ('GET', ) + allow_methods = ('GET',) From 11c61a708c70b9dc71b1f5996086520b2f50311e Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Wed, 6 Nov 2024 20:30:58 +0800 Subject: [PATCH 010/108] feat: add /artifacts/platform/v1/users/roles /artifacts/platform/v1/roles --- .../artifacts_corepy/apps/auth/apiv2.py | 9 ++-- .../artifacts_corepy/apps/auth/controller.py | 14 +++++- .../artifacts_corepy/apps/auth/route.py | 2 + .../artifacts_corepy/apps/package/apiv2.py | 1 + .../artifacts_corepy/apps/package/route.py | 43 ++++++++++++++++++- 5 files changed, 62 insertions(+), 7 deletions(-) diff --git a/artifacts-corepy/artifacts_corepy/apps/auth/apiv2.py b/artifacts-corepy/artifacts_corepy/apps/auth/apiv2.py index 1c8d839f..2f9daf2e 100644 --- a/artifacts-corepy/artifacts_corepy/apps/auth/apiv2.py +++ b/artifacts-corepy/artifacts_corepy/apps/auth/apiv2.py @@ -24,8 +24,11 @@ def list(self, params): pass +class SysRole(WeCubeResource): + def list(self, params): + return self.platform.get(self.server + '/platform/v1/roles/retrieve') + + class SysUser(WeCubeResource): def list(self, params): - params['filters'] = [] - params['paging'] = False - return self.platform.get(self.server + '/platform/v1/roles/retrieve', params) + return self.platform.get(self.server + '/platform/v1/users/roles') diff --git a/artifacts-corepy/artifacts_corepy/apps/auth/controller.py b/artifacts-corepy/artifacts_corepy/apps/auth/controller.py index 6434005a..2189d494 100644 --- a/artifacts-corepy/artifacts_corepy/apps/auth/controller.py +++ b/artifacts-corepy/artifacts_corepy/apps/auth/controller.py @@ -8,9 +8,19 @@ from artifacts_corepy.common.mixin import Controller, CollectionController as Collection, ItemController as Item from artifacts_corepy.apps.auth import api as auth_api - # proxy mode to platform -# from artifacts_corepy.apps.auth import apiv2 as auth_api +from artifacts_corepy.apps.auth import apiv2 as proxy_api + + +class ProxyUserRole(Controller): + name = 'auth.proxy-users' + resource = proxy_api.SysUser + allow_methods = ('GET',) + + +class ProxyRole(Controller): + name = 'auth.proxy-roles' + resource = proxy_api.SysRole class Token(Controller): diff --git a/artifacts-corepy/artifacts_corepy/apps/auth/route.py b/artifacts-corepy/artifacts_corepy/apps/auth/route.py index 80743e44..ac2e1880 100644 --- a/artifacts-corepy/artifacts_corepy/apps/auth/route.py +++ b/artifacts-corepy/artifacts_corepy/apps/auth/route.py @@ -18,6 +18,8 @@ def add_routes(api): api.add_route('/artifacts/v1/users/{rid}/menus', controller.UserItemMenu()) api.add_route('/artifacts/v1/users/{rid}/roles', controller.UserItemRole()) api.add_route('/artifacts/v1/roles', controller.Role()) + # api.add_route('/artifacts/v1/users/roles', controller.ProxyUserRole()) + # api.add_route('/artifacts/v1/roles', controller.ProxyRole()) api.add_route('/artifacts/v1/roles/{rid}', controller.RoleItem()) api.add_route('/artifacts/v1/roles/{rid}/menus', controller.RoleItemMenu()) api.add_route('/artifacts/v1/roles/{rid}/users', controller.RoleItemUser()) diff --git a/artifacts-corepy/artifacts_corepy/apps/package/apiv2.py b/artifacts-corepy/artifacts_corepy/apps/package/apiv2.py index f8791e5f..ec927cee 100644 --- a/artifacts-corepy/artifacts_corepy/apps/package/apiv2.py +++ b/artifacts-corepy/artifacts_corepy/apps/package/apiv2.py @@ -2465,6 +2465,7 @@ def get(self, unit_design_id): unit_design = resp_json['data']['contents'][0] return {'artifact_path': self.get_unit_design_artifact_path(unit_design)} + class DiffConfig(WeCubeResource): def update(self, data): cmdb_client = self.get_cmdb_client() diff --git a/artifacts-corepy/artifacts_corepy/apps/package/route.py b/artifacts-corepy/artifacts_corepy/apps/package/route.py index 7fd99bbd..c5015710 100644 --- a/artifacts-corepy/artifacts_corepy/apps/package/route.py +++ b/artifacts-corepy/artifacts_corepy/apps/package/route.py @@ -24,6 +24,7 @@ def __call__(self, req, resp, repository): resp.set_header('Content-Disposition', stream.headers.get('Content-Disposition')) resp.set_header('Content-Type', stream.headers.get('Content-Type')) + class EntityAdapter(object): def __call__(self, req, resp, package_name, entity_name, action_name): server = CONF.wecube.server @@ -63,6 +64,36 @@ def __call__(self, req, resp, package_name, entity_name, action_name): }) +class RoleAdapter(object): + def __call__(self, req, resp, action_name): + server = CONF.wecube.server + token = req.auth_token + client = wecube.WeCubeClient(server, token) + if action_name == 'roles': + data = client.get('/platform/v1/roles/retrieve') + resp.json = {'code': 200, 'status': 'OK', 'data': data['data'], 'message': 'success'} + else: + raise exceptions.NotFoundError( + _('%(action_name)s for role not supported') % { + 'action_name': action_name, + }) + + +class UserAdapter(object): + def __call__(self, req, resp, action_name): + server = CONF.wecube.server + token = req.auth_token + client = wecube.WeCubeClient(server, token) + if action_name == 'roles': + data = client.get('/platform/v1/users/roles') + resp.json = {'code': 200, 'status': 'OK', 'data': data['data'], 'message': 'success'} + else: + raise exceptions.NotFoundError( + _('%(action_name)s for user not supported') % { + 'action_name': action_name, + }) + + def add_routes(api): # process api.add_route('/artifacts/process/definitions', controller.CollectionProcessDef()) @@ -85,6 +116,14 @@ def add_routes(api): EntityAdapter(), r'/artifacts/platform/v1/packages/(?P[-_A-Za-z0-9]+)/entities/(?P[-_A-Za-z0-9]+)/(?P[-_A-Za-z0-9]+)' ) + api.add_sink( + RoleAdapter(), + r'/artifacts/platform/v1/roles/(?P[-_A-Za-z0-9]+)' + ) + api.add_sink( + UserAdapter(), + r'/artifacts/platform/v1/users/(?P[-_A-Za-z0-9]+)' + ) # nexus query api.add_route('/artifacts/unit-designs/{unit_design_id}/packages/queryNexusDirectiry', controller.CollectionUnitDesignNexusPackages()) @@ -120,13 +159,13 @@ def add_routes(api): # packages exist in remote nexus but not in cmdb api.add_route('/artifacts/entities/packages/query', controller.CollectionOnlyInRemoteNexusPackages()) - + # compose package api.add_route('/artifacts/packages/{deploy_package_id}/download', controller.DownloadComposePackage()) api.add_route('/artifacts/unit-designs/{unit_design_id}/packages/{deploy_package_id}/push', controller.PushComposePackage()) - + # system config api.add_route('/artifacts/sysconfig', controller.SystemConfig()) From fa503741b0f0272a47515ea5bbb22872a59b2698 Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Wed, 6 Nov 2024 20:46:17 +0800 Subject: [PATCH 011/108] feat: add /artifacts/platform/v1/users/roles /artifacts/platform/v1/roles/retrieve --- artifacts-corepy/artifacts_corepy/apps/package/route.py | 2 +- artifacts-corepy/artifacts_corepy/apps/variable/api.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/artifacts-corepy/artifacts_corepy/apps/package/route.py b/artifacts-corepy/artifacts_corepy/apps/package/route.py index c5015710..9e205349 100644 --- a/artifacts-corepy/artifacts_corepy/apps/package/route.py +++ b/artifacts-corepy/artifacts_corepy/apps/package/route.py @@ -69,7 +69,7 @@ def __call__(self, req, resp, action_name): server = CONF.wecube.server token = req.auth_token client = wecube.WeCubeClient(server, token) - if action_name == 'roles': + if action_name == 'retrieve': data = client.get('/platform/v1/roles/retrieve') resp.json = {'code': 200, 'status': 'OK', 'data': data['data'], 'message': 'success'} else: diff --git a/artifacts-corepy/artifacts_corepy/apps/variable/api.py b/artifacts-corepy/artifacts_corepy/apps/variable/api.py index aad0d540..ebc86c2c 100644 --- a/artifacts-corepy/artifacts_corepy/apps/variable/api.py +++ b/artifacts-corepy/artifacts_corepy/apps/variable/api.py @@ -30,9 +30,9 @@ def _addtional_list(self, query, filters): """权限控制,角色数据过滤""" query = super()._addtional_list(query, filters) permission_filters = {"roles.role": {'in': list(GLOBALS.request.auth_permissions)}} + # print(f'permission_filters: {permission_filters}') query = self._apply_filters(query, self.orm_meta, permission_filters) - # print(str(query)) - # print(query.statement.compile(), query.statement.compile().params) + # print(str(query), query.statement.compile(), query.statement.compile().params) return query From 997536a5d6833a87e72a343733784173803e63b3 Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Wed, 6 Nov 2024 20:49:43 +0800 Subject: [PATCH 012/108] feat: add /artifacts/platform/v1/users/roles /artifacts/platform/v1/roles/retrieve --- init.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/init.sql b/init.sql index 859adbdc..db0c2ab5 100644 --- a/init.sql +++ b/init.sql @@ -1,4 +1,4 @@ -#@v0.1.0.1-begin@; +#@v1.1.12.1-begin@; CREATE TABLE diff_conf_template ( id BIGINT PRIMARY KEY AUTO_INCREMENT, type VARCHAR(16) NOT NULL COMMENT '类型:应用-app,数据库-db', @@ -24,6 +24,6 @@ CREATE TABLE diff_conf_template_role ( ON DELETE CASCADE, INDEX (diff_conf_template_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -#@v0.1.0.1-end@; +#@v1.1.12.1-end@; From c3155eedf55c0395e8faf88067cec8755d5f39bb Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Wed, 6 Nov 2024 20:50:05 +0800 Subject: [PATCH 013/108] feat: add /artifacts/platform/v1/users/roles /artifacts/platform/v1/roles/retrieve --- init.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/init.sql b/init.sql index db0c2ab5..164423f7 100644 --- a/init.sql +++ b/init.sql @@ -1,4 +1,4 @@ -#@v1.1.12.1-begin@; +#@v1.1.12.2-begin@; CREATE TABLE diff_conf_template ( id BIGINT PRIMARY KEY AUTO_INCREMENT, type VARCHAR(16) NOT NULL COMMENT '类型:应用-app,数据库-db', @@ -24,6 +24,6 @@ CREATE TABLE diff_conf_template_role ( ON DELETE CASCADE, INDEX (diff_conf_template_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -#@v1.1.12.1-end@; +#@v1.1.12.2-end@; From cf2dbf09580d2b73e333fc43c3f1fd04804d110e Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Wed, 6 Nov 2024 20:50:17 +0800 Subject: [PATCH 014/108] feat: add /artifacts/platform/v1/users/roles /artifacts/platform/v1/roles/retrieve --- init.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/init.sql b/init.sql index 164423f7..2cb49da5 100644 --- a/init.sql +++ b/init.sql @@ -1,4 +1,4 @@ -#@v1.1.12.2-begin@; +#@v1.1.13.2-begin@; CREATE TABLE diff_conf_template ( id BIGINT PRIMARY KEY AUTO_INCREMENT, type VARCHAR(16) NOT NULL COMMENT '类型:应用-app,数据库-db', @@ -24,6 +24,6 @@ CREATE TABLE diff_conf_template_role ( ON DELETE CASCADE, INDEX (diff_conf_template_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -#@v1.1.12.2-end@; +#@v1.1.13.2-end@; From cec5103f36c02c4e4e4381e3dac53b493b20f9c7 Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Thu, 7 Nov 2024 16:16:16 +0800 Subject: [PATCH 015/108] feat: add db config --- artifacts-corepy/etc/artifacts_corepy.conf | 4 ++-- artifacts-corepy/requirements.txt | 1 + init.sql | 8 ++++---- register.xml | 3 ++- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/artifacts-corepy/etc/artifacts_corepy.conf b/artifacts-corepy/etc/artifacts_corepy.conf index f227498d..437e6a32 100644 --- a/artifacts-corepy/etc/artifacts_corepy.conf +++ b/artifacts-corepy/etc/artifacts_corepy.conf @@ -74,11 +74,11 @@ ] }, "db": { - "connection": "", + "connection": "mysql+pymysql://${db_username}:${db_password}@${db_hostip}:${db_hostport}/${db_schema}", "pool_size": 3, "pool_recycle": 3600, "pool_timeout": 5, - "max_overflow": 5 + "max_overflow": 60 }, "application": { "names": [ diff --git a/artifacts-corepy/requirements.txt b/artifacts-corepy/requirements.txt index d6df43f0..7ae0e21f 100644 --- a/artifacts-corepy/requirements.txt +++ b/artifacts-corepy/requirements.txt @@ -14,3 +14,4 @@ apscheduler==3.10.4 pytz==2023.3.post1 # for platform login encryption, apt install swig M2Crypto==0.40.1 +pymysql \ No newline at end of file diff --git a/init.sql b/init.sql index 2cb49da5..94b02fd7 100644 --- a/init.sql +++ b/init.sql @@ -1,7 +1,7 @@ -#@v1.1.13.2-begin@; +#@v1.1.13.3-begin@; CREATE TABLE diff_conf_template ( id BIGINT PRIMARY KEY AUTO_INCREMENT, - type VARCHAR(16) NOT NULL COMMENT '类型:应用-app,数据库-db', + type VARCHAR(16) NOT NULL COMMENT '类型:app/db/xx', code VARCHAR(36) NOT NULL COMMENT '编码', value TEXT NULL COMMENT '文本值', description VARCHAR(128) DEFAULT '' COMMENT '描述', @@ -15,7 +15,7 @@ CREATE TABLE diff_conf_template ( CREATE TABLE diff_conf_template_role ( id BIGINT PRIMARY KEY AUTO_INCREMENT, - permission VARCHAR(16) NOT NULL COMMENT '权限:MGMT,USE', + permission VARCHAR(16) NOT NULL COMMENT '权限:MGMT/USE/xx', role VARCHAR(64) NOT NULL COMMENT '角色', diff_conf_template_id BIGINT, CONSTRAINT fk_diff_conf_template @@ -24,6 +24,6 @@ CREATE TABLE diff_conf_template_role ( ON DELETE CASCADE, INDEX (diff_conf_template_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -#@v1.1.13.2-end@; +#@v1.1.13.3-end@; diff --git a/register.xml b/register.xml index bad92f6a..4f8bed37 100644 --- a/register.xml +++ b/register.xml @@ -75,7 +75,8 @@ - + + From 8d597ca49bd0ee66d275e55a13b33eb81a5646cc Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Thu, 7 Nov 2024 16:44:05 +0800 Subject: [PATCH 016/108] feat: add db config --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 95995776..f9c6f07b 100644 --- a/Makefile +++ b/Makefile @@ -27,6 +27,7 @@ package_py: image_py mkdir -p package cd package && docker save $(project_name):$(version) -o image.tar cd package && cp ../register.xml . + cd package && cp ../init.sql ./init.sql cd package && sed -i "s~{{REPOSITORY}}~$(project_name)~g" register.xml cd package && sed -i "s~{{VERSION}}~$(version)~g" register.xml cd artifacts-ui/dist && zip -r ui.zip . From 1050f6fd7c78d85c936535b838b8d3be901b7728 Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Thu, 7 Nov 2024 16:46:00 +0800 Subject: [PATCH 017/108] feat: add db config --- init.sql | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/init.sql b/init.sql index 94b02fd7..453745b2 100644 --- a/init.sql +++ b/init.sql @@ -1,4 +1,10 @@ -#@v1.1.13.3-begin@; +SET FOREIGN_KEY_CHECKS = 0; +SET NAMES utf8; + +-- +-- Table structure initialize +-- + CREATE TABLE diff_conf_template ( id BIGINT PRIMARY KEY AUTO_INCREMENT, type VARCHAR(16) NOT NULL COMMENT '类型:app/db/xx', @@ -24,6 +30,5 @@ CREATE TABLE diff_conf_template_role ( ON DELETE CASCADE, INDEX (diff_conf_template_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -#@v1.1.13.3-end@; From 7f77cf042b28819d6049c8330da0388037e16733 Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Thu, 7 Nov 2024 17:02:27 +0800 Subject: [PATCH 018/108] feat: add db config --- artifacts-corepy/artifacts_corepy/server/base.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/artifacts-corepy/artifacts_corepy/server/base.py b/artifacts-corepy/artifacts_corepy/server/base.py index 1e66d975..5064e000 100644 --- a/artifacts-corepy/artifacts_corepy/server/base.py +++ b/artifacts-corepy/artifacts_corepy/server/base.py @@ -28,7 +28,8 @@ def decrypt_rsa(secret_key, encrypt_text): return text.decode('utf-8') -@config.intercept('upload_enabled', 'upload_nexus_enabled', 'ci_typeid_system_design', 'ci_typeid_unit_design', +@config.intercept('db_username', 'db_hostip', 'db_hostport', 'db_schema', + 'upload_enabled', 'upload_nexus_enabled', 'ci_typeid_system_design', 'ci_typeid_unit_design', 'ci_typeid_diff_config', 'ci_typeid_deploy_package', 'encrypt_variable_prefix', 'file_variable_prefix', 'default_special_replace', 'artifact_field', 's3_access_key', 's3_secret_key', 'nexus_server', 'nexus_repository', 'nexus_username', 'nexus_password', 'local_nexus_server', From bcaab645432abebcb77e11df46ff78533f27b98c Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Thu, 7 Nov 2024 17:15:52 +0800 Subject: [PATCH 019/108] feat: add db config --- .../artifacts_corepy/server/base.py | 21 +++++++++++++++++++ artifacts-corepy/etc/artifacts_corepy.conf | 5 +++++ 2 files changed, 26 insertions(+) diff --git a/artifacts-corepy/artifacts_corepy/server/base.py b/artifacts-corepy/artifacts_corepy/server/base.py index 5064e000..aa414405 100644 --- a/artifacts-corepy/artifacts_corepy/server/base.py +++ b/artifacts-corepy/artifacts_corepy/server/base.py @@ -14,6 +14,7 @@ from Crypto.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5 from Crypto.PublicKey import RSA from talos.core import config +from urllib.parse import quote_plus from artifacts_corepy.common import utils as plugin_utils @@ -56,3 +57,23 @@ def get_env_value(value, origin_value): raise ValueError('keys with "RSA@", but rsa_key file not exists') return new_value return value + + +@config.intercept('db_password') +def get_env_value(value, origin_value): + prefix = 'ENV@' + encrypt_prefix = 'RSA@' + if value.startswith(prefix): + env_name = value[len(prefix):] + new_value = os.getenv(env_name, default='') + if new_value.startswith(encrypt_prefix): + certs_path = RSA_KEY_PATH + if os.path.exists(certs_path) and os.path.isfile(certs_path): + with open(certs_path) as f: + new_value = decrypt_rsa(f.read(), new_value[len(encrypt_prefix):]) + else: + raise ValueError('keys with "RSA@", but rsa_key file not exists') + new_value = quote_plus(new_value) + return new_value + value = quote_plus(value) + return value diff --git a/artifacts-corepy/etc/artifacts_corepy.conf b/artifacts-corepy/etc/artifacts_corepy.conf index 437e6a32..8d532475 100644 --- a/artifacts-corepy/etc/artifacts_corepy.conf +++ b/artifacts-corepy/etc/artifacts_corepy.conf @@ -10,6 +10,11 @@ "port": 9000 }, "variables": { + "db_username": "ENV@ARTIFACTS_DB_USERNAME", + "db_password": "ENV@ARTIFACTS_DB_PASSWORD", + "db_hostip": "ENV@ARTIFACTS_DB_HOSTIP", + "db_hostport": "ENV@ARTIFACTS_DB_HOSTPORT", + "db_schema": "ENV@ARTIFACTS_DB_SCHEMA", "upload_enabled": "ENV@ARTIFACTS_UPLOAD_ENABLED", "upload_nexus_enabled": "ENV@ARTIFACTS_UPLOAD_NEXUS_ENABLED", "ci_typeid_system_design": "ENV@ARTIFACTS_CITYPE_SYSTEM_DESIGN", From ff8161f66accdcd348c29821bbc36ad8a6da308a Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Thu, 7 Nov 2024 17:41:41 +0800 Subject: [PATCH 020/108] feat: add db config --- artifacts-corepy/artifacts_corepy/common/wecube.py | 4 ++++ init.sql | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/artifacts-corepy/artifacts_corepy/common/wecube.py b/artifacts-corepy/artifacts_corepy/common/wecube.py index a2ac39e9..b8f6df28 100644 --- a/artifacts-corepy/artifacts_corepy/common/wecube.py +++ b/artifacts-corepy/artifacts_corepy/common/wecube.py @@ -62,6 +62,10 @@ def update(self, url_path, data): url = self.server + url_path return self.post(url, data) + def get(self, url_path, param=None): + url = self.server + url_path + return super().get(url, param) + def retrieve(self, url_path, data=None): # op:eq-等于;neq-不等于;in-范围过滤;like-模糊过滤;gt-大于;lt-小于;is - NULL ; isnot - NULL; # { diff --git a/init.sql b/init.sql index 453745b2..22740b4b 100644 --- a/init.sql +++ b/init.sql @@ -12,7 +12,7 @@ CREATE TABLE diff_conf_template ( value TEXT NULL COMMENT '文本值', description VARCHAR(128) DEFAULT '' COMMENT '描述', create_user VARCHAR(36) DEFAULT NULL, - created_time DATETIME DEFAULT CURRENT_TIMESTAMP, + create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_user VARCHAR(36) DEFAULT NULL, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, is_deleted TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0,1', From 27fb8e9ce6f44621cef818e624b6985a0ca233ec Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Tue, 19 Nov 2024 11:57:10 +0800 Subject: [PATCH 021/108] =?UTF-8?q?refactor(auth):=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E8=BA=AB=E4=BB=BD=E8=AE=A4=E8=AF=81=E4=B8=AD=E7=9A=84=20proxy?= =?UTF-8?q?=20=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../artifacts_corepy/apps/auth/api.py | 51 +++++++------------ .../artifacts_corepy/apps/auth/apiv2.py | 34 ------------- .../artifacts_corepy/apps/auth/controller.py | 14 ----- .../artifacts_corepy/apps/auth/route.py | 2 - 4 files changed, 19 insertions(+), 82 deletions(-) delete mode 100644 artifacts-corepy/artifacts_corepy/apps/auth/apiv2.py diff --git a/artifacts-corepy/artifacts_corepy/apps/auth/api.py b/artifacts-corepy/artifacts_corepy/apps/auth/api.py index 9048cbd9..b5ca2096 100644 --- a/artifacts-corepy/artifacts_corepy/apps/auth/api.py +++ b/artifacts-corepy/artifacts_corepy/apps/auth/api.py @@ -27,41 +27,28 @@ def generate_tokens(self, rid): refresh_token_exp = access_token_iat + CONF.refresh_token_exipres decoded_secret = terminal_utils.b64decode_key(CONF.jwt_signing_key) tokens.append({ - "expiration": - str(current_time + CONF.access_token_exipres * 1000), - "token": - jwt.encode( - { - "sub": rid, - "iat": access_token_iat, - "type": "accessToken", - "clientType": "USER", - "exp": access_token_exp, - "authority": "[" + ','.join([r['id'] for r in roles]) + "]" - }, - decoded_secret, - "HS512", - ).decode(), - "tokenType": - "accessToken" + "expiration": str(current_time + CONF.access_token_exipres * 1000), + "token": jwt.encode({ + "sub": rid, + "iat": access_token_iat, + "type": "accessToken", + "clientType": "USER", + "exp": access_token_exp, + "authority": "[" + ','.join([r['id'] for r in roles]) + "]" + }, decoded_secret, "HS512").decode(), + "tokenType": "accessToken" }) tokens.append({ - "expiration": - str(current_time + CONF.refresh_token_exipres * 1000), - "token": - jwt.encode( - { - "sub": rid, - "iat": access_token_iat, - "type": "refreshToken", - "clientType": "USER", - "exp": refresh_token_exp - }, - decoded_secret, - "HS512", + "expiration": str(current_time + CONF.refresh_token_exipres * 1000), + "token": jwt.encode({ + "sub": rid, + "iat": access_token_iat, + "type": "refreshToken", + "clientType": "USER", + "exp": refresh_token_exp + }, decoded_secret, "HS512", ).decode(), - "tokenType": - "refreshToken" + "tokenType": "refreshToken" }) return tokens diff --git a/artifacts-corepy/artifacts_corepy/apps/auth/apiv2.py b/artifacts-corepy/artifacts_corepy/apps/auth/apiv2.py deleted file mode 100644 index 2f9daf2e..00000000 --- a/artifacts-corepy/artifacts_corepy/apps/auth/apiv2.py +++ /dev/null @@ -1,34 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import - -import logging -from talos.core import config -from talos.utils import scoped_globals - -from artifacts_corepy.common import wecube - -LOG = logging.getLogger(__name__) -CONF = config.CONF - - -class WeCubeResource(object): - def __init__(self, server=None, token=None): - self.server = server or CONF.wecube.server - self.token = token or scoped_globals.GLOBALS.request.auth_token - - @property - def platform(self): - return wecube.WeCubeClient(self.server, self.token) - - def list(self, params): - pass - - -class SysRole(WeCubeResource): - def list(self, params): - return self.platform.get(self.server + '/platform/v1/roles/retrieve') - - -class SysUser(WeCubeResource): - def list(self, params): - return self.platform.get(self.server + '/platform/v1/users/roles') diff --git a/artifacts-corepy/artifacts_corepy/apps/auth/controller.py b/artifacts-corepy/artifacts_corepy/apps/auth/controller.py index 2189d494..97ea8e41 100644 --- a/artifacts-corepy/artifacts_corepy/apps/auth/controller.py +++ b/artifacts-corepy/artifacts_corepy/apps/auth/controller.py @@ -8,20 +8,6 @@ from artifacts_corepy.common.mixin import Controller, CollectionController as Collection, ItemController as Item from artifacts_corepy.apps.auth import api as auth_api -# proxy mode to platform -from artifacts_corepy.apps.auth import apiv2 as proxy_api - - -class ProxyUserRole(Controller): - name = 'auth.proxy-users' - resource = proxy_api.SysUser - allow_methods = ('GET',) - - -class ProxyRole(Controller): - name = 'auth.proxy-roles' - resource = proxy_api.SysRole - class Token(Controller): name = 'auth.token' diff --git a/artifacts-corepy/artifacts_corepy/apps/auth/route.py b/artifacts-corepy/artifacts_corepy/apps/auth/route.py index ac2e1880..80743e44 100644 --- a/artifacts-corepy/artifacts_corepy/apps/auth/route.py +++ b/artifacts-corepy/artifacts_corepy/apps/auth/route.py @@ -18,8 +18,6 @@ def add_routes(api): api.add_route('/artifacts/v1/users/{rid}/menus', controller.UserItemMenu()) api.add_route('/artifacts/v1/users/{rid}/roles', controller.UserItemRole()) api.add_route('/artifacts/v1/roles', controller.Role()) - # api.add_route('/artifacts/v1/users/roles', controller.ProxyUserRole()) - # api.add_route('/artifacts/v1/roles', controller.ProxyRole()) api.add_route('/artifacts/v1/roles/{rid}', controller.RoleItem()) api.add_route('/artifacts/v1/roles/{rid}/menus', controller.RoleItemMenu()) api.add_route('/artifacts/v1/roles/{rid}/users', controller.RoleItemUser()) From 90c091ea3e9255d63b3a3abf02c1a8a073b5e73f Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Sun, 27 Oct 2024 18:43:11 +0800 Subject: [PATCH 022/108] feat: init model and db --- .../artifacts_corepy/db/__init__.py | 3 + .../artifacts_corepy/db/models.py | 63 +++++++++++ .../artifacts_corepy/db/resource.py | 107 ++++++++++++++++++ .../artifacts_corepy/db/validator.py | 76 +++++++++++++ init.sql | 29 +++++ 5 files changed, 278 insertions(+) create mode 100644 artifacts-corepy/artifacts_corepy/db/__init__.py create mode 100644 artifacts-corepy/artifacts_corepy/db/models.py create mode 100644 artifacts-corepy/artifacts_corepy/db/resource.py create mode 100644 artifacts-corepy/artifacts_corepy/db/validator.py create mode 100644 init.sql diff --git a/artifacts-corepy/artifacts_corepy/db/__init__.py b/artifacts-corepy/artifacts_corepy/db/__init__.py new file mode 100644 index 00000000..2825e7a3 --- /dev/null +++ b/artifacts-corepy/artifacts_corepy/db/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from __future__ import absolute_import diff --git a/artifacts-corepy/artifacts_corepy/db/models.py b/artifacts-corepy/artifacts_corepy/db/models.py new file mode 100644 index 00000000..55ae9a1c --- /dev/null +++ b/artifacts-corepy/artifacts_corepy/db/models.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import + +from talos.db.dictbase import DictBase +from sqlalchemy import Column, DateTime, ForeignKey, String, text, Text, func, create_engine +from sqlalchemy.dialects.mysql import BIGINT, TINYINT +from sqlalchemy.orm import relationship +from sqlalchemy.ext.declarative import declarative_base + +Base = declarative_base() +metadata = Base.metadata + + +class DiffConfTemplate(Base, DictBase): + __tablename__ = 'diff_conf_template' + attributes = [ + 'id', 'type', 'code', 'value', 'description', 'create_user', 'created_time', 'update_user', 'updated_time', + 'is_deleted', 'roles', + ] + + id = Column(BIGINT, primary_key=True, index=True) + type = Column(String(16), nullable=False, comment='类型:应用-app,数据库-db') + code = Column(String(36), nullable=False, comment='编码') + value = Column(Text, comment='文本值') + description = Column(String(128), server_default=text("''")) + create_user = Column(String(36)) + created_time = Column(DateTime, default=func.now()) + update_user = Column(String(36)) + update_time = Column(DateTime, onupdate=func.now()) + is_deleted = Column(TINYINT, nullable=False, default=0, comment='软删除:0,1') + + roles = relationship("RoleDiffConfTemplate", back_populates="diff_conf_template", uselist=True) + + @property + def as_dict(self): + return {c.name: getattr(self, c.name) for c in self.__table__.columns} + + def __repr__(self): + return "{0}:{1}".format(self.type, self.code) + + +class DiffConfTemplateRole(Base, DictBase): + __tablename__ = 'diff_conf_template_role' + attributes = ['role', 'permission'] + + id = Column(BIGINT, primary_key=True, nullable=False) + permission = Column(String(16), nullable=False, comment='权限:MGMT,USE') + role = Column(String(64), nullable=False, comment='角色') + + diff_conf_template_id = Column(ForeignKey('diff_conf_template.id', ondelete='CASCADE')) + diff_conf_template = relationship('DiffConfTemplate', lazy=True) + + @property + def as_dict(self): + return {c.name: getattr(self, c.name) for c in self.__table__.columns} + + def __repr__(self): + return "{0}:{1}".format(self.role, self.diff_conf_template_id) + + +if __name__ == '__main__': + engine = create_engine("mysql+pymysql://root:miya.12345@127.0.0.1:3306/artifacts", encoding="utf-8") + Base.metadata.create_all(engine) diff --git a/artifacts-corepy/artifacts_corepy/db/resource.py b/artifacts-corepy/artifacts_corepy/db/resource.py new file mode 100644 index 00000000..372a8ae4 --- /dev/null +++ b/artifacts-corepy/artifacts_corepy/db/resource.py @@ -0,0 +1,107 @@ +# -*- coding: utf-8 -*- + +from __future__ import absolute_import + +from talos.core import utils +from talos.db import crud, validator +from talos.utils import scoped_globals + +from artifacts_corepy.db import validator as my_validator +from artifacts_corepy.db import models + + +class MetaCRUD(crud.ResourceBase): + _id_prefix = '' + _remove_fields = [] + + def _before_create(self, resource, validate): + if 'id' not in resource and self._id_prefix: + resource['id'] = utils.generate_prefix_uuid(self._id_prefix) + resource['created_user'] = scoped_globals.GLOBALS.request.auth_user or None + + def _before_update(self, rid, resource, validate): + resource['updated_user'] = scoped_globals.GLOBALS.request.auth_user or None + + def create(self, resource, validate=True, detail=True): + ref = super().create(resource, validate=validate, detail=detail) + if ref and self._remove_fields: + for field in self._remove_fields: + del ref[field] + return ref + + def list(self, filters=None, orders=None, offset=None, limit=None, hooks=None): + refs = super().list(filters=filters, orders=orders, offset=offset, limit=limit, hooks=hooks) + if self._remove_fields: + for ref in refs: + for field in self._remove_fields: + del ref[field] + return refs + + def get(self, rid): + ref = super().get(rid) + if ref and self._remove_fields: + for field in self._remove_fields: + del ref[field] + return ref + + def update(self, rid, resource, filters=None, validate=True, detail=True): + before_update, after_update = super().update(rid, resource, filters=filters, validate=validate, detail=detail) + if before_update and self._remove_fields: + for field in self._remove_fields: + del before_update[field] + if after_update and self._remove_fields: + for field in self._remove_fields: + del after_update[field] + return (before_update, after_update) + + def delete(self, rid, filters=None, detail=True): + num_ref, refs = super().delete(rid, filters=filters, detail=detail) + for ref in refs: + if self._remove_fields: + for field in self._remove_fields: + del ref[field] + return (num_ref, refs) + + def delete_all(self, filters=None): + num_ref, refs = super().delete_all(filters=filters) + for ref in refs: + if self._remove_fields: + for field in self._remove_fields: + del ref[field] + return (num_ref, refs) + + +class DiffConfTemplate(MetaCRUD): + orm_meta = models.DiffConfTemplate + _default_order = ['-id'] + + _validate = [ + crud.ColumnValidator(field='type', + rule=my_validator.LengthValidator(1, 16), + validate_on=('create:M', 'update:O')), + crud.ColumnValidator(field='code', + rule=my_validator.LengthValidator(1, 36), + validate_on=('create:M', 'update:M'), + nullable=False), + crud.ColumnValidator(field='value', + rule=my_validator.LengthValidator(1, 40960), + validate_on=('create:M', 'update:M')), + crud.ColumnValidator(field='description', + rule=my_validator.LengthValidator(0, 128), + validate_on=('create:O', 'update:O'), + nullable=True), + crud.ColumnValidator(field='created_user', validate_on=('*:O',), nullable=True), + crud.ColumnValidator(field='created_time', validate_on=('*:O',), nullable=True), + crud.ColumnValidator(field='updated_user', validate_on=('*:O',), nullable=True), + crud.ColumnValidator(field='updated_time', validate_on=('*:O',), nullable=True), + crud.ColumnValidator(field='roles', + rule=validator.TypeValidator(dict), + validate_on=('create:M', 'update:O'), + orm_required=False), + ] + + +class RoleDiffConfTemplate(crud.ResourceBase): + orm_meta = models.RoleDiffConfTemplate + _default_order = ['-id'] + diff --git a/artifacts-corepy/artifacts_corepy/db/validator.py b/artifacts-corepy/artifacts_corepy/db/validator.py new file mode 100644 index 00000000..b0c0db29 --- /dev/null +++ b/artifacts-corepy/artifacts_corepy/db/validator.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- + +from __future__ import absolute_import + +import re +import ipaddress +from talos.core import utils +from talos.core.i18n import _ +from talos.db import validator + + +class LengthValidator(validator.NullValidator): + def __init__(self, minimum, maximum): + self._minimum = minimum + self._maximum = maximum + + def validate(self, value): + if not utils.is_string_type(value): + return _('expected string, not %(type)s ') % {'type': type(value).__name__} + if self._minimum <= len(value) and len(value) <= self._maximum: + return True + return _('length required: %(min)d <= %(value)d <= %(max)d') % { + 'min': self._minimum, + 'value': len(value), + 'max': self._maximum + } + + +class BackRefValidator(validator.NullValidator): + def __init__(self, cls_res): + self.cls_res = cls_res + + def validate(self, value): + if self.cls_res().count(filters={'id': value}) == 0: + return _('reference of %(resource)s(%(id)s) not found') % {'resource': self.cls_res.__name__, 'id': value} + return True + + +TypeValidator = validator.TypeValidator + + +class RepeatableValidator(validator.NullValidator): + def validate(self, value): + choices = ['?', '+', '*'] + if utils.is_string_type(value): + if value not in choices: + return _('expected %(choices)s, not %(value)s') % {'choices': choices, 'value': value} + elif isinstance(value, int): + if value < 1: + return _('value should be >= 1, not %(value)s') % {'value': value} + else: + return _('expected string in %(choices)s or int(>=1), not %(type)s ') % { + 'choices': choices, + 'type': type(value).__name__ + } + return True + + +class ConcatCIDRValidator(validator.NullValidator): + def __init__(self, splitter=r',|\||;'): + self._splitter = splitter + + def validate(self, value): + if not utils.is_string_type(value): + return _('expected string, not %(type)s ') % {'type': type(value).__name__} + cidrs = re.split(self._splitter, value) + cidrs = [x for x in cidrs if x] + errors = [] + for cidr in cidrs: + try: + ipaddress.IPv4Network(cidr) + except Exception: + errors.append(_('invalid cidr: %(cidr)s') % {'cidr': cidr}) + if errors: + return ','.join(errors) + return True diff --git a/init.sql b/init.sql new file mode 100644 index 00000000..859adbdc --- /dev/null +++ b/init.sql @@ -0,0 +1,29 @@ +#@v0.1.0.1-begin@; +CREATE TABLE diff_conf_template ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + type VARCHAR(16) NOT NULL COMMENT '类型:应用-app,数据库-db', + code VARCHAR(36) NOT NULL COMMENT '编码', + value TEXT NULL COMMENT '文本值', + description VARCHAR(128) DEFAULT '' COMMENT '描述', + create_user VARCHAR(36) DEFAULT NULL, + created_time DATETIME DEFAULT CURRENT_TIMESTAMP, + update_user VARCHAR(36) DEFAULT NULL, + update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + is_deleted TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0,1', + INDEX (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE diff_conf_template_role ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + permission VARCHAR(16) NOT NULL COMMENT '权限:MGMT,USE', + role VARCHAR(64) NOT NULL COMMENT '角色', + diff_conf_template_id BIGINT, + CONSTRAINT fk_diff_conf_template + FOREIGN KEY (diff_conf_template_id) + REFERENCES diff_conf_template (id) + ON DELETE CASCADE, + INDEX (diff_conf_template_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +#@v0.1.0.1-end@; + + From a9068ee26c43219051711cf1dac8eded5916a448 Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Thu, 31 Oct 2024 15:40:51 +0800 Subject: [PATCH 023/108] feat: add variable app for diff conf template --- .../apps/variable/__init__.py | 5 + .../artifacts_corepy/apps/variable/api.py | 121 ++++++++++++++ .../apps/variable/controller.py | 34 ++++ .../artifacts_corepy/apps/variable/route.py | 10 ++ .../common/model_controller.py | 150 ++++++++++++++++++ .../artifacts_corepy/db/resource.py | 2 +- 6 files changed, 321 insertions(+), 1 deletion(-) create mode 100644 artifacts-corepy/artifacts_corepy/apps/variable/__init__.py create mode 100644 artifacts-corepy/artifacts_corepy/apps/variable/api.py create mode 100644 artifacts-corepy/artifacts_corepy/apps/variable/controller.py create mode 100644 artifacts-corepy/artifacts_corepy/apps/variable/route.py create mode 100644 artifacts-corepy/artifacts_corepy/common/model_controller.py diff --git a/artifacts-corepy/artifacts_corepy/apps/variable/__init__.py b/artifacts-corepy/artifacts_corepy/apps/variable/__init__.py new file mode 100644 index 00000000..a094396d --- /dev/null +++ b/artifacts-corepy/artifacts_corepy/apps/variable/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- + +from __future__ import absolute_import + +from artifacts_corepy.apps.variable import route diff --git a/artifacts-corepy/artifacts_corepy/apps/variable/api.py b/artifacts-corepy/artifacts_corepy/apps/variable/api.py new file mode 100644 index 00000000..fc408f6b --- /dev/null +++ b/artifacts-corepy/artifacts_corepy/apps/variable/api.py @@ -0,0 +1,121 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import + +import logging +from talos.core import config +from talos.core.i18n import _ +from talos.utils.scoped_globals import GLOBALS +from artifacts_corepy.db import resource + +from artifacts_corepy.common import exceptions + +CONF = config.CONF +LOG = logging.getLogger(__name__) +TOKEN_KEY = 'terminal_subsystem_token' + + +class DiffConfTemplate(resource.DiffConfTemplate): + def count(self, filters=None, offset=None, limit=None, hooks=None): + auth_roles = GLOBALS.request.auth_permissions + filters = filters or {} + filters['roles.role'] = {'in': list(auth_roles)} + return super().count(filters=filters, offset=offset, limit=limit, hooks=hooks) + + def list(self, filters=None, orders=None, offset=None, limit=None, hooks=None): + auth_roles = GLOBALS.request.auth_permissions + filters = filters or {} + filters['roles.role'] = {'in': list(auth_roles)} + refs = super().list(filters=filters, orders=orders, offset=offset, limit=limit, hooks=hooks) + for ref in refs: + # process roles + role_mapping = {'owner': [], 'executor': []} + for role in ref['roles']: + role_mapping[role['type']].append(role['role']) + if set(role_mapping['owner']) & auth_roles: + ref['is_owner'] = 1 + else: + ref['is_owner'] = 0 + ref['roles'] = role_mapping + return refs + + def _addtional_create(self, session, data, created): + if 'roles' in data: + refs = data['roles'] + role_owner = refs.get('owner', []) or [] + role_executor = refs.get('executor', []) or [] + ref_groups = [(role_owner, 'owner', resource.BookmarkRole), + (role_executor, 'executor', resource.BookmarkRole)] + for role_refs, ref_type, resource_type in ref_groups: + reduce_refs = list(set(role_refs)) + reduce_refs.sort(key=role_refs.index) + if ref_type == 'owner' and len(reduce_refs) == 0: + raise exceptions.ValidationError(message=_('length of roles.owner must be >= 1')) + for ref in reduce_refs: + new_ref = {} + new_ref['bookmark_id'] = created['id'] + new_ref['role'] = ref + new_ref['type'] = ref_type + resource_type(transaction=session).create(new_ref) + + def _addtional_update(self, session, rid, data, before_updated, after_updated): + if 'roles' in data: + refs = data['roles'] + role_owner = refs.get('owner', None) + role_executor = refs.get('executor', None) + ref_groups = [(role_owner, 'owner', resource.BookmarkRole), + (role_executor, 'executor', resource.BookmarkRole)] + for role_refs, ref_type, resource_type in ref_groups: + if role_refs is None: + continue + reduce_refs = list(set(role_refs)) + reduce_refs.sort(key=role_refs.index) + if ref_type == 'owner' and len(reduce_refs) == 0: + raise exceptions.ValidationError(message=_('length of roles.owner must be >= 1')) + old_refs = [ + result['role'] for result in resource_type(session=session).list(filters={ + 'bookmark_id': before_updated['id'], + 'type': ref_type + }) + ] + create_refs = list(set(reduce_refs) - set(old_refs)) + create_refs.sort(key=reduce_refs.index) + delete_refs = set(old_refs) - set(reduce_refs) + + if delete_refs: + resource_type(transaction=session).delete_all(filters={ + 'bookmark_id': before_updated['id'], + 'type': ref_type + }) + for ref in create_refs: + new_ref = {} + new_ref['bookmark_id'] = before_updated['id'] + new_ref['role'] = ref + new_ref['type'] = ref_type + resource_type(transaction=session).create(new_ref) + + def update(self, rid, data, filters=None, validate=True, detail=True): + auth_roles = GLOBALS.request.auth_permissions + if super().count({'id': rid}) and resource.BookmarkRole().count({ + 'bookmark_id': rid, + 'role': { + 'in': list(auth_roles) + }, + 'type': 'owner' + }) == 0: + raise exceptions.ValidationError(message=_('the resource(%(resource)s) does not belong to you') % + {'resource': 'Bookmark[%s]' % rid}) + return super().update(rid, data, filters=filters, validate=validate, detail=detail) + + def delete(self, rid, filters=None, detail=True): + auth_roles = GLOBALS.request.auth_permissions + if super().count({'id': rid}) and resource.BookmarkRole().count({ + 'bookmark_id': rid, + 'role': { + 'in': list(auth_roles) + }, + 'type': 'owner' + }) == 0: + raise exceptions.ValidationError(message=_('the resource(%(resource)s) does not belong to you') % + {'resource': 'Bookmark[%s]' % rid}) + return super().delete(rid, filters=filters, detail=detail) + diff --git a/artifacts-corepy/artifacts_corepy/apps/variable/controller.py b/artifacts-corepy/artifacts_corepy/apps/variable/controller.py new file mode 100644 index 00000000..26fb37a3 --- /dev/null +++ b/artifacts-corepy/artifacts_corepy/apps/variable/controller.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import + +from artifacts_corepy.common import exceptions +from artifacts_corepy.common.model_controller import Collection, Item +from artifacts_corepy.apps.variable import api + + +class CollectionDiffConfTemplates(Collection): + name = 'artifacts.diff_conf_templates' + resource = api.DiffConfTemplate + + def on_get(self, req, resp, **kwargs): + self._validate_method(req) + refs = [] + count = 0 + criteria = self._build_criteria(req) + if criteria: + refs = self.list_query(req, criteria, **kwargs) + for r in refs: + # remove password info + r.pop('password', None) + count = len(refs) + resp.json = {'code': 200, 'status': 'OK', 'data': {'count': count, 'data': refs}, 'message': 'success'} + + def list_query(self, req, criteria, **kwargs): + criteria.pop('fields', None) + refs = self.make_resource(req).list_query(**criteria) + return refs + + +class ItemDiffConfTemplate(Item): + name = 'artifacts.diff_conf_templates' + resource = api.DiffConfTemplate diff --git a/artifacts-corepy/artifacts_corepy/apps/variable/route.py b/artifacts-corepy/artifacts_corepy/apps/variable/route.py new file mode 100644 index 00000000..6b86694d --- /dev/null +++ b/artifacts-corepy/artifacts_corepy/apps/variable/route.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- + +from __future__ import absolute_import + +from artifacts_corepy.apps.variable import controller + + +def add_routes(api): + api.add_route('/artifacts/v1/diff-conf-templates', controller.CollectionDiffConfTemplates()) + api.add_route('/artifacts/v1/diff-conf-templates/{rid}', controller.ItemDiffConfTemplate()) diff --git a/artifacts-corepy/artifacts_corepy/common/model_controller.py b/artifacts-corepy/artifacts_corepy/common/model_controller.py new file mode 100644 index 00000000..d03335a9 --- /dev/null +++ b/artifacts-corepy/artifacts_corepy/common/model_controller.py @@ -0,0 +1,150 @@ +# -*- coding: utf-8 -*- + +from __future__ import absolute_import + +import falcon +from talos.common.controller import CollectionController +from talos.common.controller import ItemController +from talos.common.controller import Controller as BaseController +from talos.core import exceptions as base_ex +from talos.core import utils +from talos.core.i18n import _ + +from artifacts_corepy.common import exceptions + + +class Controller(BaseController): + allow_methods = ( + 'GET', + 'POST', + ) + + def on_post(self, req, resp, **kwargs): + self._validate_method(req) + self._validate_data(req) + data = req.json + resp.json = {'code': 200, 'status': 'OK', 'data': self.create(req, data, **kwargs), 'message': 'success'} + resp.status = falcon.HTTP_200 + + def create(self, req, data, **kwargs): + return self.make_resource(req).create(data, **kwargs) + + def on_get(self, req, resp, **kwargs): + self._validate_method(req) + resp.json = {'code': 200, 'status': 'OK', 'data': self.get(req, **kwargs), 'message': 'success'} + resp.status = falcon.HTTP_200 + + def get(self, req, **kwargs): + return self.make_resource(req).get(**kwargs) + + +class Collection(CollectionController): + def on_get(self, req, resp, **kwargs): + self._validate_method(req) + refs = [] + count = 0 + criteria = self._build_criteria(req) + if criteria: + refs = self.list(req, criteria, **kwargs) + count = self.count(req, criteria, results=refs, **kwargs) + resp.json = {'code': 200, 'status': 'OK', 'data': {'count': count, 'data': refs}, 'message': 'success'} + + def on_post(self, req, resp, **kwargs): + self._validate_method(req) + self._validate_data(req) + datas = req.json + if not utils.is_list_type(datas): + raise exceptions.PluginError(_('data must be list type')) + rets = [] + ex_rets = [] + for idx, data in enumerate(datas): + try: + rets.append(self.create(req, data, **kwargs)) + except base_ex.Error as e: + ex_rets.append({'index': idx + 1, 'message': str(e)}) + if len(ex_rets): + raise exceptions.BatchPartialError(num=len(ex_rets), action='create', exception_data={'data': ex_rets}) + resp.json = {'code': 200, 'status': 'OK', 'data': rets, 'message': 'success'} + resp.status = falcon.HTTP_200 + + def on_patch(self, req, resp, **kwargs): + self._validate_method(req) + self._validate_data(req) + datas = req.json + if not utils.is_list_type(datas): + raise exceptions.PluginError(_('data must be list type')) + rets = [] + ex_rets = [] + for idx, data in enumerate(datas): + try: + res_instance = self.make_resource(req) + if res_instance.primary_keys not in data: + raise exceptions.FieldRequired(attribute=res_instance.primary_keys) + rid = data.pop(res_instance.primary_keys) + before_update, after_update = self.update(req, data, rid=rid) + if after_update is None: + raise exceptions.NotFoundError(resource='%s[%s]' % (self.resource.__name__, rid)) + rets.append(after_update) + except base_ex.Error as e: + ex_rets.append({'index': idx + 1, 'message': str(e)}) + if len(ex_rets): + raise exceptions.BatchPartialError(num=len(ex_rets), action='update', exception_data={'data': ex_rets}) + resp.json = {'code': 200, 'status': 'OK', 'data': rets, 'message': 'success'} + resp.status = falcon.HTTP_200 + + def update(self, req, data, **kwargs): + rid = kwargs.pop('rid') + return self.make_resource(req).update(rid, data) + + def on_delete(self, req, resp, **kwargs): + self._validate_method(req) + self._validate_data(req) + datas = req.json + if not utils.is_list_type(datas): + raise exceptions.PluginError(_('data must be list type')) + rets = [] + ex_rets = [] + for idx, data in enumerate(datas): + try: + res_instance = self.make_resource(req) + ref_count, ref_details = self.delete(req, rid=data) + rets.append(ref_details[0]) + except base_ex.Error as e: + ex_rets.append({'index': idx + 1, 'message': str(e)}) + if len(ex_rets): + raise exceptions.BatchPartialError(num=len(ex_rets), action='delete', exception_data={'data': ex_rets}) + resp.json = {'code': 200, 'status': 'OK', 'data': rets, 'message': 'success'} + resp.status = falcon.HTTP_200 + + def delete(self, req, **kwargs): + return self.make_resource(req).delete(**kwargs) + + +class Item(ItemController): + def on_get(self, req, resp, **kwargs): + self._validate_method(req) + ref = self.get(req, **kwargs) + if ref is not None: + resp.json = {'code': 200, 'status': 'OK', 'data': ref, 'message': 'success'} + else: + raise exceptions.NotFoundError(resource='%s[%s]' % (self.resource.__name__, kwargs.get('rid', '-'))) + + def on_patch(self, req, resp, **kwargs): + self._validate_method(req) + self._validate_data(req) + data = req.json + if data is not None and not isinstance(data, dict): + raise exceptions.PluginError(_('data must be dict type')) + ref_before, ref_after = self.update(req, data, **kwargs) + if ref_after is not None: + resp.json = {'code': 200, 'status': 'OK', 'data': ref_after, 'message': 'success'} + else: + raise exceptions.NotFoundError(resource='%s[%s]' % (self.resource.__name__, kwargs.get('rid', '-'))) + + def on_delete(self, req, resp, **kwargs): + self._validate_method(req) + ref, details = self.delete(req, **kwargs) + if ref: + resp.json = {'code': 200, 'status': 'OK', 'data': {'count': ref, 'data': details}, 'message': 'success'} + else: + raise exceptions.NotFoundError(resource='%s[%s]' % (self.resource.__name__, kwargs.get('rid', '-'))) diff --git a/artifacts-corepy/artifacts_corepy/db/resource.py b/artifacts-corepy/artifacts_corepy/db/resource.py index 372a8ae4..b90aa810 100644 --- a/artifacts-corepy/artifacts_corepy/db/resource.py +++ b/artifacts-corepy/artifacts_corepy/db/resource.py @@ -102,6 +102,6 @@ class DiffConfTemplate(MetaCRUD): class RoleDiffConfTemplate(crud.ResourceBase): - orm_meta = models.RoleDiffConfTemplate + orm_meta = models.DiffConfTemplateRole _default_order = ['-id'] From b4d5ccd28ff32bbc9be9658292f3a89a7b653179 Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Thu, 31 Oct 2024 18:27:52 +0800 Subject: [PATCH 024/108] feat: add _addtional_create --- .../artifacts_corepy/apps/variable/api.py | 111 +----------------- .../apps/variable/controller.py | 28 +---- .../artifacts_corepy/apps/variable/route.py | 4 +- .../common/{model_controller.py => mixin.py} | 9 +- .../artifacts_corepy/db/models.py | 6 +- .../artifacts_corepy/db/resource.py | 64 ++-------- 6 files changed, 29 insertions(+), 193 deletions(-) rename artifacts-corepy/artifacts_corepy/common/{model_controller.py => mixin.py} (95%) diff --git a/artifacts-corepy/artifacts_corepy/apps/variable/api.py b/artifacts-corepy/artifacts_corepy/apps/variable/api.py index fc408f6b..40470c42 100644 --- a/artifacts-corepy/artifacts_corepy/apps/variable/api.py +++ b/artifacts-corepy/artifacts_corepy/apps/variable/api.py @@ -3,119 +3,20 @@ import logging from talos.core import config -from talos.core.i18n import _ -from talos.utils.scoped_globals import GLOBALS from artifacts_corepy.db import resource from artifacts_corepy.common import exceptions CONF = config.CONF LOG = logging.getLogger(__name__) -TOKEN_KEY = 'terminal_subsystem_token' class DiffConfTemplate(resource.DiffConfTemplate): - def count(self, filters=None, offset=None, limit=None, hooks=None): - auth_roles = GLOBALS.request.auth_permissions - filters = filters or {} - filters['roles.role'] = {'in': list(auth_roles)} - return super().count(filters=filters, offset=offset, limit=limit, hooks=hooks) - - def list(self, filters=None, orders=None, offset=None, limit=None, hooks=None): - auth_roles = GLOBALS.request.auth_permissions - filters = filters or {} - filters['roles.role'] = {'in': list(auth_roles)} - refs = super().list(filters=filters, orders=orders, offset=offset, limit=limit, hooks=hooks) - for ref in refs: - # process roles - role_mapping = {'owner': [], 'executor': []} - for role in ref['roles']: - role_mapping[role['type']].append(role['role']) - if set(role_mapping['owner']) & auth_roles: - ref['is_owner'] = 1 - else: - ref['is_owner'] = 0 - ref['roles'] = role_mapping - return refs - def _addtional_create(self, session, data, created): if 'roles' in data: - refs = data['roles'] - role_owner = refs.get('owner', []) or [] - role_executor = refs.get('executor', []) or [] - ref_groups = [(role_owner, 'owner', resource.BookmarkRole), - (role_executor, 'executor', resource.BookmarkRole)] - for role_refs, ref_type, resource_type in ref_groups: - reduce_refs = list(set(role_refs)) - reduce_refs.sort(key=role_refs.index) - if ref_type == 'owner' and len(reduce_refs) == 0: - raise exceptions.ValidationError(message=_('length of roles.owner must be >= 1')) - for ref in reduce_refs: - new_ref = {} - new_ref['bookmark_id'] = created['id'] - new_ref['role'] = ref - new_ref['type'] = ref_type - resource_type(transaction=session).create(new_ref) - - def _addtional_update(self, session, rid, data, before_updated, after_updated): - if 'roles' in data: - refs = data['roles'] - role_owner = refs.get('owner', None) - role_executor = refs.get('executor', None) - ref_groups = [(role_owner, 'owner', resource.BookmarkRole), - (role_executor, 'executor', resource.BookmarkRole)] - for role_refs, ref_type, resource_type in ref_groups: - if role_refs is None: - continue - reduce_refs = list(set(role_refs)) - reduce_refs.sort(key=role_refs.index) - if ref_type == 'owner' and len(reduce_refs) == 0: - raise exceptions.ValidationError(message=_('length of roles.owner must be >= 1')) - old_refs = [ - result['role'] for result in resource_type(session=session).list(filters={ - 'bookmark_id': before_updated['id'], - 'type': ref_type - }) - ] - create_refs = list(set(reduce_refs) - set(old_refs)) - create_refs.sort(key=reduce_refs.index) - delete_refs = set(old_refs) - set(reduce_refs) - - if delete_refs: - resource_type(transaction=session).delete_all(filters={ - 'bookmark_id': before_updated['id'], - 'type': ref_type - }) - for ref in create_refs: - new_ref = {} - new_ref['bookmark_id'] = before_updated['id'] - new_ref['role'] = ref - new_ref['type'] = ref_type - resource_type(transaction=session).create(new_ref) - - def update(self, rid, data, filters=None, validate=True, detail=True): - auth_roles = GLOBALS.request.auth_permissions - if super().count({'id': rid}) and resource.BookmarkRole().count({ - 'bookmark_id': rid, - 'role': { - 'in': list(auth_roles) - }, - 'type': 'owner' - }) == 0: - raise exceptions.ValidationError(message=_('the resource(%(resource)s) does not belong to you') % - {'resource': 'Bookmark[%s]' % rid}) - return super().update(rid, data, filters=filters, validate=validate, detail=detail) - - def delete(self, rid, filters=None, detail=True): - auth_roles = GLOBALS.request.auth_permissions - if super().count({'id': rid}) and resource.BookmarkRole().count({ - 'bookmark_id': rid, - 'role': { - 'in': list(auth_roles) - }, - 'type': 'owner' - }) == 0: - raise exceptions.ValidationError(message=_('the resource(%(resource)s) does not belong to you') % - {'resource': 'Bookmark[%s]' % rid}) - return super().delete(rid, filters=filters, detail=detail) - + for perm_role in data['roles']: + resource.DiffConfTemplateRole(transaction=session).create({ + 'diff_conf_template_id': created['id'], + 'role': perm_role['role'], + 'permission': perm_role['permission'] + }) diff --git a/artifacts-corepy/artifacts_corepy/apps/variable/controller.py b/artifacts-corepy/artifacts_corepy/apps/variable/controller.py index 26fb37a3..ff84a5ff 100644 --- a/artifacts-corepy/artifacts_corepy/apps/variable/controller.py +++ b/artifacts-corepy/artifacts_corepy/apps/variable/controller.py @@ -1,34 +1,18 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import +# from talos.common.controller import CollectionController, ItemController + from artifacts_corepy.common import exceptions -from artifacts_corepy.common.model_controller import Collection, Item +from artifacts_corepy.common.mixin import CollectionController, ItemController from artifacts_corepy.apps.variable import api -class CollectionDiffConfTemplates(Collection): +class CollectionDiffConfTemplates(CollectionController): name = 'artifacts.diff_conf_templates' resource = api.DiffConfTemplate - def on_get(self, req, resp, **kwargs): - self._validate_method(req) - refs = [] - count = 0 - criteria = self._build_criteria(req) - if criteria: - refs = self.list_query(req, criteria, **kwargs) - for r in refs: - # remove password info - r.pop('password', None) - count = len(refs) - resp.json = {'code': 200, 'status': 'OK', 'data': {'count': count, 'data': refs}, 'message': 'success'} - - def list_query(self, req, criteria, **kwargs): - criteria.pop('fields', None) - refs = self.make_resource(req).list_query(**criteria) - return refs - -class ItemDiffConfTemplate(Item): - name = 'artifacts.diff_conf_templates' +class ItemDiffConfTemplate(ItemController): + name = 'artifacts.diff_conf_template' resource = api.DiffConfTemplate diff --git a/artifacts-corepy/artifacts_corepy/apps/variable/route.py b/artifacts-corepy/artifacts_corepy/apps/variable/route.py index 6b86694d..fe4e3042 100644 --- a/artifacts-corepy/artifacts_corepy/apps/variable/route.py +++ b/artifacts-corepy/artifacts_corepy/apps/variable/route.py @@ -6,5 +6,5 @@ def add_routes(api): - api.add_route('/artifacts/v1/diff-conf-templates', controller.CollectionDiffConfTemplates()) - api.add_route('/artifacts/v1/diff-conf-templates/{rid}', controller.ItemDiffConfTemplate()) + api.add_route('/artifacts/api/v1/diff-conf-templates', controller.CollectionDiffConfTemplates()) + api.add_route('/artifacts/api/v1/diff-conf-templates/{rid}', controller.ItemDiffConfTemplate()) diff --git a/artifacts-corepy/artifacts_corepy/common/model_controller.py b/artifacts-corepy/artifacts_corepy/common/mixin.py similarity index 95% rename from artifacts-corepy/artifacts_corepy/common/model_controller.py rename to artifacts-corepy/artifacts_corepy/common/mixin.py index d03335a9..5c31852f 100644 --- a/artifacts-corepy/artifacts_corepy/common/model_controller.py +++ b/artifacts-corepy/artifacts_corepy/common/mixin.py @@ -3,8 +3,8 @@ from __future__ import absolute_import import falcon -from talos.common.controller import CollectionController -from talos.common.controller import ItemController +from talos.common.controller import CollectionController as BaseCollectionController +from talos.common.controller import ItemController as BaseItemController from talos.common.controller import Controller as BaseController from talos.core import exceptions as base_ex from talos.core import utils @@ -38,7 +38,7 @@ def get(self, req, **kwargs): return self.make_resource(req).get(**kwargs) -class Collection(CollectionController): +class CollectionController(BaseCollectionController): def on_get(self, req, resp, **kwargs): self._validate_method(req) refs = [] @@ -61,6 +61,7 @@ def on_post(self, req, resp, **kwargs): try: rets.append(self.create(req, data, **kwargs)) except base_ex.Error as e: + print(e) ex_rets.append({'index': idx + 1, 'message': str(e)}) if len(ex_rets): raise exceptions.BatchPartialError(num=len(ex_rets), action='create', exception_data={'data': ex_rets}) @@ -120,7 +121,7 @@ def delete(self, req, **kwargs): return self.make_resource(req).delete(**kwargs) -class Item(ItemController): +class ItemController(BaseItemController): def on_get(self, req, resp, **kwargs): self._validate_method(req) ref = self.get(req, **kwargs) diff --git a/artifacts-corepy/artifacts_corepy/db/models.py b/artifacts-corepy/artifacts_corepy/db/models.py index 55ae9a1c..36186d9f 100644 --- a/artifacts-corepy/artifacts_corepy/db/models.py +++ b/artifacts-corepy/artifacts_corepy/db/models.py @@ -14,7 +14,7 @@ class DiffConfTemplate(Base, DictBase): __tablename__ = 'diff_conf_template' attributes = [ - 'id', 'type', 'code', 'value', 'description', 'create_user', 'created_time', 'update_user', 'updated_time', + 'id', 'type', 'code', 'value', 'description', 'create_user', 'create_time', 'update_user', 'update_time', 'is_deleted', 'roles', ] @@ -24,12 +24,12 @@ class DiffConfTemplate(Base, DictBase): value = Column(Text, comment='文本值') description = Column(String(128), server_default=text("''")) create_user = Column(String(36)) - created_time = Column(DateTime, default=func.now()) + create_time = Column(DateTime, default=func.now()) update_user = Column(String(36)) update_time = Column(DateTime, onupdate=func.now()) is_deleted = Column(TINYINT, nullable=False, default=0, comment='软删除:0,1') - roles = relationship("RoleDiffConfTemplate", back_populates="diff_conf_template", uselist=True) + roles = relationship("DiffConfTemplateRole", back_populates="diff_conf_template") @property def as_dict(self): diff --git a/artifacts-corepy/artifacts_corepy/db/resource.py b/artifacts-corepy/artifacts_corepy/db/resource.py index b90aa810..365c24ff 100644 --- a/artifacts-corepy/artifacts_corepy/db/resource.py +++ b/artifacts-corepy/artifacts_corepy/db/resource.py @@ -17,63 +17,15 @@ class MetaCRUD(crud.ResourceBase): def _before_create(self, resource, validate): if 'id' not in resource and self._id_prefix: resource['id'] = utils.generate_prefix_uuid(self._id_prefix) - resource['created_user'] = scoped_globals.GLOBALS.request.auth_user or None + resource['create_user'] = scoped_globals.GLOBALS.request.auth_user or None def _before_update(self, rid, resource, validate): - resource['updated_user'] = scoped_globals.GLOBALS.request.auth_user or None - - def create(self, resource, validate=True, detail=True): - ref = super().create(resource, validate=validate, detail=detail) - if ref and self._remove_fields: - for field in self._remove_fields: - del ref[field] - return ref - - def list(self, filters=None, orders=None, offset=None, limit=None, hooks=None): - refs = super().list(filters=filters, orders=orders, offset=offset, limit=limit, hooks=hooks) - if self._remove_fields: - for ref in refs: - for field in self._remove_fields: - del ref[field] - return refs - - def get(self, rid): - ref = super().get(rid) - if ref and self._remove_fields: - for field in self._remove_fields: - del ref[field] - return ref - - def update(self, rid, resource, filters=None, validate=True, detail=True): - before_update, after_update = super().update(rid, resource, filters=filters, validate=validate, detail=detail) - if before_update and self._remove_fields: - for field in self._remove_fields: - del before_update[field] - if after_update and self._remove_fields: - for field in self._remove_fields: - del after_update[field] - return (before_update, after_update) - - def delete(self, rid, filters=None, detail=True): - num_ref, refs = super().delete(rid, filters=filters, detail=detail) - for ref in refs: - if self._remove_fields: - for field in self._remove_fields: - del ref[field] - return (num_ref, refs) - - def delete_all(self, filters=None): - num_ref, refs = super().delete_all(filters=filters) - for ref in refs: - if self._remove_fields: - for field in self._remove_fields: - del ref[field] - return (num_ref, refs) + resource['update_user'] = scoped_globals.GLOBALS.request.auth_user or None class DiffConfTemplate(MetaCRUD): orm_meta = models.DiffConfTemplate - _default_order = ['-id'] + _default_order = ['-id', 'update_time', 'create_time'] _validate = [ crud.ColumnValidator(field='type', @@ -90,18 +42,16 @@ class DiffConfTemplate(MetaCRUD): rule=my_validator.LengthValidator(0, 128), validate_on=('create:O', 'update:O'), nullable=True), - crud.ColumnValidator(field='created_user', validate_on=('*:O',), nullable=True), - crud.ColumnValidator(field='created_time', validate_on=('*:O',), nullable=True), - crud.ColumnValidator(field='updated_user', validate_on=('*:O',), nullable=True), - crud.ColumnValidator(field='updated_time', validate_on=('*:O',), nullable=True), + # crud.ColumnValidator(field='updated_user', validate_on=('*:O',), nullable=True), + # crud.ColumnValidator(field='updated_time', validate_on=('*:O',), nullable=True), crud.ColumnValidator(field='roles', - rule=validator.TypeValidator(dict), + rule=validator.TypeValidator(list), validate_on=('create:M', 'update:O'), orm_required=False), ] -class RoleDiffConfTemplate(crud.ResourceBase): +class DiffConfTemplateRole(crud.ResourceBase): orm_meta = models.DiffConfTemplateRole _default_order = ['-id'] From c59605b82a998533efd533790278dae5a0155620 Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Thu, 31 Oct 2024 21:45:29 +0800 Subject: [PATCH 025/108] feat: add soft delete support --- .../artifacts_corepy/apps/variable/api.py | 15 +++++++------ .../artifacts_corepy/common/mixin.py | 14 +++++++++++-- .../artifacts_corepy/db/models.py | 6 +++++- .../artifacts_corepy/db/resource.py | 21 ++++++++++++++----- .../artifacts_corepy/server/wsgi_server.py | 2 +- 5 files changed, 43 insertions(+), 15 deletions(-) diff --git a/artifacts-corepy/artifacts_corepy/apps/variable/api.py b/artifacts-corepy/artifacts_corepy/apps/variable/api.py index 40470c42..e2515b5c 100644 --- a/artifacts-corepy/artifacts_corepy/apps/variable/api.py +++ b/artifacts-corepy/artifacts_corepy/apps/variable/api.py @@ -14,9 +14,12 @@ class DiffConfTemplate(resource.DiffConfTemplate): def _addtional_create(self, session, data, created): if 'roles' in data: - for perm_role in data['roles']: - resource.DiffConfTemplateRole(transaction=session).create({ - 'diff_conf_template_id': created['id'], - 'role': perm_role['role'], - 'permission': perm_role['permission'] - }) + for perm, perm_roles in data['roles'].items(): + for perm_role in perm_roles: + if not perm_role: + continue + resource.DiffConfTemplateRole(transaction=session).create({ + 'diff_conf_template_id': created['id'], + 'role': perm_role, + 'permission': perm + }) diff --git a/artifacts-corepy/artifacts_corepy/common/mixin.py b/artifacts-corepy/artifacts_corepy/common/mixin.py index 5c31852f..6653ccfe 100644 --- a/artifacts-corepy/artifacts_corepy/common/mixin.py +++ b/artifacts-corepy/artifacts_corepy/common/mixin.py @@ -61,7 +61,6 @@ def on_post(self, req, resp, **kwargs): try: rets.append(self.create(req, data, **kwargs)) except base_ex.Error as e: - print(e) ex_rets.append({'index': idx + 1, 'message': str(e)}) if len(ex_rets): raise exceptions.BatchPartialError(num=len(ex_rets), action='create', exception_data={'data': ex_rets}) @@ -118,10 +117,15 @@ def on_delete(self, req, resp, **kwargs): resp.status = falcon.HTTP_200 def delete(self, req, **kwargs): - return self.make_resource(req).delete(**kwargs) + # return self.make_resource(req).delete(**kwargs) + before, after = self.make_resource(req).update( + rid=kwargs.get('rid'), resource={"is_deleted": 1}, validate=False + ) + return 1 if before else 0, [after] class ItemController(BaseItemController): + def on_get(self, req, resp, **kwargs): self._validate_method(req) ref = self.get(req, **kwargs) @@ -149,3 +153,9 @@ def on_delete(self, req, resp, **kwargs): resp.json = {'code': 200, 'status': 'OK', 'data': {'count': ref, 'data': details}, 'message': 'success'} else: raise exceptions.NotFoundError(resource='%s[%s]' % (self.resource.__name__, kwargs.get('rid', '-'))) + + def delete(self, req, **kwargs): + before, after = self.make_resource(req).update( + rid=kwargs.get('rid'), resource={"is_deleted": 1}, validate=False + ) + return 1 if before else 0, [after] diff --git a/artifacts-corepy/artifacts_corepy/db/models.py b/artifacts-corepy/artifacts_corepy/db/models.py index 36186d9f..c91559c9 100644 --- a/artifacts-corepy/artifacts_corepy/db/models.py +++ b/artifacts-corepy/artifacts_corepy/db/models.py @@ -2,7 +2,7 @@ from __future__ import absolute_import from talos.db.dictbase import DictBase -from sqlalchemy import Column, DateTime, ForeignKey, String, text, Text, func, create_engine +from sqlalchemy import Column, DateTime, ForeignKey, String, text, Text, func, create_engine, UniqueConstraint from sqlalchemy.dialects.mysql import BIGINT, TINYINT from sqlalchemy.orm import relationship from sqlalchemy.ext.declarative import declarative_base @@ -13,6 +13,10 @@ class DiffConfTemplate(Base, DictBase): __tablename__ = 'diff_conf_template' + __table_args__ = ( + UniqueConstraint('type', 'code', name='uk_type_code'), + ) + attributes = [ 'id', 'type', 'code', 'value', 'description', 'create_user', 'create_time', 'update_user', 'update_time', 'is_deleted', 'roles', diff --git a/artifacts-corepy/artifacts_corepy/db/resource.py b/artifacts-corepy/artifacts_corepy/db/resource.py index 365c24ff..e43e9149 100644 --- a/artifacts-corepy/artifacts_corepy/db/resource.py +++ b/artifacts-corepy/artifacts_corepy/db/resource.py @@ -22,6 +22,19 @@ def _before_create(self, resource, validate): def _before_update(self, rid, resource, validate): resource['update_user'] = scoped_globals.GLOBALS.request.auth_user or None + def _apply_primary_key_filter(self, query, rid): + query = super()._apply_primary_key_filter(query, rid) + + if hasattr(self.orm_meta, 'is_deleted'): + query = query.filter(self.orm_meta.is_deleted == 0) + + return query + + def _addtional_list(self, query, filters): + if hasattr(self.orm_meta, 'is_deleted'): + query = query.filter(self.orm_meta.is_deleted == 0) + return query + class DiffConfTemplate(MetaCRUD): orm_meta = models.DiffConfTemplate @@ -29,7 +42,7 @@ class DiffConfTemplate(MetaCRUD): _validate = [ crud.ColumnValidator(field='type', - rule=my_validator.LengthValidator(1, 16), + rule=my_validator.validator.InValidator(['app', 'db']), validate_on=('create:M', 'update:O')), crud.ColumnValidator(field='code', rule=my_validator.LengthValidator(1, 36), @@ -42,10 +55,9 @@ class DiffConfTemplate(MetaCRUD): rule=my_validator.LengthValidator(0, 128), validate_on=('create:O', 'update:O'), nullable=True), - # crud.ColumnValidator(field='updated_user', validate_on=('*:O',), nullable=True), - # crud.ColumnValidator(field='updated_time', validate_on=('*:O',), nullable=True), + crud.ColumnValidator(field='create_user', validate_on=('*:O',), nullable=True), crud.ColumnValidator(field='roles', - rule=validator.TypeValidator(list), + rule=validator.TypeValidator(dict), validate_on=('create:M', 'update:O'), orm_required=False), ] @@ -54,4 +66,3 @@ class DiffConfTemplate(MetaCRUD): class DiffConfTemplateRole(crud.ResourceBase): orm_meta = models.DiffConfTemplateRole _default_order = ['-id'] - diff --git a/artifacts-corepy/artifacts_corepy/server/wsgi_server.py b/artifacts-corepy/artifacts_corepy/server/wsgi_server.py index c754ba17..3091e7ca 100644 --- a/artifacts-corepy/artifacts_corepy/server/wsgi_server.py +++ b/artifacts-corepy/artifacts_corepy/server/wsgi_server.py @@ -26,7 +26,7 @@ def error_serializer(req, resp, exception): if 'error_code' in representation: representation['code'] = representation.pop('error_code') representation['status'] = 'ERROR' - representation['data'] = None + representation['data'] = representation.get('data') or None representation['message'] = representation.pop('description', '') resp.body = json.dumps(representation, cls=utils.ComplexEncoder) resp.content_type = 'application/json' From 0b2a910747cac1b1dae12623676fe42ae2e0a90c Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Thu, 31 Oct 2024 21:56:18 +0800 Subject: [PATCH 026/108] feat: clean code --- artifacts-corepy/artifacts_corepy/apps/variable/controller.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/artifacts-corepy/artifacts_corepy/apps/variable/controller.py b/artifacts-corepy/artifacts_corepy/apps/variable/controller.py index ff84a5ff..29232a88 100644 --- a/artifacts-corepy/artifacts_corepy/apps/variable/controller.py +++ b/artifacts-corepy/artifacts_corepy/apps/variable/controller.py @@ -1,9 +1,6 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import -# from talos.common.controller import CollectionController, ItemController - -from artifacts_corepy.common import exceptions from artifacts_corepy.common.mixin import CollectionController, ItemController from artifacts_corepy.apps.variable import api From f3aa9d185aa3f4907db1480b3dc5da9e2dc15913 Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Fri, 1 Nov 2024 16:03:25 +0800 Subject: [PATCH 027/108] feat: add _addtional_list for role filter --- .../artifacts_corepy/apps/variable/api.py | 13 +++++++++++++ artifacts-corepy/artifacts_corepy/db/resource.py | 1 + 2 files changed, 14 insertions(+) diff --git a/artifacts-corepy/artifacts_corepy/apps/variable/api.py b/artifacts-corepy/artifacts_corepy/apps/variable/api.py index e2515b5c..aad0d540 100644 --- a/artifacts-corepy/artifacts_corepy/apps/variable/api.py +++ b/artifacts-corepy/artifacts_corepy/apps/variable/api.py @@ -3,6 +3,8 @@ import logging from talos.core import config +from talos.utils.scoped_globals import GLOBALS + from artifacts_corepy.db import resource from artifacts_corepy.common import exceptions @@ -23,3 +25,14 @@ def _addtional_create(self, session, data, created): 'role': perm_role, 'permission': perm }) + + def _addtional_list(self, query, filters): + """权限控制,角色数据过滤""" + query = super()._addtional_list(query, filters) + permission_filters = {"roles.role": {'in': list(GLOBALS.request.auth_permissions)}} + query = self._apply_filters(query, self.orm_meta, permission_filters) + # print(str(query)) + # print(query.statement.compile(), query.statement.compile().params) + return query + + diff --git a/artifacts-corepy/artifacts_corepy/db/resource.py b/artifacts-corepy/artifacts_corepy/db/resource.py index e43e9149..0aa13b94 100644 --- a/artifacts-corepy/artifacts_corepy/db/resource.py +++ b/artifacts-corepy/artifacts_corepy/db/resource.py @@ -31,6 +31,7 @@ def _apply_primary_key_filter(self, query, rid): return query def _addtional_list(self, query, filters): + query = super()._addtional_list(query, filters) if hasattr(self.orm_meta, 'is_deleted'): query = query.filter(self.orm_meta.is_deleted == 0) return query From 065b945420435edc9aa31166ec6e8097d2c15d43 Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Mon, 4 Nov 2024 16:55:21 +0800 Subject: [PATCH 028/108] feat: add artifacts.diff-conf-templates and auth app --- .../artifacts_corepy/apps/auth/__init__.py | 4 + .../artifacts_corepy/apps/auth/api.py | 208 ++++++++++++++++++ .../artifacts_corepy/apps/auth/controller.py | 131 +++++++++++ .../artifacts_corepy/apps/auth/route.py | 25 +++ .../apps/variable/controller.py | 4 +- .../artifacts_corepy/db/models.py | 109 ++++++++- artifacts-corepy/etc/artifacts_corepy.conf | 12 +- 7 files changed, 487 insertions(+), 6 deletions(-) create mode 100644 artifacts-corepy/artifacts_corepy/apps/auth/__init__.py create mode 100644 artifacts-corepy/artifacts_corepy/apps/auth/api.py create mode 100644 artifacts-corepy/artifacts_corepy/apps/auth/controller.py create mode 100644 artifacts-corepy/artifacts_corepy/apps/auth/route.py diff --git a/artifacts-corepy/artifacts_corepy/apps/auth/__init__.py b/artifacts-corepy/artifacts_corepy/apps/auth/__init__.py new file mode 100644 index 00000000..8e8d92d4 --- /dev/null +++ b/artifacts-corepy/artifacts_corepy/apps/auth/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import + +from artifacts_corepy.apps.auth import route diff --git a/artifacts-corepy/artifacts_corepy/apps/auth/api.py b/artifacts-corepy/artifacts_corepy/apps/auth/api.py new file mode 100644 index 00000000..9048cbd9 --- /dev/null +++ b/artifacts-corepy/artifacts_corepy/apps/auth/api.py @@ -0,0 +1,208 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import + +import logging +import time + +import jwt +from talos.core import config, utils +from talos.core.i18n import _ +from talos.core import exceptions as core_ex + +from artifacts_corepy.common import exceptions +from artifacts_corepy.common import utils as terminal_utils +from artifacts_corepy.db import resource as db_resource + +CONF = config.CONF +LOG = logging.getLogger(__name__) + + +class SysUser(db_resource.SysUser): + def generate_tokens(self, rid): + roles = self.get_roles(rid) + tokens = [] + current_time = int(time.time() * 1000) + access_token_iat = int(current_time / 1000) + access_token_exp = access_token_iat + CONF.access_token_exipres + refresh_token_exp = access_token_iat + CONF.refresh_token_exipres + decoded_secret = terminal_utils.b64decode_key(CONF.jwt_signing_key) + tokens.append({ + "expiration": + str(current_time + CONF.access_token_exipres * 1000), + "token": + jwt.encode( + { + "sub": rid, + "iat": access_token_iat, + "type": "accessToken", + "clientType": "USER", + "exp": access_token_exp, + "authority": "[" + ','.join([r['id'] for r in roles]) + "]" + }, + decoded_secret, + "HS512", + ).decode(), + "tokenType": + "accessToken" + }) + tokens.append({ + "expiration": + str(current_time + CONF.refresh_token_exipres * 1000), + "token": + jwt.encode( + { + "sub": rid, + "iat": access_token_iat, + "type": "refreshToken", + "clientType": "USER", + "exp": refresh_token_exp + }, + decoded_secret, + "HS512", + ).decode(), + "tokenType": + "refreshToken" + }) + return tokens + + def login(self, username, password): + with self.get_session(): + if self.check_password(username, password): + return self.generate_tokens(username) + else: + raise core_ex.LoginError() + + def refresh(self, token): + with self.get_session(): + try: + decoded_secret = terminal_utils.b64decode_key(CONF.jwt_signing_key) + info = jwt.decode(token, key=decoded_secret, verify=True) + if info['type'] != 'refreshToken': + raise core_ex.AuthError() + return self.generate_tokens(info['sub']) + except jwt.exceptions.ExpiredSignatureError: + raise core_ex.AuthError() + except jwt.exceptions.DecodeError: + raise core_ex.AuthError() + + def get_menus(self, rid): + menus = [] + exists = {} + roles = self.get_roles(rid) + for role in roles: + for menu in role['menus']: + if menu['is_active'] == 'yes' and menu['id'] not in exists: + menus.append(menu) + exists[menu['id']] = True + return menus + + def get_roles(self, rid): + ref = self.get(rid) + if ref: + return ref['roles'] + return [] + + def create(self, resource, validate=True, detail=True): + resource['salt'] = utils.generate_salt(16) + password = utils.generate_salt(16) + resource['password'] = utils.encrypt_password(password, resource['salt']) + ref = super().create(resource, validate=validate, detail=detail) + ref['password'] = password + return ref + + def reset_password(self, rid, password=None): + resource = {} + resource['salt'] = utils.generate_salt(16) + password = password or utils.generate_salt(16) + resource['password'] = utils.encrypt_password(password, resource['salt']) + before_update, after_update = self.update(rid, resource, validate=False) + if after_update: + after_update['password'] = password + return after_update + + def check_password(self, rid, password): + refs = self.list_internal({'id': rid}) + if refs: + return utils.check_password(refs[0]['password'], password, refs[0]['salt']) + return False + + def update_password(self, rid, password, origin_password): + if not password: + raise exceptions.PluginError(message=_('unabled to set empty password')) + if self.check_password(rid, origin_password): + resource = {} + resource['salt'] = utils.generate_salt(16) + password = password or utils.generate_salt(16) + resource['password'] = utils.encrypt_password(password, resource['salt']) + before_update, after_update = self.update(rid, resource, validate=False) + return after_update + else: + raise exceptions.PluginError(message=_('faild to set new password: incorrect origin password')) + + def delete(self, rid, filters=None, detail=True): + refs = self.list({'id': rid}) + if refs and refs[0]['is_system'] == 'yes': + raise exceptions.PluginError(message=_('unable to delete system user')) + with self.transaction() as session: + db_resource.SysRoleUser(transaction=session).delete_all({'user_id': rid}) + return super().delete(rid, filters=filters, detail=detail) + + +class SysRole(db_resource.SysRole): + def get_users(self, rid): + ref = self.get(rid) + if ref: + return ref['users'] + return [] + + def _update_intersect_refs(self, rid, self_field, ref_field, resource_type, refs, session): + old_refs = [result[ref_field] for result in resource_type(session=session).list(filters={self_field: rid})] + create_refs = list(set(refs) - set(old_refs)) + create_refs.sort(key=refs.index) + delete_refs = set(old_refs) - set(refs) + if delete_refs: + resource_type(transaction=session).delete_all(filters={ + self_field: rid, + ref_field: { + 'in': list(delete_refs) + } + }) + for ref in create_refs: + new_ref = {} + new_ref[self_field] = rid + new_ref[ref_field] = ref + resource_type(transaction=session).create(new_ref) + + def set_users(self, rid, users): + with self.transaction() as session: + self._update_intersect_refs(rid, 'role_id', 'user_id', db_resource.SysRoleUser, users, session) + return self.get_users(rid) + + def get_menus(self, rid, is_active=True): + ref = self.get(rid) + if ref: + if is_active: + return [menu for menu in ref['menus'] if menu['is_active'] == 'yes'] + else: + return ref['menus'] + return [] + + def set_menus(self, rid, menus): + with self.transaction() as session: + self._update_intersect_refs(rid, 'role_id', 'menu_id', db_resource.SysRoleMenu, menus, session) + return self.get_menus(rid, is_active=False) + + def delete(self, rid, filters=None, detail=True): + refs = self.list({'id': rid}) + if refs and refs[0]['is_system'] == 'yes': + raise exceptions.PluginError(message=_('unable to delete system role')) + with self.transaction() as session: + bindings = db_resource.SysRoleUser(transaction=session).list({'role_id': rid}) + if len(bindings) > 0: + users = ','.join([bind['user_id'] for bind in bindings]) + raise exceptions.PluginError(message=_('role binds with %(users)s') % {'users': users}) + return super().delete(rid, filters=filters, detail=detail) + + +class SysMenu(db_resource.SysMenu): + pass diff --git a/artifacts-corepy/artifacts_corepy/apps/auth/controller.py b/artifacts-corepy/artifacts_corepy/apps/auth/controller.py new file mode 100644 index 00000000..ddcb2226 --- /dev/null +++ b/artifacts-corepy/artifacts_corepy/apps/auth/controller.py @@ -0,0 +1,131 @@ +# coding=utf-8 + +from __future__ import absolute_import + +from talos.core.i18n import _ +from talos.utils.scoped_globals import GLOBALS + +from artifacts_corepy.common import exceptions +from artifacts_corepy.common.mixin import Controller, CollectionController as Collection, ItemController as Item +from artifacts_corepy.apps.auth import api as auth_api + + +class Token(Controller): + name = 'auth.token' + resource = auth_api.SysUser + allow_methods = ('POST', ) + + def create(self, req, data, **kwargs): + return self.make_resource(req).login(data.get('username', ''), data.get('password', '')) + + +class TokenRefresh(Controller): + name = 'auth.token' + resource = auth_api.SysUser + allow_methods = ('GET', ) + + def get(self, req, **kwargs): + return self.make_resource(req).refresh(GLOBALS.request.auth_token) + + +class UserMenus(Controller): + name = 'auth.user-menus' + resource = auth_api.SysUser + allow_methods = ('GET', ) + + def get(self, req, **kwargs): + return self.make_resource(req).get_menus(GLOBALS.request.auth_user) + + +class UserPassword(Controller): + name = 'auth.user-password' + resource = auth_api.SysUser + allow_methods = ('POST', ) + + def create(self, req, data, **kwargs): + return self.make_resource(req).update_password(GLOBALS.request.auth_user, data.get('newPassword', ''), + data.get('oldPassword', '')) + + +class User(Collection): + name = 'auth.users' + resource = auth_api.SysUser + + +class UserItem(Item): + name = 'auth.users' + resource = auth_api.SysUser + + +class UserItemMenu(Item): + name = 'auth.users' + resource = auth_api.SysUser + allow_methods = ('GET', ) + + def get(self, req, rid): + return self.make_resource(req).get_menus(rid) + + +class UserItemResetPassword(Controller): + name = 'auth.users.password' + resource = auth_api.SysUser + allow_methods = ('POST', ) + + def create(self, req, data, rid): + data = data or {} + return self.make_resource(req).reset_password(rid, password=data.get('password', None)) + + +class UserItemRole(Item): + name = 'auth.users' + resource = auth_api.SysUser + allow_methods = ('GET', ) + + def get(self, req, rid): + return self.make_resource(req).get_roles(rid) + + +class Role(Collection): + name = 'auth.roles' + resource = auth_api.SysRole + + +class RoleItem(Item): + name = 'auth.roles' + resource = auth_api.SysRole + + +class RoleItemMenu(Controller): + name = 'auth.roles' + resource = auth_api.SysRole + allow_methods = ('GET', 'POST') + + def get(self, req, rid): + return self.make_resource(req).get_menus(rid) + + def create(self, req, data, rid): + return self.make_resource(req).set_menus(rid, data) + + +class RoleItemUser(Controller): + name = 'auth.roles' + resource = auth_api.SysRole + allow_methods = ('GET', 'POST') + + def get(self, req, rid): + return self.make_resource(req).get_users(rid) + + def create(self, req, data, rid): + return self.make_resource(req).set_users(rid, data) + + +class Menu(Collection): + name = 'auth.menus' + resource = auth_api.SysMenu + allow_methods = ('GET', ) + + +class MenuItem(Item): + name = 'auth.menus' + resource = auth_api.SysMenu + allow_methods = ('GET', ) diff --git a/artifacts-corepy/artifacts_corepy/apps/auth/route.py b/artifacts-corepy/artifacts_corepy/apps/auth/route.py new file mode 100644 index 00000000..80743e44 --- /dev/null +++ b/artifacts-corepy/artifacts_corepy/apps/auth/route.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import + +from artifacts_corepy.apps.auth import controller + + +def add_routes(api): + # 不需要登陆 + api.add_route('/artifacts/v1/login', controller.Token()) + api.add_route('/artifacts/v1/refresh-token', controller.TokenRefresh()) + # 登陆但不需要权限校验 + api.add_route('/artifacts/v1/user-menus', controller.UserMenus()) + api.add_route('/artifacts/v1/user-password', controller.UserPassword()) + # 登陆且经过权限校验 + api.add_route('/artifacts/v1/users', controller.User()) + api.add_route('/artifacts/v1/users/{rid}', controller.UserItem()) + api.add_route('/artifacts/v1/users/{rid}/reset-password', controller.UserItemResetPassword()) + api.add_route('/artifacts/v1/users/{rid}/menus', controller.UserItemMenu()) + api.add_route('/artifacts/v1/users/{rid}/roles', controller.UserItemRole()) + api.add_route('/artifacts/v1/roles', controller.Role()) + api.add_route('/artifacts/v1/roles/{rid}', controller.RoleItem()) + api.add_route('/artifacts/v1/roles/{rid}/menus', controller.RoleItemMenu()) + api.add_route('/artifacts/v1/roles/{rid}/users', controller.RoleItemUser()) + api.add_route('/artifacts/v1/menus', controller.Menu()) + # api.add_route('/artifacts/v1/menus/{rid}', controller.MenuItem()) diff --git a/artifacts-corepy/artifacts_corepy/apps/variable/controller.py b/artifacts-corepy/artifacts_corepy/apps/variable/controller.py index 29232a88..80608901 100644 --- a/artifacts-corepy/artifacts_corepy/apps/variable/controller.py +++ b/artifacts-corepy/artifacts_corepy/apps/variable/controller.py @@ -6,10 +6,10 @@ class CollectionDiffConfTemplates(CollectionController): - name = 'artifacts.diff_conf_templates' + name = 'artifacts.diff-conf-templates' resource = api.DiffConfTemplate class ItemDiffConfTemplate(ItemController): - name = 'artifacts.diff_conf_template' + name = 'artifacts.diff-conf-templates' resource = api.DiffConfTemplate diff --git a/artifacts-corepy/artifacts_corepy/db/models.py b/artifacts-corepy/artifacts_corepy/db/models.py index c91559c9..3089e63d 100644 --- a/artifacts-corepy/artifacts_corepy/db/models.py +++ b/artifacts-corepy/artifacts_corepy/db/models.py @@ -2,8 +2,10 @@ from __future__ import absolute_import from talos.db.dictbase import DictBase -from sqlalchemy import Column, DateTime, ForeignKey, String, text, Text, func, create_engine, UniqueConstraint -from sqlalchemy.dialects.mysql import BIGINT, TINYINT +from sqlalchemy import ( + Column, DateTime, ForeignKey, String, text, Text, func, create_engine, UniqueConstraint, +) +from sqlalchemy.dialects.mysql import BIGINT, TINYINT, INTEGER from sqlalchemy.orm import relationship from sqlalchemy.ext.declarative import declarative_base @@ -62,6 +64,109 @@ def __repr__(self): return "{0}:{1}".format(self.role, self.diff_conf_template_id) +class SysMenu(Base, DictBase): + __tablename__ = 'sys_menu' + attributes = [ + 'id', 'display_name', 'url', 'seq_no', 'parent', 'is_active', 'created_by', 'created_time', 'updated_by', + 'updated_time' + ] + summary_attributes = ['id', 'display_name', 'url', 'seq_no', 'parent', 'is_active'] + + id = Column(String(36), primary_key=True, comment='主键') + display_name = Column(String(64), comment='显示名') + url = Column(String(255), comment='访问路径') + seq_no = Column(INTEGER(11), server_default=text("'0'"), comment='排序号') + parent = Column(String(36), comment='父菜单') + is_active = Column(String(8), server_default=text("'yes'"), comment='状态') + created_by = Column(String(36)) + created_time = Column(DateTime) + updated_by = Column(String(36)) + updated_time = Column(DateTime) + + roles = relationship("SysRole", secondary="sys_role_menu", back_populates="menus", uselist=True, viewonly=True) + + +class SysRole(Base, DictBase): + __tablename__ = 'sys_role' + attributes = [ + 'id', 'description', 'role_type', 'is_system', 'created_by', 'created_time', 'updated_by', 'updated_time', + 'menus' + ] + summary_attributes = ['id', 'description', 'role_type', 'is_system'] + detail_attributes = [ + 'id', 'description', 'role_type', 'is_system', 'created_by', 'created_time', 'updated_by', 'updated_time', + 'users', 'menus' + ] + + id = Column(String(36), primary_key=True, comment='主键') + description = Column(String(255), comment='描述') + role_type = Column(String(32), comment='角色类型') + is_system = Column(String(8), server_default=text("'no'"), comment='是否系统角色') + created_by = Column(String(36)) + created_time = Column(DateTime) + updated_by = Column(String(36)) + updated_time = Column(DateTime) + + users = relationship("SysUser", secondary="sys_role_user", back_populates="roles", uselist=True, viewonly=True) + menus = relationship("SysMenu", secondary="sys_role_menu", back_populates="roles", uselist=True, viewonly=True) + + +class SysUser(Base, DictBase): + __tablename__ = 'sys_user' + attributes = [ + 'id', 'display_name', 'password', 'salt', 'description', 'is_system', 'created_by', 'created_time', + 'updated_by', 'updated_time' + ] + summary_attributes = ['id', 'display_name', 'description', 'is_system'] + detail_attributes = [ + 'id', 'display_name', 'password', 'salt', 'description', 'is_system', 'created_by', 'created_time', + 'updated_by', 'updated_time', 'roles' + ] + + id = Column(String(36), primary_key=True, comment='主键') + display_name = Column(String(64), comment='显示名') + password = Column(String(128), comment='加密密钥') + salt = Column(String(36), comment='加密盐') + description = Column(String(255), comment='描述') + is_system = Column(String(8), server_default=text("'no'"), comment='是否系统用户') + created_by = Column(String(36)) + created_time = Column(DateTime) + updated_by = Column(String(36)) + updated_time = Column(DateTime) + + roles = relationship("SysRole", secondary="sys_role_user", back_populates="users", uselist=True, viewonly=True) + + +class SysRoleMenu(Base, DictBase): + __tablename__ = 'sys_role_menu' + + id = Column(BIGINT(20), primary_key=True) + role_id = Column(ForeignKey('sys_role.id'), index=True, comment='角色id') + menu_id = Column(ForeignKey('sys_menu.id'), index=True, comment='菜单id') + created_by = Column(String(36)) + created_time = Column(DateTime) + updated_by = Column(String(36)) + updated_time = Column(DateTime) + + menu = relationship('SysMenu') + role = relationship('SysRole') + + +class SysRoleUser(Base, DictBase): + __tablename__ = 'sys_role_user' + + id = Column(BIGINT(20), primary_key=True) + role_id = Column(ForeignKey('sys_role.id'), index=True, comment='角色id') + user_id = Column(ForeignKey('sys_user.id'), index=True, comment='用户id') + created_by = Column(String(36)) + created_time = Column(DateTime) + updated_by = Column(String(36)) + updated_time = Column(DateTime) + + role = relationship('SysRole') + user = relationship('SysUser') + + if __name__ == '__main__': engine = create_engine("mysql+pymysql://root:miya.12345@127.0.0.1:3306/artifacts", encoding="utf-8") Base.metadata.create_all(engine) diff --git a/artifacts-corepy/etc/artifacts_corepy.conf b/artifacts-corepy/etc/artifacts_corepy.conf index 57e798c8..f227498d 100644 --- a/artifacts-corepy/etc/artifacts_corepy.conf +++ b/artifacts-corepy/etc/artifacts_corepy.conf @@ -81,7 +81,10 @@ "max_overflow": 5 }, "application": { - "names": ["artifacts_corepy.apps.package", "artifacts_corepy.apps.plugin"] + "names": [ + "artifacts_corepy.apps.package", "artifacts_corepy.apps.plugin", + "artifacts_corepy.apps.variable", "artifacts_corepy.apps.auth" + ] }, "rate_limit": { "enabled": false, @@ -180,8 +183,13 @@ "artifacts.pushcomposepackage": ["SUB_SYSTEM", "IMPLEMENTATION_ARTIFACT_MANAGEMENT"], "artifacts.systemconfig": ["SUB_SYSTEM", "IMPLEMENTATION_ARTIFACT_MANAGEMENT"], "artifacts.unit-design.nexus.path": ["SUB_SYSTEM", "IMPLEMENTATION_ARTIFACT_MANAGEMENT"], - "artifacts.process.defs": ["SUB_SYSTEM", "IMPLEMENTATION_ARTIFACT_MANAGEMENT"] + "artifacts.process.defs": ["SUB_SYSTEM", "IMPLEMENTATION_ARTIFACT_MANAGEMENT"], + "artifacts.diff-conf-templates": ["SUB_SYSTEM", "IMPLEMENTATION_ARTIFACT_MANAGEMENT"] }, + "permission_passthrough": ["auth.token", "auth.user-menus", "auth.user-password"], + "menu_permissions": { + "system_authorization": ["auth.users", "auth.users.password", "auth.roles", "auth.menus"] + }, "plugin_permissions": [ "artifacts.plugins.fromimage" ] From a8f48b8aedd8ceee85a9a8c9c41f05a742fb4103 Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Mon, 4 Nov 2024 16:57:59 +0800 Subject: [PATCH 029/108] feat: add artifacts.diff-conf-templates and auth app --- .../artifacts_corepy/db/resource.py | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/artifacts-corepy/artifacts_corepy/db/resource.py b/artifacts-corepy/artifacts_corepy/db/resource.py index 0aa13b94..cc841bf1 100644 --- a/artifacts-corepy/artifacts_corepy/db/resource.py +++ b/artifacts-corepy/artifacts_corepy/db/resource.py @@ -67,3 +67,99 @@ class DiffConfTemplate(MetaCRUD): class DiffConfTemplateRole(crud.ResourceBase): orm_meta = models.DiffConfTemplateRole _default_order = ['-id'] + + +class SysRoleMenu(MetaCRUD): + orm_meta = models.SysRoleMenu + _default_order = ['-created_time'] + + +class SysRoleUser(MetaCRUD): + orm_meta = models.SysRoleUser + _default_order = ['-created_time'] + + +class SysMenu(MetaCRUD): + orm_meta = models.SysMenu + _default_order = ['seq_no'] + _id_prefix = 'menu-' + + _validate = [ + crud.ColumnValidator(field='id', validate_on=('create:M', 'update:O')), + crud.ColumnValidator(field='display_name', + rule=my_validator.LengthValidator(1, 64), + validate_on=('create:M', 'update:O')), + crud.ColumnValidator(field='url', + rule=my_validator.LengthValidator(0, 255), + validate_on=('create:O', 'update:O'), + nullable=True), + crud.ColumnValidator(field='seq_no', + rule=my_validator.validator.NumberValidator(int, range_min=0, range_max=65535), + validate_on=('create:O', 'update:O')), + crud.ColumnValidator(field='parent', + rule=my_validator.BackRefValidator(SysRoleMenu), + validate_on=('create:O', 'update:O'), + nullable=True), + crud.ColumnValidator(field='is_active', + rule=my_validator.validator.InValidator(['yes', 'no']), + validate_on=('create:O', 'update:O')), + crud.ColumnValidator(field='created_by', validate_on=('*:O',), nullable=True), + crud.ColumnValidator(field='created_time', validate_on=('*:O',), nullable=True), + crud.ColumnValidator(field='updated_by', validate_on=('*:O',), nullable=True), + crud.ColumnValidator(field='updated_time', validate_on=('*:O',), nullable=True), + ] + + +class SysRole(MetaCRUD): + orm_meta = models.SysRole + _default_order = ['-created_time'] + _id_prefix = 'role-' + _detail_relationship_as_summary = True + + _validate = [ + crud.ColumnValidator(field='id', validate_on=('create:M', 'update:O')), + crud.ColumnValidator(field='description', + rule=my_validator.LengthValidator(1, 255), + validate_on=('create:M', 'update:O')), + crud.ColumnValidator(field='role_type', + rule=my_validator.LengthValidator(0, 32), + validate_on=('create:O', 'update:O'), + nullable=True), + crud.ColumnValidator(field='is_system', + rule=my_validator.validator.InValidator(['yes', 'no']), + validate_on=('create:O', 'update:O')), + crud.ColumnValidator(field='created_by', validate_on=('*:O',), nullable=True), + crud.ColumnValidator(field='created_time', validate_on=('*:O',), nullable=True), + crud.ColumnValidator(field='updated_by', validate_on=('*:O',), nullable=True), + crud.ColumnValidator(field='updated_time', validate_on=('*:O',), nullable=True), + ] + + +class SysUser(MetaCRUD): + orm_meta = models.SysUser + _default_order = ['-created_time'] + _remove_fields = ['password', 'salt'] + _id_prefix = 'user-' + + _validate = [ + crud.ColumnValidator(field='id', validate_on=('create:M', 'update:O')), + crud.ColumnValidator(field='display_name', + rule=my_validator.LengthValidator(1, 64), + validate_on=('create:M', 'update:O')), + crud.ColumnValidator(field='password', rule=my_validator.LengthValidator(1, 128), validate_on=('create:M',)), + crud.ColumnValidator(field='salt', rule=my_validator.LengthValidator(1, 36), validate_on=('create:M',)), + crud.ColumnValidator(field='description', + rule=my_validator.LengthValidator(0, 255), + validate_on=('create:O', 'update:O'), + nullable=True), + crud.ColumnValidator(field='is_system', + rule=my_validator.validator.InValidator(['yes', 'no']), + validate_on=('create:O', 'update:O')), + crud.ColumnValidator(field='created_by', validate_on=('*:O',), nullable=True), + crud.ColumnValidator(field='created_time', validate_on=('*:O',), nullable=True), + crud.ColumnValidator(field='updated_by', validate_on=('*:O',), nullable=True), + crud.ColumnValidator(field='updated_time', validate_on=('*:O',), nullable=True), + ] + + def list_internal(self, filters=None, orders=None, offset=None, limit=None, hooks=None): + return super(MetaCRUD, self).list(filters=filters, orders=orders, offset=offset, limit=limit, hooks=hooks) From 75a79a509fbc60120e11571753f414b6c69d79d5 Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Mon, 4 Nov 2024 17:22:05 +0800 Subject: [PATCH 030/108] feat: add proxy mode to platform --- .../artifacts_corepy/apps/auth/apiv2.py | 31 +++++++++++++++++++ .../artifacts_corepy/apps/auth/controller.py | 25 ++++++++------- 2 files changed, 45 insertions(+), 11 deletions(-) create mode 100644 artifacts-corepy/artifacts_corepy/apps/auth/apiv2.py diff --git a/artifacts-corepy/artifacts_corepy/apps/auth/apiv2.py b/artifacts-corepy/artifacts_corepy/apps/auth/apiv2.py new file mode 100644 index 00000000..1c8d839f --- /dev/null +++ b/artifacts-corepy/artifacts_corepy/apps/auth/apiv2.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import + +import logging +from talos.core import config +from talos.utils import scoped_globals + +from artifacts_corepy.common import wecube + +LOG = logging.getLogger(__name__) +CONF = config.CONF + + +class WeCubeResource(object): + def __init__(self, server=None, token=None): + self.server = server or CONF.wecube.server + self.token = token or scoped_globals.GLOBALS.request.auth_token + + @property + def platform(self): + return wecube.WeCubeClient(self.server, self.token) + + def list(self, params): + pass + + +class SysUser(WeCubeResource): + def list(self, params): + params['filters'] = [] + params['paging'] = False + return self.platform.get(self.server + '/platform/v1/roles/retrieve', params) diff --git a/artifacts-corepy/artifacts_corepy/apps/auth/controller.py b/artifacts-corepy/artifacts_corepy/apps/auth/controller.py index ddcb2226..6434005a 100644 --- a/artifacts-corepy/artifacts_corepy/apps/auth/controller.py +++ b/artifacts-corepy/artifacts_corepy/apps/auth/controller.py @@ -1,5 +1,4 @@ -# coding=utf-8 - +# -*- coding: utf-8 -*- from __future__ import absolute_import from talos.core.i18n import _ @@ -10,10 +9,14 @@ from artifacts_corepy.apps.auth import api as auth_api +# proxy mode to platform +# from artifacts_corepy.apps.auth import apiv2 as auth_api + + class Token(Controller): name = 'auth.token' resource = auth_api.SysUser - allow_methods = ('POST', ) + allow_methods = ('POST',) def create(self, req, data, **kwargs): return self.make_resource(req).login(data.get('username', ''), data.get('password', '')) @@ -22,7 +25,7 @@ def create(self, req, data, **kwargs): class TokenRefresh(Controller): name = 'auth.token' resource = auth_api.SysUser - allow_methods = ('GET', ) + allow_methods = ('GET',) def get(self, req, **kwargs): return self.make_resource(req).refresh(GLOBALS.request.auth_token) @@ -31,7 +34,7 @@ def get(self, req, **kwargs): class UserMenus(Controller): name = 'auth.user-menus' resource = auth_api.SysUser - allow_methods = ('GET', ) + allow_methods = ('GET',) def get(self, req, **kwargs): return self.make_resource(req).get_menus(GLOBALS.request.auth_user) @@ -40,7 +43,7 @@ def get(self, req, **kwargs): class UserPassword(Controller): name = 'auth.user-password' resource = auth_api.SysUser - allow_methods = ('POST', ) + allow_methods = ('POST',) def create(self, req, data, **kwargs): return self.make_resource(req).update_password(GLOBALS.request.auth_user, data.get('newPassword', ''), @@ -60,7 +63,7 @@ class UserItem(Item): class UserItemMenu(Item): name = 'auth.users' resource = auth_api.SysUser - allow_methods = ('GET', ) + allow_methods = ('GET',) def get(self, req, rid): return self.make_resource(req).get_menus(rid) @@ -69,7 +72,7 @@ def get(self, req, rid): class UserItemResetPassword(Controller): name = 'auth.users.password' resource = auth_api.SysUser - allow_methods = ('POST', ) + allow_methods = ('POST',) def create(self, req, data, rid): data = data or {} @@ -79,7 +82,7 @@ def create(self, req, data, rid): class UserItemRole(Item): name = 'auth.users' resource = auth_api.SysUser - allow_methods = ('GET', ) + allow_methods = ('GET',) def get(self, req, rid): return self.make_resource(req).get_roles(rid) @@ -122,10 +125,10 @@ def create(self, req, data, rid): class Menu(Collection): name = 'auth.menus' resource = auth_api.SysMenu - allow_methods = ('GET', ) + allow_methods = ('GET',) class MenuItem(Item): name = 'auth.menus' resource = auth_api.SysMenu - allow_methods = ('GET', ) + allow_methods = ('GET',) From 19b865173594eff09c835973800afb50de7c4928 Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Wed, 6 Nov 2024 20:30:58 +0800 Subject: [PATCH 031/108] feat: add /artifacts/platform/v1/users/roles /artifacts/platform/v1/roles --- .../artifacts_corepy/apps/auth/apiv2.py | 9 ++-- .../artifacts_corepy/apps/auth/controller.py | 14 +++++- .../artifacts_corepy/apps/auth/route.py | 2 + .../artifacts_corepy/apps/package/apiv2.py | 1 + .../artifacts_corepy/apps/package/route.py | 43 ++++++++++++++++++- 5 files changed, 62 insertions(+), 7 deletions(-) diff --git a/artifacts-corepy/artifacts_corepy/apps/auth/apiv2.py b/artifacts-corepy/artifacts_corepy/apps/auth/apiv2.py index 1c8d839f..2f9daf2e 100644 --- a/artifacts-corepy/artifacts_corepy/apps/auth/apiv2.py +++ b/artifacts-corepy/artifacts_corepy/apps/auth/apiv2.py @@ -24,8 +24,11 @@ def list(self, params): pass +class SysRole(WeCubeResource): + def list(self, params): + return self.platform.get(self.server + '/platform/v1/roles/retrieve') + + class SysUser(WeCubeResource): def list(self, params): - params['filters'] = [] - params['paging'] = False - return self.platform.get(self.server + '/platform/v1/roles/retrieve', params) + return self.platform.get(self.server + '/platform/v1/users/roles') diff --git a/artifacts-corepy/artifacts_corepy/apps/auth/controller.py b/artifacts-corepy/artifacts_corepy/apps/auth/controller.py index 6434005a..2189d494 100644 --- a/artifacts-corepy/artifacts_corepy/apps/auth/controller.py +++ b/artifacts-corepy/artifacts_corepy/apps/auth/controller.py @@ -8,9 +8,19 @@ from artifacts_corepy.common.mixin import Controller, CollectionController as Collection, ItemController as Item from artifacts_corepy.apps.auth import api as auth_api - # proxy mode to platform -# from artifacts_corepy.apps.auth import apiv2 as auth_api +from artifacts_corepy.apps.auth import apiv2 as proxy_api + + +class ProxyUserRole(Controller): + name = 'auth.proxy-users' + resource = proxy_api.SysUser + allow_methods = ('GET',) + + +class ProxyRole(Controller): + name = 'auth.proxy-roles' + resource = proxy_api.SysRole class Token(Controller): diff --git a/artifacts-corepy/artifacts_corepy/apps/auth/route.py b/artifacts-corepy/artifacts_corepy/apps/auth/route.py index 80743e44..ac2e1880 100644 --- a/artifacts-corepy/artifacts_corepy/apps/auth/route.py +++ b/artifacts-corepy/artifacts_corepy/apps/auth/route.py @@ -18,6 +18,8 @@ def add_routes(api): api.add_route('/artifacts/v1/users/{rid}/menus', controller.UserItemMenu()) api.add_route('/artifacts/v1/users/{rid}/roles', controller.UserItemRole()) api.add_route('/artifacts/v1/roles', controller.Role()) + # api.add_route('/artifacts/v1/users/roles', controller.ProxyUserRole()) + # api.add_route('/artifacts/v1/roles', controller.ProxyRole()) api.add_route('/artifacts/v1/roles/{rid}', controller.RoleItem()) api.add_route('/artifacts/v1/roles/{rid}/menus', controller.RoleItemMenu()) api.add_route('/artifacts/v1/roles/{rid}/users', controller.RoleItemUser()) diff --git a/artifacts-corepy/artifacts_corepy/apps/package/apiv2.py b/artifacts-corepy/artifacts_corepy/apps/package/apiv2.py index f8791e5f..ec927cee 100644 --- a/artifacts-corepy/artifacts_corepy/apps/package/apiv2.py +++ b/artifacts-corepy/artifacts_corepy/apps/package/apiv2.py @@ -2465,6 +2465,7 @@ def get(self, unit_design_id): unit_design = resp_json['data']['contents'][0] return {'artifact_path': self.get_unit_design_artifact_path(unit_design)} + class DiffConfig(WeCubeResource): def update(self, data): cmdb_client = self.get_cmdb_client() diff --git a/artifacts-corepy/artifacts_corepy/apps/package/route.py b/artifacts-corepy/artifacts_corepy/apps/package/route.py index 7fd99bbd..c5015710 100644 --- a/artifacts-corepy/artifacts_corepy/apps/package/route.py +++ b/artifacts-corepy/artifacts_corepy/apps/package/route.py @@ -24,6 +24,7 @@ def __call__(self, req, resp, repository): resp.set_header('Content-Disposition', stream.headers.get('Content-Disposition')) resp.set_header('Content-Type', stream.headers.get('Content-Type')) + class EntityAdapter(object): def __call__(self, req, resp, package_name, entity_name, action_name): server = CONF.wecube.server @@ -63,6 +64,36 @@ def __call__(self, req, resp, package_name, entity_name, action_name): }) +class RoleAdapter(object): + def __call__(self, req, resp, action_name): + server = CONF.wecube.server + token = req.auth_token + client = wecube.WeCubeClient(server, token) + if action_name == 'roles': + data = client.get('/platform/v1/roles/retrieve') + resp.json = {'code': 200, 'status': 'OK', 'data': data['data'], 'message': 'success'} + else: + raise exceptions.NotFoundError( + _('%(action_name)s for role not supported') % { + 'action_name': action_name, + }) + + +class UserAdapter(object): + def __call__(self, req, resp, action_name): + server = CONF.wecube.server + token = req.auth_token + client = wecube.WeCubeClient(server, token) + if action_name == 'roles': + data = client.get('/platform/v1/users/roles') + resp.json = {'code': 200, 'status': 'OK', 'data': data['data'], 'message': 'success'} + else: + raise exceptions.NotFoundError( + _('%(action_name)s for user not supported') % { + 'action_name': action_name, + }) + + def add_routes(api): # process api.add_route('/artifacts/process/definitions', controller.CollectionProcessDef()) @@ -85,6 +116,14 @@ def add_routes(api): EntityAdapter(), r'/artifacts/platform/v1/packages/(?P[-_A-Za-z0-9]+)/entities/(?P[-_A-Za-z0-9]+)/(?P[-_A-Za-z0-9]+)' ) + api.add_sink( + RoleAdapter(), + r'/artifacts/platform/v1/roles/(?P[-_A-Za-z0-9]+)' + ) + api.add_sink( + UserAdapter(), + r'/artifacts/platform/v1/users/(?P[-_A-Za-z0-9]+)' + ) # nexus query api.add_route('/artifacts/unit-designs/{unit_design_id}/packages/queryNexusDirectiry', controller.CollectionUnitDesignNexusPackages()) @@ -120,13 +159,13 @@ def add_routes(api): # packages exist in remote nexus but not in cmdb api.add_route('/artifacts/entities/packages/query', controller.CollectionOnlyInRemoteNexusPackages()) - + # compose package api.add_route('/artifacts/packages/{deploy_package_id}/download', controller.DownloadComposePackage()) api.add_route('/artifacts/unit-designs/{unit_design_id}/packages/{deploy_package_id}/push', controller.PushComposePackage()) - + # system config api.add_route('/artifacts/sysconfig', controller.SystemConfig()) From 2dfe55851bd9e4823caca087e54ee22860897081 Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Wed, 6 Nov 2024 20:46:17 +0800 Subject: [PATCH 032/108] feat: add /artifacts/platform/v1/users/roles /artifacts/platform/v1/roles/retrieve --- artifacts-corepy/artifacts_corepy/apps/package/route.py | 2 +- artifacts-corepy/artifacts_corepy/apps/variable/api.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/artifacts-corepy/artifacts_corepy/apps/package/route.py b/artifacts-corepy/artifacts_corepy/apps/package/route.py index c5015710..9e205349 100644 --- a/artifacts-corepy/artifacts_corepy/apps/package/route.py +++ b/artifacts-corepy/artifacts_corepy/apps/package/route.py @@ -69,7 +69,7 @@ def __call__(self, req, resp, action_name): server = CONF.wecube.server token = req.auth_token client = wecube.WeCubeClient(server, token) - if action_name == 'roles': + if action_name == 'retrieve': data = client.get('/platform/v1/roles/retrieve') resp.json = {'code': 200, 'status': 'OK', 'data': data['data'], 'message': 'success'} else: diff --git a/artifacts-corepy/artifacts_corepy/apps/variable/api.py b/artifacts-corepy/artifacts_corepy/apps/variable/api.py index aad0d540..ebc86c2c 100644 --- a/artifacts-corepy/artifacts_corepy/apps/variable/api.py +++ b/artifacts-corepy/artifacts_corepy/apps/variable/api.py @@ -30,9 +30,9 @@ def _addtional_list(self, query, filters): """权限控制,角色数据过滤""" query = super()._addtional_list(query, filters) permission_filters = {"roles.role": {'in': list(GLOBALS.request.auth_permissions)}} + # print(f'permission_filters: {permission_filters}') query = self._apply_filters(query, self.orm_meta, permission_filters) - # print(str(query)) - # print(query.statement.compile(), query.statement.compile().params) + # print(str(query), query.statement.compile(), query.statement.compile().params) return query From e215b87fda0accf68fb6b8eab32dc98081dd24e8 Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Wed, 6 Nov 2024 20:49:43 +0800 Subject: [PATCH 033/108] feat: add /artifacts/platform/v1/users/roles /artifacts/platform/v1/roles/retrieve --- init.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/init.sql b/init.sql index 859adbdc..db0c2ab5 100644 --- a/init.sql +++ b/init.sql @@ -1,4 +1,4 @@ -#@v0.1.0.1-begin@; +#@v1.1.12.1-begin@; CREATE TABLE diff_conf_template ( id BIGINT PRIMARY KEY AUTO_INCREMENT, type VARCHAR(16) NOT NULL COMMENT '类型:应用-app,数据库-db', @@ -24,6 +24,6 @@ CREATE TABLE diff_conf_template_role ( ON DELETE CASCADE, INDEX (diff_conf_template_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -#@v0.1.0.1-end@; +#@v1.1.12.1-end@; From cd61ea0f9283d770171b5f8acbcc8148f119d46c Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Wed, 6 Nov 2024 20:50:05 +0800 Subject: [PATCH 034/108] feat: add /artifacts/platform/v1/users/roles /artifacts/platform/v1/roles/retrieve --- init.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/init.sql b/init.sql index db0c2ab5..164423f7 100644 --- a/init.sql +++ b/init.sql @@ -1,4 +1,4 @@ -#@v1.1.12.1-begin@; +#@v1.1.12.2-begin@; CREATE TABLE diff_conf_template ( id BIGINT PRIMARY KEY AUTO_INCREMENT, type VARCHAR(16) NOT NULL COMMENT '类型:应用-app,数据库-db', @@ -24,6 +24,6 @@ CREATE TABLE diff_conf_template_role ( ON DELETE CASCADE, INDEX (diff_conf_template_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -#@v1.1.12.1-end@; +#@v1.1.12.2-end@; From 296b533841e279c4dabec3938150e588b3929e6d Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Wed, 6 Nov 2024 20:50:17 +0800 Subject: [PATCH 035/108] feat: add /artifacts/platform/v1/users/roles /artifacts/platform/v1/roles/retrieve --- init.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/init.sql b/init.sql index 164423f7..2cb49da5 100644 --- a/init.sql +++ b/init.sql @@ -1,4 +1,4 @@ -#@v1.1.12.2-begin@; +#@v1.1.13.2-begin@; CREATE TABLE diff_conf_template ( id BIGINT PRIMARY KEY AUTO_INCREMENT, type VARCHAR(16) NOT NULL COMMENT '类型:应用-app,数据库-db', @@ -24,6 +24,6 @@ CREATE TABLE diff_conf_template_role ( ON DELETE CASCADE, INDEX (diff_conf_template_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -#@v1.1.12.2-end@; +#@v1.1.13.2-end@; From 8e7d51caad21d8570d4176e9b169d6012d7420f0 Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Thu, 7 Nov 2024 16:16:16 +0800 Subject: [PATCH 036/108] feat: add db config --- artifacts-corepy/etc/artifacts_corepy.conf | 4 ++-- artifacts-corepy/requirements.txt | 1 + init.sql | 8 ++++---- register.xml | 3 ++- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/artifacts-corepy/etc/artifacts_corepy.conf b/artifacts-corepy/etc/artifacts_corepy.conf index f227498d..437e6a32 100644 --- a/artifacts-corepy/etc/artifacts_corepy.conf +++ b/artifacts-corepy/etc/artifacts_corepy.conf @@ -74,11 +74,11 @@ ] }, "db": { - "connection": "", + "connection": "mysql+pymysql://${db_username}:${db_password}@${db_hostip}:${db_hostport}/${db_schema}", "pool_size": 3, "pool_recycle": 3600, "pool_timeout": 5, - "max_overflow": 5 + "max_overflow": 60 }, "application": { "names": [ diff --git a/artifacts-corepy/requirements.txt b/artifacts-corepy/requirements.txt index d6df43f0..7ae0e21f 100644 --- a/artifacts-corepy/requirements.txt +++ b/artifacts-corepy/requirements.txt @@ -14,3 +14,4 @@ apscheduler==3.10.4 pytz==2023.3.post1 # for platform login encryption, apt install swig M2Crypto==0.40.1 +pymysql \ No newline at end of file diff --git a/init.sql b/init.sql index 2cb49da5..94b02fd7 100644 --- a/init.sql +++ b/init.sql @@ -1,7 +1,7 @@ -#@v1.1.13.2-begin@; +#@v1.1.13.3-begin@; CREATE TABLE diff_conf_template ( id BIGINT PRIMARY KEY AUTO_INCREMENT, - type VARCHAR(16) NOT NULL COMMENT '类型:应用-app,数据库-db', + type VARCHAR(16) NOT NULL COMMENT '类型:app/db/xx', code VARCHAR(36) NOT NULL COMMENT '编码', value TEXT NULL COMMENT '文本值', description VARCHAR(128) DEFAULT '' COMMENT '描述', @@ -15,7 +15,7 @@ CREATE TABLE diff_conf_template ( CREATE TABLE diff_conf_template_role ( id BIGINT PRIMARY KEY AUTO_INCREMENT, - permission VARCHAR(16) NOT NULL COMMENT '权限:MGMT,USE', + permission VARCHAR(16) NOT NULL COMMENT '权限:MGMT/USE/xx', role VARCHAR(64) NOT NULL COMMENT '角色', diff_conf_template_id BIGINT, CONSTRAINT fk_diff_conf_template @@ -24,6 +24,6 @@ CREATE TABLE diff_conf_template_role ( ON DELETE CASCADE, INDEX (diff_conf_template_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -#@v1.1.13.2-end@; +#@v1.1.13.3-end@; diff --git a/register.xml b/register.xml index bad92f6a..4f8bed37 100644 --- a/register.xml +++ b/register.xml @@ -75,7 +75,8 @@ - + + From fcfbe23539fac39545094332cdea07e4ebeadc88 Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Thu, 7 Nov 2024 16:44:05 +0800 Subject: [PATCH 037/108] feat: add db config --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 95995776..f9c6f07b 100644 --- a/Makefile +++ b/Makefile @@ -27,6 +27,7 @@ package_py: image_py mkdir -p package cd package && docker save $(project_name):$(version) -o image.tar cd package && cp ../register.xml . + cd package && cp ../init.sql ./init.sql cd package && sed -i "s~{{REPOSITORY}}~$(project_name)~g" register.xml cd package && sed -i "s~{{VERSION}}~$(version)~g" register.xml cd artifacts-ui/dist && zip -r ui.zip . From 563c854818ae12b17c432ba946f1614016dc9d2d Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Thu, 7 Nov 2024 16:46:00 +0800 Subject: [PATCH 038/108] feat: add db config --- init.sql | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/init.sql b/init.sql index 94b02fd7..453745b2 100644 --- a/init.sql +++ b/init.sql @@ -1,4 +1,10 @@ -#@v1.1.13.3-begin@; +SET FOREIGN_KEY_CHECKS = 0; +SET NAMES utf8; + +-- +-- Table structure initialize +-- + CREATE TABLE diff_conf_template ( id BIGINT PRIMARY KEY AUTO_INCREMENT, type VARCHAR(16) NOT NULL COMMENT '类型:app/db/xx', @@ -24,6 +30,5 @@ CREATE TABLE diff_conf_template_role ( ON DELETE CASCADE, INDEX (diff_conf_template_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -#@v1.1.13.3-end@; From 52902761d27c94cf7489ef6238c700e88889d66b Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Thu, 7 Nov 2024 17:02:27 +0800 Subject: [PATCH 039/108] feat: add db config --- artifacts-corepy/artifacts_corepy/server/base.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/artifacts-corepy/artifacts_corepy/server/base.py b/artifacts-corepy/artifacts_corepy/server/base.py index 1e66d975..5064e000 100644 --- a/artifacts-corepy/artifacts_corepy/server/base.py +++ b/artifacts-corepy/artifacts_corepy/server/base.py @@ -28,7 +28,8 @@ def decrypt_rsa(secret_key, encrypt_text): return text.decode('utf-8') -@config.intercept('upload_enabled', 'upload_nexus_enabled', 'ci_typeid_system_design', 'ci_typeid_unit_design', +@config.intercept('db_username', 'db_hostip', 'db_hostport', 'db_schema', + 'upload_enabled', 'upload_nexus_enabled', 'ci_typeid_system_design', 'ci_typeid_unit_design', 'ci_typeid_diff_config', 'ci_typeid_deploy_package', 'encrypt_variable_prefix', 'file_variable_prefix', 'default_special_replace', 'artifact_field', 's3_access_key', 's3_secret_key', 'nexus_server', 'nexus_repository', 'nexus_username', 'nexus_password', 'local_nexus_server', From fe938cb928da07af9331c39ab3ce1ef1d3b43ff1 Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Thu, 7 Nov 2024 17:15:52 +0800 Subject: [PATCH 040/108] feat: add db config --- .../artifacts_corepy/server/base.py | 21 +++++++++++++++++++ artifacts-corepy/etc/artifacts_corepy.conf | 5 +++++ 2 files changed, 26 insertions(+) diff --git a/artifacts-corepy/artifacts_corepy/server/base.py b/artifacts-corepy/artifacts_corepy/server/base.py index 5064e000..aa414405 100644 --- a/artifacts-corepy/artifacts_corepy/server/base.py +++ b/artifacts-corepy/artifacts_corepy/server/base.py @@ -14,6 +14,7 @@ from Crypto.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5 from Crypto.PublicKey import RSA from talos.core import config +from urllib.parse import quote_plus from artifacts_corepy.common import utils as plugin_utils @@ -56,3 +57,23 @@ def get_env_value(value, origin_value): raise ValueError('keys with "RSA@", but rsa_key file not exists') return new_value return value + + +@config.intercept('db_password') +def get_env_value(value, origin_value): + prefix = 'ENV@' + encrypt_prefix = 'RSA@' + if value.startswith(prefix): + env_name = value[len(prefix):] + new_value = os.getenv(env_name, default='') + if new_value.startswith(encrypt_prefix): + certs_path = RSA_KEY_PATH + if os.path.exists(certs_path) and os.path.isfile(certs_path): + with open(certs_path) as f: + new_value = decrypt_rsa(f.read(), new_value[len(encrypt_prefix):]) + else: + raise ValueError('keys with "RSA@", but rsa_key file not exists') + new_value = quote_plus(new_value) + return new_value + value = quote_plus(value) + return value diff --git a/artifacts-corepy/etc/artifacts_corepy.conf b/artifacts-corepy/etc/artifacts_corepy.conf index 437e6a32..8d532475 100644 --- a/artifacts-corepy/etc/artifacts_corepy.conf +++ b/artifacts-corepy/etc/artifacts_corepy.conf @@ -10,6 +10,11 @@ "port": 9000 }, "variables": { + "db_username": "ENV@ARTIFACTS_DB_USERNAME", + "db_password": "ENV@ARTIFACTS_DB_PASSWORD", + "db_hostip": "ENV@ARTIFACTS_DB_HOSTIP", + "db_hostport": "ENV@ARTIFACTS_DB_HOSTPORT", + "db_schema": "ENV@ARTIFACTS_DB_SCHEMA", "upload_enabled": "ENV@ARTIFACTS_UPLOAD_ENABLED", "upload_nexus_enabled": "ENV@ARTIFACTS_UPLOAD_NEXUS_ENABLED", "ci_typeid_system_design": "ENV@ARTIFACTS_CITYPE_SYSTEM_DESIGN", From 2c2bbaf264bab3881b811e756c49451677382243 Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Thu, 7 Nov 2024 17:41:41 +0800 Subject: [PATCH 041/108] feat: add db config --- artifacts-corepy/artifacts_corepy/common/wecube.py | 4 ++++ init.sql | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/artifacts-corepy/artifacts_corepy/common/wecube.py b/artifacts-corepy/artifacts_corepy/common/wecube.py index a2ac39e9..b8f6df28 100644 --- a/artifacts-corepy/artifacts_corepy/common/wecube.py +++ b/artifacts-corepy/artifacts_corepy/common/wecube.py @@ -62,6 +62,10 @@ def update(self, url_path, data): url = self.server + url_path return self.post(url, data) + def get(self, url_path, param=None): + url = self.server + url_path + return super().get(url, param) + def retrieve(self, url_path, data=None): # op:eq-等于;neq-不等于;in-范围过滤;like-模糊过滤;gt-大于;lt-小于;is - NULL ; isnot - NULL; # { diff --git a/init.sql b/init.sql index 453745b2..22740b4b 100644 --- a/init.sql +++ b/init.sql @@ -12,7 +12,7 @@ CREATE TABLE diff_conf_template ( value TEXT NULL COMMENT '文本值', description VARCHAR(128) DEFAULT '' COMMENT '描述', create_user VARCHAR(36) DEFAULT NULL, - created_time DATETIME DEFAULT CURRENT_TIMESTAMP, + create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_user VARCHAR(36) DEFAULT NULL, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, is_deleted TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0,1', From 7a0c06592cbc3a86c83555f2b502ac39dcd9e3b0 Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Tue, 19 Nov 2024 11:57:10 +0800 Subject: [PATCH 042/108] =?UTF-8?q?refactor(auth):=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E8=BA=AB=E4=BB=BD=E8=AE=A4=E8=AF=81=E4=B8=AD=E7=9A=84=20proxy?= =?UTF-8?q?=20=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../artifacts_corepy/apps/auth/api.py | 51 +++++++------------ .../artifacts_corepy/apps/auth/apiv2.py | 34 ------------- .../artifacts_corepy/apps/auth/controller.py | 14 ----- .../artifacts_corepy/apps/auth/route.py | 2 - 4 files changed, 19 insertions(+), 82 deletions(-) delete mode 100644 artifacts-corepy/artifacts_corepy/apps/auth/apiv2.py diff --git a/artifacts-corepy/artifacts_corepy/apps/auth/api.py b/artifacts-corepy/artifacts_corepy/apps/auth/api.py index 9048cbd9..b5ca2096 100644 --- a/artifacts-corepy/artifacts_corepy/apps/auth/api.py +++ b/artifacts-corepy/artifacts_corepy/apps/auth/api.py @@ -27,41 +27,28 @@ def generate_tokens(self, rid): refresh_token_exp = access_token_iat + CONF.refresh_token_exipres decoded_secret = terminal_utils.b64decode_key(CONF.jwt_signing_key) tokens.append({ - "expiration": - str(current_time + CONF.access_token_exipres * 1000), - "token": - jwt.encode( - { - "sub": rid, - "iat": access_token_iat, - "type": "accessToken", - "clientType": "USER", - "exp": access_token_exp, - "authority": "[" + ','.join([r['id'] for r in roles]) + "]" - }, - decoded_secret, - "HS512", - ).decode(), - "tokenType": - "accessToken" + "expiration": str(current_time + CONF.access_token_exipres * 1000), + "token": jwt.encode({ + "sub": rid, + "iat": access_token_iat, + "type": "accessToken", + "clientType": "USER", + "exp": access_token_exp, + "authority": "[" + ','.join([r['id'] for r in roles]) + "]" + }, decoded_secret, "HS512").decode(), + "tokenType": "accessToken" }) tokens.append({ - "expiration": - str(current_time + CONF.refresh_token_exipres * 1000), - "token": - jwt.encode( - { - "sub": rid, - "iat": access_token_iat, - "type": "refreshToken", - "clientType": "USER", - "exp": refresh_token_exp - }, - decoded_secret, - "HS512", + "expiration": str(current_time + CONF.refresh_token_exipres * 1000), + "token": jwt.encode({ + "sub": rid, + "iat": access_token_iat, + "type": "refreshToken", + "clientType": "USER", + "exp": refresh_token_exp + }, decoded_secret, "HS512", ).decode(), - "tokenType": - "refreshToken" + "tokenType": "refreshToken" }) return tokens diff --git a/artifacts-corepy/artifacts_corepy/apps/auth/apiv2.py b/artifacts-corepy/artifacts_corepy/apps/auth/apiv2.py deleted file mode 100644 index 2f9daf2e..00000000 --- a/artifacts-corepy/artifacts_corepy/apps/auth/apiv2.py +++ /dev/null @@ -1,34 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import - -import logging -from talos.core import config -from talos.utils import scoped_globals - -from artifacts_corepy.common import wecube - -LOG = logging.getLogger(__name__) -CONF = config.CONF - - -class WeCubeResource(object): - def __init__(self, server=None, token=None): - self.server = server or CONF.wecube.server - self.token = token or scoped_globals.GLOBALS.request.auth_token - - @property - def platform(self): - return wecube.WeCubeClient(self.server, self.token) - - def list(self, params): - pass - - -class SysRole(WeCubeResource): - def list(self, params): - return self.platform.get(self.server + '/platform/v1/roles/retrieve') - - -class SysUser(WeCubeResource): - def list(self, params): - return self.platform.get(self.server + '/platform/v1/users/roles') diff --git a/artifacts-corepy/artifacts_corepy/apps/auth/controller.py b/artifacts-corepy/artifacts_corepy/apps/auth/controller.py index 2189d494..97ea8e41 100644 --- a/artifacts-corepy/artifacts_corepy/apps/auth/controller.py +++ b/artifacts-corepy/artifacts_corepy/apps/auth/controller.py @@ -8,20 +8,6 @@ from artifacts_corepy.common.mixin import Controller, CollectionController as Collection, ItemController as Item from artifacts_corepy.apps.auth import api as auth_api -# proxy mode to platform -from artifacts_corepy.apps.auth import apiv2 as proxy_api - - -class ProxyUserRole(Controller): - name = 'auth.proxy-users' - resource = proxy_api.SysUser - allow_methods = ('GET',) - - -class ProxyRole(Controller): - name = 'auth.proxy-roles' - resource = proxy_api.SysRole - class Token(Controller): name = 'auth.token' diff --git a/artifacts-corepy/artifacts_corepy/apps/auth/route.py b/artifacts-corepy/artifacts_corepy/apps/auth/route.py index ac2e1880..80743e44 100644 --- a/artifacts-corepy/artifacts_corepy/apps/auth/route.py +++ b/artifacts-corepy/artifacts_corepy/apps/auth/route.py @@ -18,8 +18,6 @@ def add_routes(api): api.add_route('/artifacts/v1/users/{rid}/menus', controller.UserItemMenu()) api.add_route('/artifacts/v1/users/{rid}/roles', controller.UserItemRole()) api.add_route('/artifacts/v1/roles', controller.Role()) - # api.add_route('/artifacts/v1/users/roles', controller.ProxyUserRole()) - # api.add_route('/artifacts/v1/roles', controller.ProxyRole()) api.add_route('/artifacts/v1/roles/{rid}', controller.RoleItem()) api.add_route('/artifacts/v1/roles/{rid}/menus', controller.RoleItemMenu()) api.add_route('/artifacts/v1/roles/{rid}/users', controller.RoleItemUser()) From 07ad52f15621c4303580b0fc376c5f99530173b6 Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Tue, 19 Nov 2024 19:21:58 +0800 Subject: [PATCH 043/108] =?UTF-8?q?refactor(artifacts-corepy):=20=E8=B0=83?= =?UTF-8?q?=E6=95=B4=E5=8F=98=E9=87=8F=E7=B1=BB=E5=9E=8B=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E5=B1=9E=E6=80=A7,=E6=B7=BB=E5=8A=A0=5Faddtional=5Fcount?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将变量类型字段从 nullable=False 改为 nullable=True- 更新变量类型的有效性验证规则 - 添加数据库迁移脚本以修改变量类型字段属性 --- artifacts-corepy/artifacts_corepy/apps/variable/api.py | 5 ++--- artifacts-corepy/artifacts_corepy/db/models.py | 2 +- artifacts-corepy/artifacts_corepy/db/resource.py | 3 ++- init.sql | 7 +++++++ 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/artifacts-corepy/artifacts_corepy/apps/variable/api.py b/artifacts-corepy/artifacts_corepy/apps/variable/api.py index ebc86c2c..bf107dce 100644 --- a/artifacts-corepy/artifacts_corepy/apps/variable/api.py +++ b/artifacts-corepy/artifacts_corepy/apps/variable/api.py @@ -30,9 +30,8 @@ def _addtional_list(self, query, filters): """权限控制,角色数据过滤""" query = super()._addtional_list(query, filters) permission_filters = {"roles.role": {'in': list(GLOBALS.request.auth_permissions)}} - # print(f'permission_filters: {permission_filters}') query = self._apply_filters(query, self.orm_meta, permission_filters) - # print(str(query), query.statement.compile(), query.statement.compile().params) return query - + def _addtional_count(self, query, filters): + return self._addtional_list(query, filters) diff --git a/artifacts-corepy/artifacts_corepy/db/models.py b/artifacts-corepy/artifacts_corepy/db/models.py index 3089e63d..b92b2c51 100644 --- a/artifacts-corepy/artifacts_corepy/db/models.py +++ b/artifacts-corepy/artifacts_corepy/db/models.py @@ -25,7 +25,7 @@ class DiffConfTemplate(Base, DictBase): ] id = Column(BIGINT, primary_key=True, index=True) - type = Column(String(16), nullable=False, comment='类型:应用-app,数据库-db') + type = Column(String(16), nullable=True, comment='类型:应用-app,数据库-db') code = Column(String(36), nullable=False, comment='编码') value = Column(Text, comment='文本值') description = Column(String(128), server_default=text("''")) diff --git a/artifacts-corepy/artifacts_corepy/db/resource.py b/artifacts-corepy/artifacts_corepy/db/resource.py index cc841bf1..5d2bb6ab 100644 --- a/artifacts-corepy/artifacts_corepy/db/resource.py +++ b/artifacts-corepy/artifacts_corepy/db/resource.py @@ -43,7 +43,8 @@ class DiffConfTemplate(MetaCRUD): _validate = [ crud.ColumnValidator(field='type', - rule=my_validator.validator.InValidator(['app', 'db']), + # rule=my_validator.validator.InValidator(['app', 'db']), + rule=my_validator.LengthValidator(1, 16), validate_on=('create:M', 'update:O')), crud.ColumnValidator(field='code', rule=my_validator.LengthValidator(1, 36), diff --git a/init.sql b/init.sql index 22740b4b..3df6acbc 100644 --- a/init.sql +++ b/init.sql @@ -32,3 +32,10 @@ CREATE TABLE diff_conf_template_role ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +#@v1.2.1.2-begin@; + +alter table diff_conf_template + modify type varchar(16) null comment '类型:应用-app,数据库-db'; + +#@v1.2.1.2-end@; + From 03a472ed4293bbf3331e1d96b49e30f545b9f23c Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Tue, 19 Nov 2024 19:25:54 +0800 Subject: [PATCH 044/108] =?UTF-8?q?refactor(artifacts-corepy):=20=E8=B0=83?= =?UTF-8?q?=E6=95=B4=E5=8F=98=E9=87=8F=E7=B1=BB=E5=9E=8B=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E5=B1=9E=E6=80=A7,=E6=B7=BB=E5=8A=A0=5Faddtional=5Fcount?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- init.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/init.sql b/init.sql index 3df6acbc..672d5959 100644 --- a/init.sql +++ b/init.sql @@ -32,10 +32,10 @@ CREATE TABLE diff_conf_template_role ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -#@v1.2.1.2-begin@; +#@v1.2.1.3-begin@; alter table diff_conf_template modify type varchar(16) null comment '类型:应用-app,数据库-db'; -#@v1.2.1.2-end@; +#@v1.2.1.3-end@; From 143c8bf103259867dc7b6b14c8dd668aaee3d943 Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Wed, 20 Nov 2024 10:44:40 +0800 Subject: [PATCH 045/108] =?UTF-8?q?feat(artifacts-corepy):=20=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=20diff=5Fconf=5Ftemplate=20=E8=A1=A8=E7=BB=93?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- artifacts-corepy/artifacts_corepy/db/models.py | 8 ++++---- artifacts-corepy/artifacts_corepy/db/resource.py | 2 +- init.sql | 7 +++++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/artifacts-corepy/artifacts_corepy/db/models.py b/artifacts-corepy/artifacts_corepy/db/models.py index b92b2c51..4909eefb 100644 --- a/artifacts-corepy/artifacts_corepy/db/models.py +++ b/artifacts-corepy/artifacts_corepy/db/models.py @@ -15,9 +15,9 @@ class DiffConfTemplate(Base, DictBase): __tablename__ = 'diff_conf_template' - __table_args__ = ( - UniqueConstraint('type', 'code', name='uk_type_code'), - ) + # __table_args__ = ( + # UniqueConstraint('type', 'code', name='uk_type_code'), + # ) attributes = [ 'id', 'type', 'code', 'value', 'description', 'create_user', 'create_time', 'update_user', 'update_time', @@ -26,7 +26,7 @@ class DiffConfTemplate(Base, DictBase): id = Column(BIGINT, primary_key=True, index=True) type = Column(String(16), nullable=True, comment='类型:应用-app,数据库-db') - code = Column(String(36), nullable=False, comment='编码') + code = Column(String(36), nullable=False, unique=True, comment='编码') value = Column(Text, comment='文本值') description = Column(String(128), server_default=text("''")) create_user = Column(String(36)) diff --git a/artifacts-corepy/artifacts_corepy/db/resource.py b/artifacts-corepy/artifacts_corepy/db/resource.py index 5d2bb6ab..a9e32cb4 100644 --- a/artifacts-corepy/artifacts_corepy/db/resource.py +++ b/artifacts-corepy/artifacts_corepy/db/resource.py @@ -44,7 +44,7 @@ class DiffConfTemplate(MetaCRUD): _validate = [ crud.ColumnValidator(field='type', # rule=my_validator.validator.InValidator(['app', 'db']), - rule=my_validator.LengthValidator(1, 16), + rule=my_validator.LengthValidator(0, 16), validate_on=('create:M', 'update:O')), crud.ColumnValidator(field='code', rule=my_validator.LengthValidator(1, 36), diff --git a/init.sql b/init.sql index 672d5959..e30821e3 100644 --- a/init.sql +++ b/init.sql @@ -33,9 +33,12 @@ CREATE TABLE diff_conf_template_role ( #@v1.2.1.3-begin@; - alter table diff_conf_template modify type varchar(16) null comment '类型:应用-app,数据库-db'; - #@v1.2.1.3-end@; + +#@v1.2.1.4-begin@; +alter table diff_conf_template add constraint uniq_code unique (code); +#@v1.2.1.4-end@; + From 187f3eb7b750f081e344120e259f79f35698bb3a Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Wed, 20 Nov 2024 15:28:25 +0800 Subject: [PATCH 046/108] =?UTF-8?q?feat(artifacts-corepy):=20=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E5=B7=AE=E5=BC=82=E9=85=8D=E7=BD=AE=E6=A8=A1=E6=9D=BF?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加模板角色权限更新逻辑 - 增加模板列表和详情的 is_owner 字段 - 修改模板代码的唯一性约束,考虑 is_deleted 状态 - 优化模板类型字段的校验规则 - 添加更新用户的校验规则 --- .../artifacts_corepy/apps/variable/api.py | 55 +++++++++++++++++++ .../artifacts_corepy/db/models.py | 6 +- .../artifacts_corepy/db/resource.py | 5 +- init.sql | 2 +- 4 files changed, 62 insertions(+), 6 deletions(-) diff --git a/artifacts-corepy/artifacts_corepy/apps/variable/api.py b/artifacts-corepy/artifacts_corepy/apps/variable/api.py index bf107dce..7427d874 100644 --- a/artifacts-corepy/artifacts_corepy/apps/variable/api.py +++ b/artifacts-corepy/artifacts_corepy/apps/variable/api.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import +import collections import logging from talos.core import config from talos.utils.scoped_globals import GLOBALS @@ -26,6 +27,41 @@ def _addtional_create(self, session, data, created): 'permission': perm }) + def _addtional_update(self, session, rid, data, before_updated, after_updated): + if 'roles' in data: + current_roles = collections.defaultdict(set) + for r in after_updated["roles"]: + current_roles[r['permission']].add(r['role']) + + for perm, perm_roles in data['roles'].items(): + deleted_perm_roles = current_roles[perm] - set(perm_roles) + new_perm_roles = set(perm_roles) - current_roles[perm] + + for perm_role in new_perm_roles: + if not perm_role: + continue + resource.DiffConfTemplateRole(transaction=session).create({ + 'diff_conf_template_id': after_updated['id'], + 'role': perm_role, + 'permission': perm + }) + + if deleted_perm_roles: + resource.DiffConfTemplateRole(transaction=session).delete_all(filters={ + 'diff_conf_template_id': before_updated['id'], + 'permission': perm, + 'role': { + 'in': list(deleted_perm_roles) + } + }) + + # update final roles + after_updated["roles"] = [ + {"permission": perm, "role": perm_role} + for perm, perm_roles in data['roles'].items() + for perm_role in perm_roles + ] + def _addtional_list(self, query, filters): """权限控制,角色数据过滤""" query = super()._addtional_list(query, filters) @@ -35,3 +71,22 @@ def _addtional_list(self, query, filters): def _addtional_count(self, query, filters): return self._addtional_list(query, filters) + + def list(self, filters=None, orders=None, offset=None, limit=None, hooks=None): + """ + 补充 is_owner + """ + results = super().list(filters, orders, offset, limit, hooks) + for result in results: + result['is_owner'] = GLOBALS.request.auth_user == result['create_user'] + return results + + def get(self, rid): + """ + 补充 is_owner + """ + result = super().get(rid) + if result: + result['is_owner'] = GLOBALS.request.auth_user == result['create_user'] + + return result diff --git a/artifacts-corepy/artifacts_corepy/db/models.py b/artifacts-corepy/artifacts_corepy/db/models.py index 4909eefb..b043801c 100644 --- a/artifacts-corepy/artifacts_corepy/db/models.py +++ b/artifacts-corepy/artifacts_corepy/db/models.py @@ -15,9 +15,9 @@ class DiffConfTemplate(Base, DictBase): __tablename__ = 'diff_conf_template' - # __table_args__ = ( - # UniqueConstraint('type', 'code', name='uk_type_code'), - # ) + __table_args__ = ( + UniqueConstraint('code', 'is_deleted', name='uk_is_deleted_code'), + ) attributes = [ 'id', 'type', 'code', 'value', 'description', 'create_user', 'create_time', 'update_user', 'update_time', diff --git a/artifacts-corepy/artifacts_corepy/db/resource.py b/artifacts-corepy/artifacts_corepy/db/resource.py index a9e32cb4..31cbba70 100644 --- a/artifacts-corepy/artifacts_corepy/db/resource.py +++ b/artifacts-corepy/artifacts_corepy/db/resource.py @@ -45,7 +45,7 @@ class DiffConfTemplate(MetaCRUD): crud.ColumnValidator(field='type', # rule=my_validator.validator.InValidator(['app', 'db']), rule=my_validator.LengthValidator(0, 16), - validate_on=('create:M', 'update:O')), + validate_on=('create:O', 'update:O')), crud.ColumnValidator(field='code', rule=my_validator.LengthValidator(1, 36), validate_on=('create:M', 'update:M'), @@ -55,9 +55,10 @@ class DiffConfTemplate(MetaCRUD): validate_on=('create:M', 'update:M')), crud.ColumnValidator(field='description', rule=my_validator.LengthValidator(0, 128), - validate_on=('create:O', 'update:O'), + validate_on=('*:O',), nullable=True), crud.ColumnValidator(field='create_user', validate_on=('*:O',), nullable=True), + crud.ColumnValidator(field='update_user', validate_on=('*:O',), nullable=True), crud.ColumnValidator(field='roles', rule=validator.TypeValidator(dict), validate_on=('create:M', 'update:O'), diff --git a/init.sql b/init.sql index e30821e3..725e31b9 100644 --- a/init.sql +++ b/init.sql @@ -39,6 +39,6 @@ alter table diff_conf_template #@v1.2.1.4-begin@; -alter table diff_conf_template add constraint uniq_code unique (code); +alter table diff_conf_template add constraint uk_is_deleted_code unique (code, is_deleted); #@v1.2.1.4-end@; From 67a77be5153ea6b68711475a10d4c3393069eaa8 Mon Sep 17 00:00:00 2001 From: gaohongsong Date: Wed, 20 Nov 2024 15:29:08 +0800 Subject: [PATCH 047/108] =?UTF-8?q?feat(artifacts-corepy):=20=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E5=B7=AE=E5=BC=82=E9=85=8D=E7=BD=AE=E6=A8=A1=E6=9D=BF?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加模板角色权限更新逻辑 - 增加模板列表和详情的 is_owner 字段 - 修改模板代码的唯一性约束,考虑 is_deleted 状态 - 优化模板类型字段的校验规则 - 添加更新用户的校验规则 --- init.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/init.sql b/init.sql index 725e31b9..4b358579 100644 --- a/init.sql +++ b/init.sql @@ -38,7 +38,7 @@ alter table diff_conf_template #@v1.2.1.3-end@; -#@v1.2.1.4-begin@; +#@v1.2.1.5-begin@; alter table diff_conf_template add constraint uk_is_deleted_code unique (code, is_deleted); -#@v1.2.1.4-end@; +#@v1.2.1.5-end@; From 31877e6818a0fde68b7472b91dbd94d3996e05ac Mon Sep 17 00:00:00 2001 From: pobu168 <244202969@qq.com> Date: Thu, 21 Nov 2024 17:35:41 +0800 Subject: [PATCH 048/108] add: template mgmt --- artifacts-ui/src/api/server.js | 14 +- artifacts-ui/src/components/auth.vue | 146 ++++++++++ .../src/components/pkg-diff-variable.vue | 254 ++++++++++++++++-- artifacts-ui/src/locale/i18n/en-US.json | 14 +- artifacts-ui/src/locale/i18n/zh-CN.json | 14 +- 5 files changed, 412 insertions(+), 30 deletions(-) create mode 100644 artifacts-ui/src/components/auth.vue diff --git a/artifacts-ui/src/api/server.js b/artifacts-ui/src/api/server.js index 7c289c5c..70f3cc81 100644 --- a/artifacts-ui/src/api/server.js +++ b/artifacts-ui/src/api/server.js @@ -6,7 +6,8 @@ if (window.request) { post: (url, ...params) => pluginErrorMessage(window.request.post(baseURL + url, ...params)), get: (url, ...params) => pluginErrorMessage(window.request.get(baseURL + url, ...params)), delete: (url, ...params) => pluginErrorMessage(window.request.delete(baseURL + url, ...params)), - put: (url, ...params) => pluginErrorMessage(window.request.put(baseURL + url, ...params)) + put: (url, ...params) => pluginErrorMessage(window.request.put(baseURL + url, ...params)), + patch: (url, ...params) => pluginErrorMessage(window.request.put(baseURL + url, ...params)) } } @@ -67,3 +68,14 @@ export const getFlowLists = guid => req.get(`/process/definitions?rootEntityGuid export const getPkgTypeNum = unitDesignId => req.post(`/unit-designs/${unitDesignId}/packages/statistics`, {}) export const getUserList = guid => req.get(`/users`) +// 获取所有角色 +export const getRoleList = params => req.get('/platform/v1/roles/retrieve', { params }) +// 获取当前用户角色 +export const getCurrentUserRoles = () => req.get('/platform/v1/users/roles') +// 保存模版 +export const saveTemplate = data => req.post(`/api/v1/diff-conf-templates`, data) +export const updateTemplate = (data, id) => req.patch(`/api/v1/diff-conf-templates/${id}`, data) + +// 获取模版列表 +export const getTemplate = queryString => req.get(`/api/v1/diff-conf-templates?${queryString}`) +export const deleteTemplate = id => req.delete(`/api/v1/diff-conf-templates/${id}`) diff --git a/artifacts-ui/src/components/auth.vue b/artifacts-ui/src/components/auth.vue new file mode 100644 index 00000000..43f92aad --- /dev/null +++ b/artifacts-ui/src/components/auth.vue @@ -0,0 +1,146 @@ + + + diff --git a/artifacts-ui/src/components/pkg-diff-variable.vue b/artifacts-ui/src/components/pkg-diff-variable.vue index 19566969..ab0c0f8c 100644 --- a/artifacts-ui/src/components/pkg-diff-variable.vue +++ b/artifacts-ui/src/components/pkg-diff-variable.vue @@ -60,6 +60,7 @@ +

{{ $t('art_copy_exist') }} @@ -68,7 +69,7 @@

- +
+ +
+ +
+
@@ -101,26 +123,24 @@
- - - - + {{ customInputs }} +
+ diff --git a/artifacts-ui/src/locale/i18n/en-US.json b/artifacts-ui/src/locale/i18n/en-US.json index d2cb113e..22c28089 100755 --- a/artifacts-ui/src/locale/i18n/en-US.json +++ b/artifacts-ui/src/locale/i18n/en-US.json @@ -151,5 +151,17 @@ "art_no_template": "Template not yet configured.", "art_variable_name": "Variable Name", "art_value_rule": "Value Rule", - "art_creator": "Creator" + "art_creator": "Creator", + "art_name": "Name", + "selected_role": "Selected Role", + "unselected_role": "Unselected Role", + "role_drawer_title": "Select Roles", + "edit_config_role": "Select Configs Owner Roles And Use Roles", + "no_permission_to_mgmt": "You Don't Have Permission", + "mgmt_role": "Owner Role", + "mgmt_role_warning": "Owner Role Cannot Empty", + "use_role": "Use Role", + "art_permissions": "Permissions", + "art_save_as_template": "Save Template", + "art_create_time": "Create Time" } diff --git a/artifacts-ui/src/locale/i18n/zh-CN.json b/artifacts-ui/src/locale/i18n/zh-CN.json index e959c4e9..2ec42d76 100755 --- a/artifacts-ui/src/locale/i18n/zh-CN.json +++ b/artifacts-ui/src/locale/i18n/zh-CN.json @@ -151,5 +151,17 @@ "art_no_template": "尚未配置模版", "art_variable_name": "变量名", "art_value_rule": "取值规则", - "art_creator": "创建人" + "art_creator": "创建人", + "art_name": "名称", + "selected_role": "选中角色", + "unselected_role": "未选中角色", + "role_drawer_title": "选择角色", + "edit_config_role": "选择属主角色和使用角色", + "mgmt_role": "属主角色", + "mgmt_role_warning": "属主角色不能为空", + "no_permission_to_mgmt": "你没有操作权限", + "use_role": "使用角色", + "art_permissions": "权限", + "art_save_as_template": "存为模版", + "art_create_time": "创建时间" } From 9bc5a000fc1eccaf36afb88c01e958e66e824a12 Mon Sep 17 00:00:00 2001 From: pobu168 <244202969@qq.com> Date: Mon, 25 Nov 2024 10:16:21 +0800 Subject: [PATCH 049/108] add: variable test --- artifacts-ui/src/components/auth.vue | 80 ++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 4 deletions(-) diff --git a/artifacts-ui/src/components/auth.vue b/artifacts-ui/src/components/auth.vue index 43f92aad..985efff3 100644 --- a/artifacts-ui/src/components/auth.vue +++ b/artifacts-ui/src/components/auth.vue @@ -76,9 +76,18 @@ export default { async saveAsTemplate () { this.templateParams.roles.MGMT = this.mgmtRolesKeyToFlow this.templateParams.roles.USE = this.useRolesKeyToFlow - console.log(this.templateParams) - const method = this.isAdd ? saveTemplate : updateTemplate - let params = this.isAdd ? [this.templateParams] : this.templateParams + let method = null + let params = null + if (this.isAdd) { + method = saveTemplate + this.templateParams.value = this.valueMgmt(this.templateParams.value) + params = [this.templateParams] + } else { + method = updateTemplate + params = this.templateParams + } + // const method = this.isAdd ? saveTemplate : updateTemplate + // let params = this.isAdd ? [this.templateParams] : this.templateParams const { status } = await method(params, this.templateParams.id) if (status === 'OK') { this.$Notice.success({ @@ -112,7 +121,6 @@ export default { }, // 启动入口 async startAuth (mgmtRolesKeyToFlow, useRolesKeyToFlow, diffExpr) { - console.log(1.1, diffExpr) this.isAdd = true this.templateParams.value = diffExpr this.templateParams.code = '' @@ -122,6 +130,70 @@ export default { await this.getCurrentUserRoles() this.flowRoleManageModal = true }, + // 启动入口 + valueMgmt (diffExpr) { + console.log(1.1, diffExpr) + const input = diffExpr + const result = this.deepParseJSON(input) + console.log(1.2, result) + // const ss = this.updateFilters(result) + // const result11 = this.stringifyDeepestFirst(ss) + // console.log(1.4, result11) + // return result11 + }, + stringifyDeepestFirst (data) { + // 递归处理对象 + if (Array.isArray(data)) { + // 处理数组:遍历每个元素,并将转换后的值替换原值 + return JSON.stringify(data.map(item => this.stringifyDeepestFirst(item))) + } else if (typeof data === 'object' && data !== null) { + // 处理对象:遍历每个属性,递归转换后赋值 + for (const key in data) { + data[key] = this.stringifyDeepestFirst(data[key]) + } + return JSON.stringify(data) // 转换对象为字符串 + } + return data // 如果是基本类型,直接返回 + }, + deepParseJSON (input) { + if (typeof input === 'string') { + try { + const parsed = JSON.parse(input) + return this.deepParseJSON(parsed) // 继续递归解析 + } catch { + return input // 如果解析出错,返回原值(终止条件) + } + } else if (Array.isArray(input)) { + return input.map(item => this.deepParseJSON(item)) // 递归解析数组 + } else if (typeof input === 'object' && input !== null) { + for (const key in input) { + input[key] = this.deepParseJSON(input[key]) // 递归解析对象字段 + } + return input + } + return input + }, + updateFilters (input) { + if (Array.isArray(input)) { + // 遍历数组中的每个元素 + return input.map(item => this.updateFilters(item)) + } else if (typeof input === 'object' && input !== null) { + // 如果对象包含 filters 数组 + if (Array.isArray(input.filters)) { + input.filters = input.filters.map(filter => { + if (filter.type === 'value') { + filter.value = 'testF' // 修改 value 字段值为 'testF' + } + return filter + }) + } + // 递归处理对象的其他字段 + for (const key in input) { + input[key] = this.updateFilters(input[key]) + } + } + return input + }, async editAuth (row) { this.isAdd = false this.templateParams = row From a14d5faee1b5177dc0638a42cf7d592cd29f7feb Mon Sep 17 00:00:00 2001 From: pobu168 <244202969@qq.com> Date: Tue, 26 Nov 2024 10:15:41 +0800 Subject: [PATCH 050/108] update: use template --- artifacts-ui/src/components/pkg-diff-variable.vue | 3 +++ 1 file changed, 3 insertions(+) diff --git a/artifacts-ui/src/components/pkg-diff-variable.vue b/artifacts-ui/src/components/pkg-diff-variable.vue index ab0c0f8c..da9d9966 100644 --- a/artifacts-ui/src/components/pkg-diff-variable.vue +++ b/artifacts-ui/src/components/pkg-diff-variable.vue @@ -1090,6 +1090,9 @@ export default { this.closeUseTemplateModal() if (this.customInputs.length > 0) { this.isShowCiConfigModal = true + } else { + // 直接替换 + this.setCIConfigRowValue() } }, closeUseTemplateModal () { From c0460e966860ddfeaa4b51eba51eedb7b882d283 Mon Sep 17 00:00:00 2001 From: pobu168 <244202969@qq.com> Date: Tue, 26 Nov 2024 14:18:42 +0800 Subject: [PATCH 051/108] update: diff variable template mgmt --- .../{auth.vue => diff-variable-template.vue} | 72 ++++++++++++++++--- .../src/components/pkg-diff-variable.vue | 13 ++-- 2 files changed, 67 insertions(+), 18 deletions(-) rename artifacts-ui/src/components/{auth.vue => diff-variable-template.vue} (67%) diff --git a/artifacts-ui/src/components/auth.vue b/artifacts-ui/src/components/diff-variable-template.vue similarity index 67% rename from artifacts-ui/src/components/auth.vue rename to artifacts-ui/src/components/diff-variable-template.vue index 985efff3..571abe95 100644 --- a/artifacts-ui/src/components/auth.vue +++ b/artifacts-ui/src/components/diff-variable-template.vue @@ -48,7 +48,8 @@ export default { MGMT: [], USE: [] } - } + }, + key: '' // 当前编辑行的属性名 } }, computed: { @@ -86,8 +87,6 @@ export default { method = updateTemplate params = this.templateParams } - // const method = this.isAdd ? saveTemplate : updateTemplate - // let params = this.isAdd ? [this.templateParams] : this.templateParams const { status } = await method(params, this.templateParams.id) if (status === 'OK') { this.$Notice.success({ @@ -120,9 +119,10 @@ export default { } }, // 启动入口 - async startAuth (mgmtRolesKeyToFlow, useRolesKeyToFlow, diffExpr) { + async startAuth (mgmtRolesKeyToFlow, useRolesKeyToFlow, diffExpr, key) { this.isAdd = true this.templateParams.value = diffExpr + this.key = key this.templateParams.code = '' this.mgmtRolesKeyToFlow = mgmtRolesKeyToFlow this.useRolesKeyToFlow = useRolesKeyToFlow @@ -132,14 +132,64 @@ export default { }, // 启动入口 valueMgmt (diffExpr) { - console.log(1.1, diffExpr) - const input = diffExpr + let input = diffExpr + // const input = `[{"type":"rule","value":"[{\\"ciTypeId\\":\\"app_instance\\",\\"filters\\":[{\\"name\\":\\"create_user\\",\\"operator\\":\\"eq\\",\\"type\\":\\"value\\",\\"value\\":\\"test\\"},{\\"name\\":\\"code\\",\\"operator\\":\\"eq\\",\\"type\\":\\"autoFill\\",\\"value\\":\\"[{\\\\\\"type\\\\\\":\\\\\\"rule\\\\\\",\\\\\\"value\\\\\\":\\\\\\"[{\\\\\\\\\\\\\\"ciTypeId\\\\\\\\\\\\\\":\\\\\\\\\\\\\\"app_instance\\\\\\\\\\\\\\",\\\\\\\\\\\\\\"filters\\\\\\\\\\\\\\":[{\\\\\\\\\\\\\\"name\\\\\\\\\\\\\\":\\\\\\\\\\\\\\"asset_id\\\\\\\\\\\\\\",\\\\\\\\\\\\\\"operator\\\\\\\\\\\\\\":\\\\\\\\\\\\\\"eq\\\\\\\\\\\\\\",\\\\\\\\\\\\\\"type\\\\\\\\\\\\\\":\\\\\\\\\\\\\\"value\\\\\\\\\\\\\\",\\\\\\\\\\\\\\"value\\\\\\\\\\\\\\":\\\\\\\\\\\\\\"123\\\\\\\\\\\\\\"}]},{\\\\\\\\\\\\\\"ciTypeId\\\\\\\\\\\\\\":\\\\\\\\\\\\\\"app_instance\\\\\\\\\\\\\\",\\\\\\\\\\\\\\"parentRs\\\\\\\\\\\\\\":{\\\\\\\\\\\\\\"attrId\\\\\\\\\\\\\\":\\\\\\\\\\\\\\"app_instance__create_user\\\\\\\\\\\\\\",\\\\\\\\\\\\\\"isReferedFromParent\\\\\\\\\\\\\\":1}}]\\\\\\"}]\\"}]},{\\"ciTypeId\\":\\"app_instance\\",\\"parentRs\\":{\\"attrId\\":\\"app_instance__port\\",\\"isReferedFromParent\\":1}}]"}]` + // 将表达式转成json const result = this.deepParseJSON(input) - console.log(1.2, result) - // const ss = this.updateFilters(result) - // const result11 = this.stringifyDeepestFirst(ss) - // console.log(1.4, result11) - // return result11 + // 找出 type: 'value' 的 JSON 对象中的 value 值 + const res = this.extractValueByType(result) + // 在表达式中找出以"+value值开始,以"结束的字符串,中间可能包含多个转义符(\) + let targetStr = [] + res.forEach(r => { + const pattern = new RegExp(`"${r}(?:\\\\)*?"`, 'g') // 动态创建正则表达式 + const matches = input.match(pattern) + targetStr = targetStr.concat(matches) + }) + targetStr.forEach((f, fIndex) => { + // 为待替换字符串增加参数标识 + let replaceStr = '' + if (f.startsWith(`"${this.key}`)) { + replaceStr = f.replace(this.key, `!&dd!&`) + } else { + replaceStr = f.replace(res[fIndex], `$^${'params' + fIndex}$^`) + } + // 替换表达式中的目标字符串 + input = input.replaceAll(f, replaceStr) + }) + return input + }, + + extractValueByType (data, targetType = 'value') { + const result = [] // 存储所有匹配的 value 值 + function traverse (node) { + if (Array.isArray(node)) { + node.forEach(traverse) // 遍历数组中的每个元素 + } else if (node && typeof node === 'object') { + if (node.type === targetType && node.hasOwnProperty('value')) { + result.push(node.value) // 提取匹配的 value 值 + } + // 遍历对象中的所有属性 + Object.values(node).forEach(traverse) + } + } + traverse(data) + return result + }, + stringifyFilters (obj) { + if (Array.isArray(obj)) { + return obj.map(item => this.stringifyFilters(item)) // 递归处理数组中的每个元素 + } else if (typeof obj === 'object' && obj !== null) { + const newObj = {} + for (const key in obj) { + if (key === 'filters') { + newObj[key] = JSON.stringify(obj[key].map(this.stringifyFilters)) // 处理 filters 字段并转为字符串 + } else { + newObj[key] = this.stringifyFilters(obj[key]) // 递归处理其他字段 + } + } + return newObj + } + return obj // 基本类型直接返回 }, stringifyDeepestFirst (data) { // 递归处理对象 diff --git a/artifacts-ui/src/components/pkg-diff-variable.vue b/artifacts-ui/src/components/pkg-diff-variable.vue index da9d9966..c52b6106 100644 --- a/artifacts-ui/src/components/pkg-diff-variable.vue +++ b/artifacts-ui/src/components/pkg-diff-variable.vue @@ -123,13 +123,12 @@
- {{ customInputs }}
- + @@ -140,7 +139,7 @@ import axios from 'axios' import { decode } from 'js-base64' import RuleTable from './rule-table.vue' import { debounce } from 'lodash' -import TemplateAuth from '@/components/auth' +import DiffVariableTemplate from '@/components/diff-variable-template' // 业务运行实例ciTypeId const defaultAppRootCiTypeId = 'app_instance' const defaultDBRootCiTypeId = 'rdb_instance' @@ -1116,7 +1115,7 @@ export default { this.packageDetail[tmp].forEach(elFile => { elFile.configKeyInfos.forEach(elFileVar => { if (this.currentConfigRow.key.toLowerCase() === elFileVar.key.toLowerCase()) { - let resultStr = currentConfigValueCodeTovalue.replaceAll(/\$&(\w)*\$&/g, elFileVar.key) + let resultStr = currentConfigValueCodeTovalue.replaceAll(/!&(\w)*!&/g, elFileVar.key) this.customInputs.forEach(item => { resultStr = resultStr.replaceAll(item.origin, item.value) }) @@ -1191,11 +1190,11 @@ export default { }, saveAsTemplate (row) { - this.$refs.templateAuthRef.startAuth([], [], row.conf_variable.diffExpr) + this.$refs.diffVariableTemplateRef.startAuth([], [], row.conf_variable.diffExpr, row.key) }, // 模版授权 templateAuth (row) { - this.$refs.templateAuthRef.editAuth(row) + this.$refs.diffVariableTemplateRef.editAuth(row) }, // 模版删除 templateDelete (row) { @@ -1234,7 +1233,7 @@ export default { }, components: { RuleTable, - TemplateAuth + DiffVariableTemplate } } From 45c7f57465e1c830112e25b98045a39f707d43dd Mon Sep 17 00:00:00 2001 From: pobu168 <244202969@qq.com> Date: Thu, 28 Nov 2024 15:04:53 +0800 Subject: [PATCH 052/108] update: remove table high row --- artifacts-ui/src/components/simple-table.vue | 2 +- artifacts-ui/src/views/artifacts.vue | 36 +++++++++++++++----- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/artifacts-ui/src/components/simple-table.vue b/artifacts-ui/src/components/simple-table.vue index d45260ac..23c75aa9 100644 --- a/artifacts-ui/src/components/simple-table.vue +++ b/artifacts-ui/src/components/simple-table.vue @@ -1,6 +1,6 @@