fix: scope user organization labels to the current tenant.
This commit is contained in:
1 parent
02d1e16036
commit
81bda0cb79
3 files changed
+221
-7
No files matched your search
@@ -156,13 +156,19 @@ def get_user(db: Session, user_id: str) -> Optional[SysUser]:
|
||||
)
|
||||
|
||||
|
||||
def list_post_ids_for_user(db: Session, user_id: str) -> List[str]:
|
||||
def list_post_ids_for_user(
|
||||
db: Session, user_id: str, tenant_no: Optional[str] = None
|
||||
) -> List[str]:
|
||||
"""用户当前有效职位 ID 列表。"""
|
||||
query = db.query(SysUserPost).filter(
|
||||
and_(SysUserPost.user_id == user_id, SysUserPost.del_flag == 0)
|
||||
)
|
||||
tenant = (tenant_no or "").strip()
|
||||
if tenant:
|
||||
query = query.filter(SysUserPost.tenant_no == tenant)
|
||||
return [
|
||||
r.post_id
|
||||
for r in db.query(SysUserPost)
|
||||
.filter(and_(SysUserPost.user_id == user_id, SysUserPost.del_flag == 0))
|
||||
.all()
|
||||
for r in query.all()
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -32,23 +32,35 @@ def _avatar_url_field(user: SysUser) -> Optional[str]:
|
||||
|
||||
def resolve_user_org_labels(db: Session, user: SysUser) -> Tuple[Optional[str], List[str]]:
|
||||
"""解析用户所属部门名称与职位名称列表(保序)。"""
|
||||
tenant_no = (getattr(user, "tenant_no", None) or "").strip()
|
||||
dept_name: Optional[str] = None
|
||||
dept_id = (getattr(user, "dept_id", None) or "").strip()
|
||||
if dept_id:
|
||||
dept = (
|
||||
db.query(SysDept)
|
||||
.filter(SysDept.id == dept_id, SysDept.del_flag == 0)
|
||||
.filter(
|
||||
SysDept.id == dept_id,
|
||||
SysDept.tenant_no == tenant_no,
|
||||
SysDept.del_flag == 0,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if dept and (dept.dept_name or "").strip():
|
||||
dept_name = str(dept.dept_name).strip()
|
||||
|
||||
post_names: List[str] = []
|
||||
post_ids = list_post_ids_for_user(db, user.id)
|
||||
post_ids = list_post_ids_for_user(
|
||||
db, user.id, tenant_no=tenant_no
|
||||
)
|
||||
if post_ids:
|
||||
posts = (
|
||||
db.query(SysPost)
|
||||
.filter(SysPost.id.in_(post_ids), SysPost.del_flag == 0)
|
||||
.filter(
|
||||
SysPost.id.in_(post_ids),
|
||||
SysPost.tenant_no == tenant_no,
|
||||
SysPost.status == "1",
|
||||
SysPost.del_flag == 0,
|
||||
)
|
||||
.all()
|
||||
)
|
||||
name_map = {
|
||||
|
||||
@@ -584,3 +584,199 @@ def test_public_auth_entrypoints_pass_the_users_tenant_to_role_queries(
|
||||
"names": [("u1", "tenant-b")] * 2,
|
||||
"menus": [("u1", "tenant-b")],
|
||||
}
|
||||
|
||||
|
||||
def test_org_labels_are_tenant_scoped_in_auth_profile_and_token_payloads(
|
||||
monkeypatch,
|
||||
):
|
||||
from app.api import public
|
||||
from app.core.config import settings
|
||||
from app.crud.user import list_post_ids_for_user
|
||||
from app.schemas.auth import ProfileUpdateRequest
|
||||
from app.services import issue_user_tokens
|
||||
|
||||
db = _session()
|
||||
_add_auth_user(db)
|
||||
_add_role_menu(
|
||||
db,
|
||||
tenant_no="tenant-b",
|
||||
role_id="tenant-b-role",
|
||||
menu_id="tenant-b-menu",
|
||||
perm_code="mini:market:specialist:presign",
|
||||
role_name="市场专员",
|
||||
data_scope="4",
|
||||
)
|
||||
user = db.query(SysUser).filter_by(id="u1").one()
|
||||
user.dept_id = "default-dept"
|
||||
db.add_all(
|
||||
[
|
||||
SysUserRole(
|
||||
id="tenant-b-role-link",
|
||||
user_id="u1",
|
||||
role_id="tenant-b-role",
|
||||
tenant_no="tenant-b",
|
||||
create_dept_id="",
|
||||
del_flag=0,
|
||||
),
|
||||
SysDept(
|
||||
id="default-dept",
|
||||
dept_name="默认租户部门",
|
||||
tenant_no=settings.DEFAULT_TENANT_NO,
|
||||
create_dept_id="",
|
||||
del_flag=0,
|
||||
),
|
||||
SysDept(
|
||||
id="tenant-b-dept",
|
||||
dept_name="租户B部门",
|
||||
tenant_no="tenant-b",
|
||||
create_dept_id="",
|
||||
del_flag=0,
|
||||
),
|
||||
SysDept(
|
||||
id="tenant-b-deleted-dept",
|
||||
dept_name="租户B已删除部门",
|
||||
tenant_no="tenant-b",
|
||||
create_dept_id="",
|
||||
del_flag=1,
|
||||
),
|
||||
SysPost(
|
||||
id="default-post",
|
||||
post_name="默认租户职位",
|
||||
post_code="default-post",
|
||||
status="1",
|
||||
tenant_no=settings.DEFAULT_TENANT_NO,
|
||||
create_dept_id="",
|
||||
del_flag=0,
|
||||
),
|
||||
SysPost(
|
||||
id="tenant-b-post",
|
||||
post_name="租户B有效职位",
|
||||
post_code="tenant-b-post",
|
||||
status="1",
|
||||
tenant_no="tenant-b",
|
||||
create_dept_id="",
|
||||
del_flag=0,
|
||||
),
|
||||
SysPost(
|
||||
id="tenant-b-disabled-post",
|
||||
post_name="租户B停用职位",
|
||||
post_code="tenant-b-disabled-post",
|
||||
status="0",
|
||||
tenant_no="tenant-b",
|
||||
create_dept_id="",
|
||||
del_flag=0,
|
||||
),
|
||||
SysPost(
|
||||
id="tenant-b-deleted-post",
|
||||
post_name="租户B已删除职位",
|
||||
post_code="tenant-b-deleted-post",
|
||||
status="1",
|
||||
tenant_no="tenant-b",
|
||||
create_dept_id="",
|
||||
del_flag=1,
|
||||
),
|
||||
SysPost(
|
||||
id="tenant-b-unbound-post",
|
||||
post_name="租户B已解绑职位",
|
||||
post_code="tenant-b-unbound-post",
|
||||
status="1",
|
||||
tenant_no="tenant-b",
|
||||
create_dept_id="",
|
||||
del_flag=0,
|
||||
),
|
||||
SysUserPost(
|
||||
id="default-user-post",
|
||||
user_id="u1",
|
||||
post_id="default-post",
|
||||
tenant_no=settings.DEFAULT_TENANT_NO,
|
||||
create_dept_id="",
|
||||
del_flag=0,
|
||||
),
|
||||
SysUserPost(
|
||||
id="tenant-b-user-post",
|
||||
user_id="u1",
|
||||
post_id="tenant-b-post",
|
||||
tenant_no="tenant-b",
|
||||
create_dept_id="",
|
||||
del_flag=0,
|
||||
),
|
||||
SysUserPost(
|
||||
id="tenant-b-disabled-user-post",
|
||||
user_id="u1",
|
||||
post_id="tenant-b-disabled-post",
|
||||
tenant_no="tenant-b",
|
||||
create_dept_id="",
|
||||
del_flag=0,
|
||||
),
|
||||
SysUserPost(
|
||||
id="tenant-b-deleted-user-post",
|
||||
user_id="u1",
|
||||
post_id="tenant-b-deleted-post",
|
||||
tenant_no="tenant-b",
|
||||
create_dept_id="",
|
||||
del_flag=0,
|
||||
),
|
||||
SysUserPost(
|
||||
id="tenant-b-unbound-user-post",
|
||||
user_id="u1",
|
||||
post_id="tenant-b-unbound-post",
|
||||
tenant_no="tenant-b",
|
||||
create_dept_id="",
|
||||
del_flag=1,
|
||||
),
|
||||
]
|
||||
)
|
||||
db.commit()
|
||||
|
||||
dept_name, post_names = issue_user_tokens.resolve_user_org_labels(db, user)
|
||||
assert dept_name is None
|
||||
assert post_names == ["租户B有效职位"]
|
||||
assert set(list_post_ids_for_user(db, "u1")) == {
|
||||
"default-post",
|
||||
"tenant-b-post",
|
||||
"tenant-b-disabled-post",
|
||||
"tenant-b-deleted-post",
|
||||
}
|
||||
assert set(list_post_ids_for_user(db, "u1", tenant_no="tenant-b")) == {
|
||||
"tenant-b-post",
|
||||
"tenant-b-disabled-post",
|
||||
"tenant-b-deleted-post",
|
||||
}
|
||||
|
||||
user.dept_id = "tenant-b-deleted-dept"
|
||||
db.commit()
|
||||
dept_name, post_names = issue_user_tokens.resolve_user_org_labels(db, user)
|
||||
assert dept_name is None
|
||||
assert post_names == ["租户B有效职位"]
|
||||
|
||||
user.dept_id = "tenant-b-dept"
|
||||
db.commit()
|
||||
authenticated = _authenticate(db, monkeypatch)
|
||||
assert authenticated["dept_name"] == "租户B部门"
|
||||
assert authenticated["post_names"] == ["租户B有效职位"]
|
||||
|
||||
async def no_op(*args, **kwargs):
|
||||
return None
|
||||
|
||||
monkeypatch.setattr(issue_user_tokens, "store_session", no_op)
|
||||
monkeypatch.setattr(issue_user_tokens, "store_refresh_token", no_op)
|
||||
issued = asyncio.run(issue_user_tokens.issue_tokens_for_user(db, user))
|
||||
assert issued["userInfo"]["deptName"] == "租户B部门"
|
||||
assert issued["userInfo"]["postNames"] == ["租户B有效职位"]
|
||||
|
||||
current_user = {"user_id": "u1", "tenant_no": "tenant-b"}
|
||||
|
||||
async def resolve_current(request, db_arg):
|
||||
return current_user
|
||||
|
||||
monkeypatch.setattr(public, "_resolve_current_user_from_request", resolve_current)
|
||||
monkeypatch.setattr(public, "update_user", lambda *args, **kwargs: None)
|
||||
profiled = asyncio.run(
|
||||
public.update_current_user_profile(
|
||||
request=None,
|
||||
body=ProfileUpdateRequest(),
|
||||
db=db,
|
||||
)
|
||||
)
|
||||
assert profiled["data"]["deptName"] == "租户B部门"
|
||||
assert profiled["data"]["postNames"] == ["租户B有效职位"]
|
||||
Reference in new issue
Block a user