Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add JustLeaveAppManager for better experience #1472

Merged
merged 2 commits into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@
import time
from typing import Union

from django.conf import settings
from iam.exceptions import AuthAPIError
from rest_framework.exceptions import PermissionDenied
from rest_framework.permissions import BasePermission

from paasng.infras.iam.constants import PERM_EXEMPT_TIME_FOR_OWNER_AFTER_CREATE_APP
from paasng.infras.iam.helpers import user_group_apply_url
from paasng.infras.iam.permissions.resources.application import AppAction, ApplicationPermission, AppPermCtx
from paasng.platform.applications.models import Application
Expand Down Expand Up @@ -71,7 +71,7 @@ def can_exempt_application_perm(user, application: Application) -> bool:
# 因此需要在应用创建后的一定的时间内,对创建者(拥有应用最高权限)的操作进行权限豁免以保证功能可正常使用
return (
user.pk == application.owner
and time.time() - application.created.timestamp() < PERM_EXEMPT_TIME_FOR_OWNER_AFTER_CREATE_APP
and time.time() - application.created.timestamp() < settings.IAM_PERM_EFFECTIVE_TIMEDELTA
)


Expand Down
5 changes: 0 additions & 5 deletions apiserver/paasng/paasng/infras/iam/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,3 @@ class IAMErrorCodes(int, StructuredEnum):
],
},
}


# 由于权限中心的用户组授权为异步行为,即创建用户组,添加用户,对组授权后需要等待一段时间(10-20秒左右)才能鉴权
# 因此需要在应用创建后的一定的时间内,对创建者(拥有应用最高权限)的操作进行权限豁免以保证功能可正常使用
PERM_EXEMPT_TIME_FOR_OWNER_AFTER_CREATE_APP = 5 * 60
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from django.utils.translation import gettext_lazy as _
from iam.exceptions import AuthAPIError

from paasng.infras.iam.constants import PERM_EXEMPT_TIME_FOR_OWNER_AFTER_CREATE_APP, ResourceType
from paasng.infras.iam.constants import ResourceType
from paasng.infras.iam.permissions.perm import PermCtx, Permission, ResCreatorAction, validate_empty
from paasng.infras.iam.permissions.request import ResourceRequest

Expand Down Expand Up @@ -224,7 +224,7 @@ def _gen_app_filters_by_request(self, request):
# 因此在应用创建后的短时间内,需特殊豁免以免在列表页无法查询到最新的应用
perm_exempt_filter = Q(
owner=user_id_encoder.encode(settings.USER_TYPE, request.subject.id),
created__gt=datetime.now() - timedelta(seconds=PERM_EXEMPT_TIME_FOR_OWNER_AFTER_CREATE_APP),
created__gt=datetime.now() - timedelta(seconds=settings.IAM_PERM_EFFECTIVE_TIMEDELTA),
)
if not filters:
return perm_exempt_filter
Expand Down
42 changes: 41 additions & 1 deletion apiserver/paasng/paasng/platform/applications/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import os
import time
import uuid
from typing import TYPE_CHECKING, Dict, List, Optional, Union
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Union

from bkstorages.backends.bkrepo import RequestError
from django.conf import settings
Expand All @@ -29,6 +29,7 @@
from pilkit.processors import ResizeToFill

from paasng.core.core.storages.object_storage import app_logo_storage
from paasng.core.core.storages.redisdb import get_default_redis
from paasng.core.region.models import get_region
from paasng.infras.iam.helpers import fetch_role_members
from paasng.infras.iam.permissions.resources.application import ApplicationPermission
Expand Down Expand Up @@ -195,6 +196,39 @@ def process_order_by(order_by: List[str], queryset: QuerySet) -> QuerySet:
return queryset.order_by(*fields)


class JustLeaveAppManager:
"""
刚退出的应用管理器

Q:为什么需要有这个管理器
A:开发者中心接入权限中心后,由于接入的是 RBAC 模型,导致有这么一个链路
用户退出权限中心用户组 ----异步任务----> 回收用户权限
这样会出现一个问题:用户退出应用后,短时间(30s)内还有这个应用的权限,体验不佳
这里的思路是:利用 Redis,缓存用户退出的应用 Code(5min),这段时间内,把这个应用 exclude 掉

Q:为什么所有方法都加 try-except
A:这个 manager 只是优化体验,如果 redis 挂了(虽然概率不大),也不该阻塞主流程
"""

def __init__(self, username: str):
self.redis_db = get_default_redis()
self.username = username
self.cache_key = f"bkpaas_just_leave_app_codes:{username}"

def add(self, app_code: str) -> None:
try:
self.redis_db.rpush(self.cache_key, app_code)
self.redis_db.expire(self.cache_key, settings.IAM_PERM_EFFECTIVE_TIMEDELTA)
except Exception:
pass

