Skip to content

Commit

Permalink
Merge pull request #2010 from TencentBlueKing/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
zhu327 authored May 29, 2023
2 parents 7e57188 + bd41ecf commit 89efcef
Show file tree
Hide file tree
Showing 18 changed files with 146 additions and 67 deletions.
5 changes: 0 additions & 5 deletions saas/.pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,6 @@ repos:
language: python
pass_filenames: false
entry: pflake8 --config=saas/pyproject.toml saas
- id: bandit
name: bandit
language: python
pass_filenames: false
entry: bandit -c saas/pyproject.toml -r saas
- id: mypy
name: mypy
language: python
Expand Down
2 changes: 1 addition & 1 deletion saas/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.10.2
1.10.3
5 changes: 5 additions & 0 deletions saas/backend/account/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet

from backend.apps.organization.models import User
from backend.apps.role.models import RoleUser
from backend.biz.role import RoleBiz
from backend.common.error_codes import error_codes
Expand All @@ -34,12 +35,16 @@ def retrieve(self, request, *args, **kwargs):
user = request.user
role = request.role
timestamp = int(time.time())

u = User.objects.filter(username=user.username).only("display_name").first()

return Response(
{
"timestamp": timestamp,
"username": user.username,
"role": {"type": role.type, "id": role.id, "name": role.name},
"timezone": user.get_property("time_zone"),
"name": u.display_name,
}
)

Expand Down
2 changes: 1 addition & 1 deletion saas/backend/api/authorization/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class AllowListMatchOperationEnum(ChoicesEnum, LowerStrEnum):
STARTS_WITH = auto()


AllowListObjectOperationSep = ":"
ALLOW_LIST_OBJECT_OPERATION_STEP = ":"


class VerifyApiParamLocationEnum(ChoicesEnum, LowerStrEnum):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
from django.db import migrations

from backend.api.authorization.constants import (
ALLOW_LIST_OBJECT_OPERATION_STEP,
AllowListMatchOperationEnum,
AllowListObjectOperationSep,
AuthorizationAPIEnum,
)

Expand All @@ -18,9 +18,9 @@ def add_allow_list(apps, schema_editor):
# 实例授权API 白名单
system_actions = {
"bk_cmdb": [
"".join([AllowListMatchOperationEnum.STARTS_WITH.value, AllowListObjectOperationSep, "create_comobj"]),
"".join([AllowListMatchOperationEnum.STARTS_WITH.value, AllowListObjectOperationSep, "edit_comobj"]),
"".join([AllowListMatchOperationEnum.STARTS_WITH.value, AllowListObjectOperationSep, "delete_comobj"]),
"".join([AllowListMatchOperationEnum.STARTS_WITH.value, ALLOW_LIST_OBJECT_OPERATION_STEP, "create_comobj"]),
"".join([AllowListMatchOperationEnum.STARTS_WITH.value, ALLOW_LIST_OBJECT_OPERATION_STEP, "edit_comobj"]),
"".join([AllowListMatchOperationEnum.STARTS_WITH.value, ALLOW_LIST_OBJECT_OPERATION_STEP, "delete_comobj"]),
],
}
auth_api_allow_list_config = []
Expand Down
6 changes: 3 additions & 3 deletions saas/backend/api/authorization/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from backend.service.models import Subject
from backend.trans.role import RoleAuthScopeTrans

from .constants import AllowListMatchOperationEnum, AllowListObjectOperationSep, AuthorizationAPIEnum, OperateEnum
from .constants import ALLOW_LIST_OBJECT_OPERATION_STEP, AllowListMatchOperationEnum, AuthorizationAPIEnum, OperateEnum
from .models import AuthAPIAllowListConfig

logger = logging.getLogger("app")
Expand All @@ -39,8 +39,8 @@ def __init__(self, object_id: str):

# 解析object_id,拆分出operation 和 匹配的对象
# 若分隔符在object_id里,说明需要拆分出真正的object_id和operation
if AllowListObjectOperationSep in object_id:
object_split_list = object_id.split(AllowListObjectOperationSep)
if ALLOW_LIST_OBJECT_OPERATION_STEP in object_id:
object_split_list = object_id.split(ALLOW_LIST_OBJECT_OPERATION_STEP)
# 长度非2,则说明非正常的规则,则默认使用等于匹配
if len(object_split_list) == 2:
self.operation = object_split_list[0]
Expand Down
4 changes: 2 additions & 2 deletions saas/backend/api/authorization/views/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def post(self, request, *args, **kwargs):

audit_context_setter(operate=operate, subject=subject, system_id=system_id, policies=policies)

return self.policy_response(policies[0])
return self.policy_response(policies[0] if policies else {})


class AuthPathView(AuthViewMixin, APIView):
Expand Down Expand Up @@ -109,7 +109,7 @@ def post(self, request, *args, **kwargs):

audit_context_setter(operate=operate, subject=subject, system_id=system_id, policies=policies)

return self.policy_response(policies[0])
return self.policy_response(policies[0] if policies else {})


