Skip to content

Commit

Permalink
feat: add audit records of application operation (TencentBlueKing#1569)
Browse files Browse the repository at this point in the history
Co-authored-by: schnee <[email protected]>
  • Loading branch information
lyzqs and narasux authored Sep 5, 2024
1 parent 157017e commit abea4c3
Show file tree
Hide file tree
Showing 14 changed files with 523 additions and 105 deletions.
182 changes: 106 additions & 76 deletions apiserver/paasng/locale/en/LC_MESSAGES/django.po

Large diffs are not rendered by default.

44 changes: 44 additions & 0 deletions apiserver/paasng/paas_wl/bk_app/cnative/specs/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@
from paas_wl.workloads.images.models import AppUserCredential
from paasng.infras.accounts.permissions.application import application_perm_class
from paasng.infras.iam.permissions.resources.application import AppAction
from paasng.misc.audit.constants import DataType, OperationEnum, OperationTarget
from paasng.misc.audit.service import DataDetail, add_app_audit_record
from paasng.platform.applications.mixins import ApplicationCodeInPathMixin
from paasng.platform.modules.models import BuildConfig
from paasng.platform.sourcectl.controllers.docker import DockerRegistryController
Expand Down Expand Up @@ -235,13 +237,29 @@ def create(self, request, code, module_name):
slz = MountSLZ(mount_instance)
except GetSourceConfigDataError as e:
raise error_codes.CREATE_VOLUME_MOUNT_FAILED.f(_(e))

# source_config 字段无法被序列化
data_after = MountSLZ(mount_instance).data
del data_after["source_config"]
add_app_audit_record(
app_code=code,
user=request.user.pk,
action_id=AppAction.BASIC_DEVELOP,
operation=OperationEnum.CREATE,
target=OperationTarget.VOLUME_MOUNT,
attribute=mount_instance.name,
module_name=module_name,
data_after=DataDetail(type=DataType.RAW_DATA, data=data_after),
)
return Response(data=slz.data, status=status.HTTP_201_CREATED)

@transaction.atomic
@swagger_auto_schema(responses={200: MountSLZ()}, request_body=UpsertMountSLZ)
def update(self, request, code, module_name, mount_id):
module = self.get_module_via_path()
mount_instance = get_object_or_404(Mount, id=mount_id, module_id=module.id)
data_before = MountSLZ(mount_instance).data
del data_before["source_config"]

slz = UpsertMountSLZ(data=request.data, context={"module_id": module.id, "mount_id": mount_instance.id})
slz.is_valid(raise_exception=True)
Expand Down Expand Up @@ -273,12 +291,28 @@ def update(self, request, code, module_name, mount_id):
slz = MountSLZ(mount_instance)
except GetSourceConfigDataError as e:
raise error_codes.UPDATE_VOLUME_MOUNT_FAILED.f(_(e))

data_after = MountSLZ(mount_instance).data
del data_after["source_config"]
add_app_audit_record(
app_code=code,
user=request.user.pk,
action_id=AppAction.BASIC_DEVELOP,
operation=OperationEnum.MODIFY,
target=OperationTarget.VOLUME_MOUNT,
attribute=mount_instance.name,
module_name=module_name,
data_before=DataDetail(type=DataType.RAW_DATA, data=data_before),
data_after=DataDetail(type=DataType.RAW_DATA, data=data_after),
)
return Response(data=slz.data, status=status.HTTP_200_OK)

@transaction.atomic
def destroy(self, request, code, module_name, mount_id):
module = self.get_module_via_path()
mount_instance = get_object_or_404(Mount, id=mount_id, module_id=module.id)
data_before = MountSLZ(mount_instance).data
del data_before["source_config"]

controller = init_volume_source_controller(mount_instance.source_type)
controller.delete_by_env(
Expand All @@ -289,6 +323,16 @@ def destroy(self, request, code, module_name, mount_id):
)
mount_instance.delete()

add_app_audit_record(
app_code=code,
user=request.user.pk,
action_id=AppAction.BASIC_DEVELOP,
operation=OperationEnum.DELETE,
target=OperationTarget.VOLUME_MOUNT,
attribute=mount_instance.name,
module_name=module_name,
data_before=DataDetail(type=DataType.RAW_DATA, data=data_before),
)
return Response(status=status.HTTP_204_NO_CONTENT)


Expand Down
25 changes: 25 additions & 0 deletions apiserver/paasng/paas_wl/workloads/networking/egress/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
from paas_wl.workloads.networking.egress.serializers import RCStateAppBindingSLZ
from paasng.infras.accounts.permissions.application import application_perm_class
from paasng.infras.iam.permissions.resources.application import AppAction
from paasng.misc.audit.constants import DataType, OperationEnum, OperationTarget
from paasng.misc.audit.service import DataDetail, add_app_audit_record
from paasng.platform.applications.constants import AppFeatureFlag
from paasng.platform.applications.mixins import ApplicationCodeInPathMixin

Expand Down Expand Up @@ -79,6 +81,17 @@ def create(self, request, code, module_name, environment):
raise error_codes.ERROR_ACQUIRING_EGRESS_GATEWAY_INFO.f("请稍候再试")

serializer = RCStateAppBindingSLZ(binding)

add_app_audit_record(
app_code=code,
user=request.user.pk,
action_id=AppAction.BASIC_DEVELOP,
operation=OperationEnum.CREATE,
target=OperationTarget.EXIT_IP,
module_name=module_name,
environment=environment,
data_after=DataDetail(type=DataType.RAW_DATA, data=serializer.data),
)
return Response({"name": "default", "rcs_binding_data": serializer.data}, status=status.HTTP_201_CREATED)

def destroy(self, request, code, module_name, environment):
Expand All @@ -88,5 +101,17 @@ def destroy(self, request, code, module_name, environment):
binding = RCStateAppBinding.objects.get(app=wl_app)
except RCStateAppBinding.DoesNotExist:
raise error_codes.ERROR_RECYCLING_EGRESS_GATEWAY_INFO.f("未获取过网关信息")
data_before = DataDetail(type=DataType.RAW_DATA, data=RCStateAppBindingSLZ(binding).data)
binding.delete()

add_app_audit_record(
app_code=code,
user=request.user.pk,
action_id=AppAction.BASIC_DEVELOP,
operation=OperationEnum.DELETE,
target=OperationTarget.EXIT_IP,
module_name=module_name,
environment=environment,
data_before=data_before,
)
return Response(status=status.HTTP_204_NO_CONTENT)
56 changes: 47 additions & 9 deletions apiserver/paasng/paas_wl/workloads/networking/entrance/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
from paasng.core.region.models import get_region
from paasng.infras.accounts.permissions.application import application_perm_class
from paasng.infras.iam.permissions.resources.application import AppAction
from paasng.misc.audit.constants import DataType, OperationEnum, OperationTarget
from paasng.misc.audit.service import DataDetail, add_app_audit_record
from paasng.platform.applications.mixins import ApplicationCodeInPathMixin
from paasng.platform.modules.constants import ExposedURLType
from paasng.utils.api_docs import openapi_empty_response
Expand Down Expand Up @@ -96,13 +98,24 @@ def create(self, request, **kwargs):

data = validate_domain_payload(request.data, application, serializer_cls=DomainSLZ)
env = application.get_module(data["module"]["name"]).get_envs(data["environment"]["environment"])
instance = get_custom_domain_mgr(application).create(
domain = get_custom_domain_mgr(application).create(
env=env,
host=data["name"],
path_prefix=data["path_prefix"],
https_enabled=data["https_enabled"],
)
return Response(DomainSLZ(instance).data, status=status.HTTP_201_CREATED)

add_app_audit_record(
app_code=application.code,
user=request.user.pk,
action_id=AppAction.BASIC_DEVELOP,
operation=OperationEnum.CREATE,
target=OperationTarget.APP_DOMAIN,
module_name=env.module.name,
environment=env.environment,
data_after=DataDetail(type=DataType.RAW_DATA, data=DomainSLZ(domain).data),
)
return Response(DomainSLZ(domain).data, status=status.HTTP_201_CREATED)

@swagger_auto_schema(
operation_id="update-app-domain",
Expand All @@ -113,29 +126,54 @@ def create(self, request, **kwargs):
def update(self, request, **kwargs):
"""更新一个独立域名的域名与路径信息"""
application = self.get_application()
instance = get_object_or_404(self.get_queryset(), pk=self.kwargs["id"])
domain = get_object_or_404(self.get_queryset(), pk=self.kwargs["id"])
if not self.allow_modifications(application.region):
raise error_codes.UPDATE_CUSTOM_DOMAIN_FAILED.format(
"当前应用版本不允许手动管理独立域名,请联系平台管理员"
)

data = validate_domain_payload(request.data, application, serializer_cls=DomainForUpdateSLZ, instance=instance)
new_instance = get_custom_domain_mgr(application).update(
instance, host=data["name"], path_prefix=data["path_prefix"], https_enabled=data["https_enabled"]
data_before = DataDetail(type=DataType.RAW_DATA, data=DomainSLZ(domain).data)
data = validate_domain_payload(request.data, application, serializer_cls=DomainForUpdateSLZ, instance=domain)
new_domain = get_custom_domain_mgr(application).update(
domain, host=data["name"], path_prefix=data["path_prefix"], https_enabled=data["https_enabled"]
)

add_app_audit_record(
app_code=application.code,
user=request.user.pk,
action_id=AppAction.BASIC_DEVELOP,
operation=OperationEnum.MODIFY,
target=OperationTarget.APP_DOMAIN,
module_name=domain.module.name,
environment=domain.environment,
data_before=data_before,
data_after=DataDetail(type=DataType.RAW_DATA, data=DomainSLZ(new_domain).data),
)
return Response(DomainSLZ(new_instance).data)
return Response(DomainSLZ(new_domain).data)

@swagger_auto_schema(operation_id="delete-app-domain", responses={204: openapi_empty_response}, tags=["Domains"])
def destroy(self, request, *args, **kwargs):
"""通过 ID 删除一个独立域名"""
application = self.get_application()
instance = get_object_or_404(self.get_queryset(), pk=self.kwargs["id"])
domain = get_object_or_404(self.get_queryset(), pk=self.kwargs["id"])
if not self.allow_modifications(application.region):
raise error_codes.DELETE_CUSTOM_DOMAIN_FAILED.format(
"当前应用版本不允许手动管理独立域名,请联系平台管理员"
)

get_custom_domain_mgr(application).delete(instance)
data_before = DataDetail(type=DataType.RAW_DATA, data=DomainSLZ(domain).data)
get_custom_domain_mgr(application).delete(domain)

add_app_audit_record(
app_code=application.code,
user=request.user.pk,
action_id=AppAction.BASIC_DEVELOP,
operation=OperationEnum.DELETE,
target=OperationTarget.APP_DOMAIN,
module_name=domain.module.name,
environment=domain.environment,
data_before=data_before,
)
return Response(status=status.HTTP_204_NO_CONTENT)

@swagger_auto_schema(response_serializer=slzs.CustomDomainsConfigSLZ(many=True), tags=["访问入口"])
Expand Down
26 changes: 26 additions & 0 deletions apiserver/paasng/paasng/accessories/app_secret/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
from paasng.infras.oauth2.api import BkOauthClient
from paasng.infras.oauth2.models import BkAppSecretInEnvVar
from paasng.infras.oauth2.utils import get_app_secret_in_env_var
from paasng.misc.audit.constants import OperationEnum, OperationTarget
from paasng.misc.audit.service import add_app_audit_record
from paasng.platform.applications.constants import ApplicationType
from paasng.platform.applications.mixins import ApplicationCodeInPathMixin
from paasng.platform.applications.models import Application
Expand Down Expand Up @@ -81,6 +83,14 @@ def create(self, request, code):
raise ValidationError(_(f"密钥已达到上限,应用仅允许有 {MAX_SECRET_COUNT} 个密钥"))

client.create_app_secret(code)

add_app_audit_record(
app_code=code,
user=request.user.pk,
action_id=AppAction.BASIC_DEVELOP,
operation=OperationEnum.CREATE,
target=OperationTarget.SECRET,
)
return Response(status=status.HTTP_201_CREATED)

@swagger_auto_schema(tags=["鉴权信息"], request_body=AppSecretStatusSLZ, responses={"204": "没有返回数据"})
Expand All @@ -98,6 +108,14 @@ def toggle(self, request, code, bk_app_secret_id):
raise ValidationError(_("当前密钥为内置密钥,不允许被禁用"))

BkOauthClient().toggle_app_secret(code, bk_app_secret_id, enabled)

add_app_audit_record(
app_code=code,
user=request.user.pk,
action_id=AppAction.BASIC_DEVELOP,
operation=OperationEnum.ENABLE if enabled else OperationEnum.DISABLE,
target=OperationTarget.SECRET,
)
return Response(status=status.HTTP_204_NO_CONTENT)

@swagger_auto_schema(tags=["鉴权信息"], responses={"204": "没有返回数据"})
Expand Down Expand Up @@ -125,6 +143,14 @@ def delete(self, request, code, bk_app_secret_id):
raise ValidationError(_("当前密钥为内置密钥,不允许删除"))

client.del_app_secret(code, bk_app_secret_id)

add_app_audit_record(
app_code=code,
user=request.user.pk,
action_id=AppAction.BASIC_DEVELOP,
operation=OperationEnum.DELETE,
target=OperationTarget.SECRET,
)
return Response(status=status.HTTP_204_NO_CONTENT)

@swagger_auto_schema(tags=["鉴权信息"], request_body=VerificationCodeSLZ)
Expand Down
23 changes: 23 additions & 0 deletions apiserver/paasng/paasng/accessories/servicehub/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@
from paasng.infras.accounts.permissions.constants import SiteAction
from paasng.infras.accounts.permissions.global_site import site_perm_class
from paasng.infras.iam.permissions.resources.application import AppAction
from paasng.misc.audit.constants import DataType, OperationEnum, OperationTarget
from paasng.misc.audit.service import DataDetail, add_app_audit_record
from paasng.misc.metrics import SERVICE_BIND_COUNTER
from paasng.platform.applications.mixins import ApplicationCodeInPathMixin
from paasng.platform.applications.models import Application, UserApplicationFilter
Expand Down Expand Up @@ -152,6 +154,17 @@ def bind(self, request):
"module_name": module.name,
}
)

add_app_audit_record(
app_code=application.code,
user=request.user.pk,
action_id=AppAction.BASIC_DEVELOP,
operation=OperationEnum.ENABLE,
target=OperationTarget.ADD_ON,
attribute=service_obj.name,
module_name=module.name,
data_after=DataDetail(type=DataType.RAW_DATA, data=service_obj.config),
)
return Response(serializer.data)

@app_action_required(AppAction.BASIC_DEVELOP)
Expand Down Expand Up @@ -233,6 +246,16 @@ def unbind(self, request, code, module_name, service_id):
raise error_codes.CANNOT_DESTROY_SERVICE.f(f"{e}")

module_attachment.delete()
add_app_audit_record(
app_code=code,
user=request.user.pk,
action_id=AppAction.MANAGE_ADDONS_SERVICES,
operation=OperationEnum.DISABLE,
target=OperationTarget.ADD_ON,
attribute=module_attachment.service.name,
module_name=module_name,
data_before=DataDetail(type=DataType.RAW_DATA, data=module_attachment.service.config),
)
return Response(status=status.HTTP_200_OK)

@app_action_required(AppAction.BASIC_DEVELOP)
Expand Down
6 changes: 6 additions & 0 deletions apiserver/paasng/paasng/misc/audit/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ class OperationTarget(str, StructuredEnum):
APP_DOMAIN = EnumField("app_domain", label=_("访问地址"))
APP_MEMBER = EnumField("app_member", label=_("应用成员"))
TEMPLATE = EnumField("template", label=_("模板"))
BUILD_CONFIG = EnumField("build_config", label=_("构建配置"))
VOLUME_MOUNT = EnumField("volume_mount", label=_("挂载卷"))
SERVICE_DISCOVERY = EnumField("service_discovery", label=_("服务发现"))
DOMAIN_RESOLUTION = EnumField("domain_resolution", label=_("域名解析"))
DEPLOY_RESTRICTION = EnumField("deploy_restriction", label=_("部署限制"))
EXIT_IP = EnumField("exit_ip", label=_("出口 IP"))


class OperationEnum(str, StructuredEnum):
Expand Down
31 changes: 27 additions & 4 deletions apiserver/paasng/paasng/platform/bkapp_model/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
from paasng.accessories.servicehub.manager import mixed_service_mgr
from paasng.infras.accounts.permissions.application import application_perm_class
from paasng.infras.iam.permissions.resources.application import AppAction
from paasng.misc.audit.constants import DataType, OperationEnum, OperationTarget
from paasng.misc.audit.service import DataDetail, add_app_audit_record
from paasng.platform.applications.mixins import ApplicationCodeInPathMixin
from paasng.platform.bkapp_model.entities import Process
from paasng.platform.bkapp_model.entities_syncer import sync_processes
Expand Down Expand Up @@ -250,14 +252,23 @@ def upsert(self, request, code):
slz.is_valid(raise_exception=True)
validated_data = slz.validated_data

svc_disc, _ = SvcDiscConfig.objects.update_or_create(
svc_disc, created = SvcDiscConfig.objects.update_or_create(
application_id=application.id,
defaults={
"bk_saas": validated_data["bk_saas"],
},
)
svc_disc.refresh_from_db()
return Response(SvcDiscConfigSLZ(svc_disc).data, status=status.HTTP_200_OK)
data = SvcDiscConfigSLZ(svc_disc).data
add_app_audit_record(
app_code=code,
user=request.user.pk,
action_id=AppAction.BASIC_DEVELOP,
operation=OperationEnum.CREATE if created else OperationEnum.MODIFY,
target=OperationTarget.SERVICE_DISCOVERY,
data_after=DataDetail(type=DataType.RAW_DATA, data=data),
)
return Response(data, status=status.HTTP_200_OK)


class DomainResolutionViewSet(viewsets.GenericViewSet, ApplicationCodeInPathMixin):
Expand Down Expand Up @@ -286,7 +297,19 @@ def upsert(self, request, code):
if host_aliases is not None:
defaults["host_aliases"] = host_aliases

domain_resolution, _ = DomainResolution.objects.update_or_create(application=application, defaults=defaults)
domain_resolution, created = DomainResolution.objects.update_or_create(
application=application, defaults=defaults
)

domain_resolution.refresh_from_db()
return Response(DomainResolutionSLZ(domain_resolution).data, status=status.HTTP_200_OK)

data = DomainResolutionSLZ(domain_resolution).data
add_app_audit_record(
app_code=code,
user=request.user.pk,
action_id=AppAction.BASIC_DEVELOP,
operation=OperationEnum.CREATE if created else OperationEnum.MODIFY,
target=OperationTarget.DOMAIN_RESOLUTION,
data_after=DataDetail(type=DataType.RAW_DATA, data=data),
)
return Response(data, status=status.HTTP_200_OK)
Loading

0 comments on commit abea4c3

Please sign in to comment.