def list(self) -> Set[str]:
try:
return {x.decode() for x in self.redis_db.lrange(self.cache_key, 0, -1)}
except Exception:
return set()


class UserApplicationFilter:
"""List user applications"""

Expand All @@ -216,6 +250,12 @@ def filter(
if order_by is None:
order_by = []
applications = Application.objects.filter_by_user(self.user.pk, exclude_collaborated=exclude_collaborated)

# 从缓存拿刚刚退出的应用 code exclude 掉,避免出现退出用户组,权限中心权限未同步的情况
mgr = JustLeaveAppManager(get_username_by_bkpaas_user_id(self.user.pk))
if just_leave_app_codes := mgr.list():
applications = applications.exclude(code__in=just_leave_app_codes)

return BaseApplicationFilter.filter_queryset(
applications,
include_inactive=include_inactive,
Expand Down
7 changes: 7 additions & 0 deletions apiserver/paasng/paasng/platform/applications/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
from paasng.platform.applications.models import (
Application,
ApplicationEnvironment,
JustLeaveAppManager,
UserApplicationFilter,
UserMarkedApplication,
)
Expand Down Expand Up @@ -822,6 +823,9 @@ def leave(self, request, *args, **kwargs):
except BKIAMGatewayServiceError as e:
raise error_codes.DELETE_APP_MEMBERS_ERROR.f(e.message)

# 将该应用 Code 标记为刚退出,避免出现退出用户组,权限中心权限未同步的情况
JustLeaveAppManager(request.user.username).add(application.code)

sync_developers_to_sentry.delay(application.id)
application_member_updated.send(sender=application, application=application)
return Response(status=status.HTTP_204_NO_CONTENT)
Expand All @@ -837,6 +841,9 @@ def destroy(self, request, *args, **kwargs):
except BKIAMGatewayServiceError as e:
raise error_codes.DELETE_APP_MEMBERS_ERROR.f(e.message)

# 将该应用 Code 标记为刚退出,避免出现退出用户组,权限中心权限未同步的情况
JustLeaveAppManager(username).add(application.code)

sync_developers_to_sentry.delay(application.id)
application_member_updated.send(sender=application, application=application)
return Response(status=status.HTTP_204_NO_CONTENT)
Expand Down
7 changes: 6 additions & 1 deletion apiserver/paasng/paasng/platform/evaluation/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from paasng.infras.iam.permissions.resources.application import ApplicationPermission
from paasng.misc.operations.models import Operation
from paasng.platform.applications.constants import ApplicationRole, ApplicationType
from paasng.platform.applications.models import Application
from paasng.platform.applications.models import Application, JustLeaveAppManager
from paasng.platform.engine.constants import AppEnvName
from paasng.platform.engine.models import Deployment
from paasng.platform.evaluation.collectors import AppDeploymentCollector, AppResQuotaCollector, AppUserVisitCollector
Expand Down Expand Up @@ -194,6 +194,11 @@ def send_idle_email_to_app_developers(app_codes: List[str], only_specified_users
for idx, username in enumerate(waiting_notify_usernames):
filters = ApplicationPermission().gen_develop_app_filters(username)
app_codes = Application.objects.filter(filters).values_list("code", flat=True)

# 从缓存拿刚刚退出的应用 code exclude 掉,避免出现退出用户组,权限中心权限未同步的情况
if just_leave_app_codes := JustLeaveAppManager(username).list():
app_codes = [c for c in app_codes if c not in just_leave_app_codes]

user_idle_app_reports = reports.filter(app__code__in=app_codes)

if not user_idle_app_reports.exists():
Expand Down
6 changes: 6 additions & 0 deletions apiserver/paasng/paasng/settings/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,12 @@ def _build_file_handler(log_path: Path, filename: str, format: str) -> Dict:
# 跳过初始化已有应用数据到权限中心(注意:仅跳过初始化数据,所有权限相关的操作还是依赖权限中心)
BK_IAM_SKIP = settings.get("BK_IAM_SKIP", False)

# IAM 权限生效时间(单位:秒)
# 权限中心的用户组授权是异步行为,即创建用户组,添加用户,对组授权后需要等待一段时间(10-20秒左右)才能鉴权
# 因此需要在应用创建后的一定的时间内,对创建者(拥有应用最高权限)的操作进行权限豁免以保证功能可正常使用
# 退出用户组同理,因此在退出的一定时间内,需要先 exclude 掉避免退出后还可以看到应用的问题
IAM_PERM_EFFECTIVE_TIMEDELTA = 5 * 60

BKAUTH_DEFAULT_PROVIDER_TYPE = settings.get("BKAUTH_DEFAULT_PROVIDER_TYPE", "BK")

# 蓝鲸的云 API 地址,用于内置环境变量的配置项
Expand Down