class AuthBatchInstanceView(AuthViewMixin, APIView):
Expand Down
65 changes: 41 additions & 24 deletions saas/backend/api/bkci/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,25 @@
from backend.apps.role.models import RoleRelatedObject
from backend.apps.template.models import PermTemplatePolicyAuthorized
from backend.component.iam import list_all_subject_member
from backend.service.constants import SubjectType
from backend.service.models.policy import Policy
from backend.service.models.subject import Subject
from backend.util.json import json_dumps


def get_user_set():
user_set = set()

queryset = User.objects.filter(staff_status="IN").only("username").order_by("id")
paginator = Paginator(queryset, 1000)

for i in paginator.page_range:
for u in paginator.page(i):
user_set.add(u.username)

return user_set


class BKCIMigrateTask(Task):
name = "backend.api.bkci.tasks.BKCIMigrateTask"

Expand All @@ -57,10 +71,12 @@ def run(self):

role_ids = json.loads(task.role_ids)

user_set = get_user_set()

# create new migrate data
self.handle_group_api_policy(role_ids)
self.handle_group_web_policy(role_ids)
self.handle_user_custom_policy()
self.handle_group_api_policy(role_ids, user_set)
self.handle_group_web_policy(role_ids, user_set)
self.handle_user_custom_policy(user_set)

# update status
task.status = "SUCCESS"
Expand Down Expand Up @@ -89,22 +105,25 @@ def _handle_policy(self, policy: Policy, subject: Subject, project_subject_path_
policy.action_id
)

def handle_user_custom_policy(self):
def handle_user_custom_policy(self, user_set):
project_subject_path_action = defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(set))))

qs = PolicyModel.objects.filter(system_id="bk_ci", subject_type="user").order_by("id")
paginator = Paginator(qs, 100)

for i in paginator.page_range:
for p in paginator.page(i):
if p.subject_id not in user_set:
continue

pb = Policy.from_db_model(p, 0)

subject = Subject(type=p.subject_type, id=p.subject_id)
self._handle_policy(pb, subject, project_subject_path_action)

self.batch_create_migrate_data(project_subject_path_action, "user_custom_policy")
self.batch_create_migrate_data(project_subject_path_action, "user_custom_policy", user_set)

def handle_group_web_policy(self, role_ids):
def handle_group_web_policy(self, role_ids, user_set):
project_subject_path_action = defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(set))))

if role_ids:
Expand Down Expand Up @@ -141,9 +160,9 @@ def handle_group_web_policy(self, role_ids):
for p in policies:
self._handle_policy(p, subject, project_subject_path_action)

self.batch_create_migrate_data(project_subject_path_action, "group_web_policy")
self.batch_create_migrate_data(project_subject_path_action, "group_web_policy", user_set)

def handle_group_api_policy(self, role_ids):
def handle_group_api_policy(self, role_ids, user_set):
if not role_ids:
return

Expand Down Expand Up @@ -185,9 +204,9 @@ def handle_group_api_policy(self, role_ids):
for p in policies:
self._handle_policy(p, subject, project_subject_path_action)

self.batch_create_migrate_data(project_subject_path_action, "group_api_policy")
self.batch_create_migrate_data(project_subject_path_action, "group_api_policy", user_set)

def batch_create_migrate_data(self, project_subject_path_action, handler_type):
def batch_create_migrate_data(self, project_subject_path_action, handler_type, user_set):
for project, subject_path_action in project_subject_path_action.items():
for subject, path_type_action in subject_path_action.items():
subject_dict = subject.dict()
Expand Down Expand Up @@ -216,7 +235,17 @@ def batch_create_migrate_data(self, project_subject_path_action, handler_type):
}

if subject.type == "group":
data["members"] = list_all_subject_member(subject.type, subject.id)
members = list_all_subject_member(subject.type, subject.id)
members = [
one
for one in members
if (one["type"] == SubjectType.USER.value and one["id"] in user_set)
or one["type"] == SubjectType.DEPARTMENT.value
]
if not members:
continue

data["members"] = members

migrate_data = MigrateData(
project_id=project[1],
Expand All @@ -238,26 +267,14 @@ def run(self, id: int):
return

project_ids = json.loads(task.project_ids)
user_set = self.get_user_set()
user_set = get_user_set()
for project_id in project_ids:
self.handle_project(project_id, user_set)

# update status
task.status = "SUCCESS"
task.save(update_fields=["status"])

def get_user_set(self):
user_set = set()

queryset = User.objects.filter(staff_status="IN").only("username").order_by("id")
paginator = Paginator(queryset, 1000)

for i in paginator.page_range:
for u in paginator.page(i):
user_set.add(u.username)

return user_set

def handle_project(self, project_id: str, user_set):
MigrateData.objects.filter(version="v0", project_id=project_id).delete()

Expand Down
2 changes: 1 addition & 1 deletion saas/backend/apps/group/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ def create(self, request, *args, **kwargs):
exists_role_ids.add(group_role.id)
self.role_biz.incr_update_subject_scope(group_role, members)

except Exception as e:
except Exception as e: # pylint: disable=broad-except noqa
failed_info.update({group.name: "{}".format(e)})

else:
Expand Down
45 changes: 37 additions & 8 deletions saas/backend/biz/role.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@
"""
import logging
from collections import defaultdict
from textwrap import dedent
from typing import Dict, List, Optional, Set

from blue_krill.web.std_error import APIError
from django.conf import settings
from django.db import connection
from django.db.models import Q
from django.utils.functional import cached_property
from django.utils.translation import gettext as _
Expand All @@ -23,7 +25,7 @@
from backend.apps.application.models import Application
from backend.apps.group.models import Group
from backend.apps.organization.models import Department, DepartmentMember, User
from backend.apps.role.models import Role, RoleRelatedObject, RoleRelation, RoleSource, RoleUser, ScopeSubject
from backend.apps.role.models import Role, RoleRelatedObject, RoleRelation, RoleSource, RoleUser
from backend.apps.template.models import PermTemplate
from backend.biz.policy import (
ConditionBean,
Expand All @@ -45,7 +47,6 @@
ApplicationStatus,
ApplicationType,
RoleRelatedObjectType,
RoleScopeSubjectType,
RoleSourceType,
RoleType,
SubjectType,
Expand Down Expand Up @@ -568,7 +569,7 @@ def check_grade_manager_of_system_limit(self, system_id: str):
for one_system_limit in split_system_limits:
system_limit = one_system_limit.split(":")
system_limits[system_limit[0].strip()] = int(system_limit[1].strip())
except Exception as error:
except Exception as error: # pylint: disable=broad-except noqa
logger.error(
f"parse grade_manager_of_specified_systems_limit from setting failed, value:{value}, error: {error}"
)
Expand Down Expand Up @@ -730,11 +731,39 @@ def _list_authorization_scope_include_user_role_ids(self):
department_ids = self.user.ancestor_department_ids

# 2. 查询 subjects 相关的分级管理员
grade_mgr_ids = ScopeSubject.objects.filter(
Q(subject_type=RoleScopeSubjectType.DEPARTMENT.value, subject_id__in=department_ids)
| Q(subject_type=RoleScopeSubjectType.USER.value, subject_id=self.user.username) # noqa
| Q(subject_type=SUBJECT_TYPE_ALL, subject_id=SUBJECT_ALL) # noqa
).values_list("role_id", flat=True)
# grade_mgr_ids = ScopeSubject.objects.filter(
# Q(subject_type=RoleScopeSubjectType.DEPARTMENT.value, subject_id__in=department_ids)
# | Q(subject_type=RoleScopeSubjectType.USER.value, subject_id=self.user.username) # noqa
# | Q(subject_type=SUBJECT_TYPE_ALL, subject_id=SUBJECT_ALL) # noqa
# ).values_list("role_id", flat=True)

sql = dedent(
"""SELECT
a.id
FROM
role_role a
LEFT JOIN role_scopesubject b ON a.id = b.role_id
WHERE
a.hidden = 0
AND (
(
b.subject_id IN %s
AND b.subject_type = 'department'
)
OR (
b.subject_id = %s
AND b.`subject_type` = 'user'
)
OR (
b.`subject_id` = '*'
AND b.`subject_type` = '*'
)
)"""
)

with connection.cursor() as cursor:
cursor.execute(sql, (tuple(department_ids), self.user.username))
grade_mgr_ids = [row[0] for row in cursor.fetchall()]

# 3. 查询 超级管理员 + 系统管理员的 ids
super_ids = Role.objects.filter(
Expand Down
3 changes: 2 additions & 1 deletion saas/backend/common/pagination.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
# 目标是统一使用page_size/page参数
# WebAPI: 使用config/default.py里DEFAULT_PAGINATION_CLASS默认配置的CompatiblePagination,后续需要前端配合一起调整为page_size/page参数
# OpenAPI:
# 对于已开放接口admin.list_groups/admin.list_group_member/mgmt.list_group/mgmt.list_group_member使用CompatiblePagination兼容limit/offset和page_size/page
# 对于已开放接口admin.list_groups/admin.list_group_member/mgmt.list_group/mgmt.list_group_member
# 使用CompatiblePagination兼容limit/offset和page_size/page
# 对于OpenAPI新接口,需要ViewSet需要显示配置pagination_class=CustomPageNumberPagination
from collections import OrderedDict

Expand Down
6 changes: 5 additions & 1 deletion saas/backend/component/itsm.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
"""
from typing import Dict, List
from typing import Dict, List, Optional

from .esb import _call_esb_api
from .http import http_get, http_post
Expand Down Expand Up @@ -41,6 +41,7 @@ def create_ticket(
reason: str,
content: Dict,
tag: str = "",
dynamic_fields: Optional[List] = None,
**kwargs,
) -> Dict:
"""获取审批流程,并根据单据创建者判断是否实例化审批节点"""
Expand All @@ -58,6 +59,9 @@ def create_ticket(
],
}

if dynamic_fields:
data["dynamic_fields"] = dynamic_fields

if tag:
data["tag"] = tag # NOTE: 用于ITSM审批单列表api筛选过滤字段

Expand Down
Loading

0 comments on commit 89efcef

Please sign in to comment.