fix: lock protected launcher access roles
This commit is contained in:
@@ -1412,6 +1412,14 @@ const accessAssignmentOptions: Array<NodeDcSelectOption<AccessAssignmentValue>>
|
||||
{ value: "deny", label: "Заблокирован", description: "Запрет доступа", tone: "red" },
|
||||
];
|
||||
|
||||
const operationalCoreAccessAssignmentOptions: Array<NodeDcSelectOption<AccessAssignmentValue>> = [
|
||||
{ value: "unset", label: "—", description: "Не назначен" },
|
||||
{ value: "viewer", label: "Гость", description: "Просмотр", tone: "green" },
|
||||
{ value: "member", label: "Участник", description: "Рабочий доступ", tone: "green" },
|
||||
{ value: "admin", label: "Админ", description: "Системная роль", tone: "green", disabled: true, hidden: true },
|
||||
{ value: "deny", label: "Заблокирован", description: "Запрет доступа", tone: "red" },
|
||||
];
|
||||
|
||||
const engineAccessAssignmentOptions: Array<NodeDcSelectOption<AccessAssignmentValue>> = [
|
||||
{ value: "unset", label: "—", description: "Не назначен" },
|
||||
{ value: "viewer", label: "Гость", description: "Только явно открытые workflow", tone: "green" },
|
||||
@@ -1424,12 +1432,12 @@ const publicOperationalCoreAccessOptions: Array<NodeDcSelectOption<AccessAssignm
|
||||
{ value: "unset", label: "—", description: "Не назначен", hidden: true },
|
||||
{ value: "viewer", label: "Workspace Guest", description: "Выдаётся только через Tasker", tone: "green", hidden: true },
|
||||
{ value: "member", label: "Workspace Member", description: "Доступ к приглашённому workspace", tone: "green" },
|
||||
{ value: "admin", label: "Service Admin", description: "Self-service", tone: "green" },
|
||||
{ value: "admin", label: "Service Admin", description: "Системная роль", tone: "green", disabled: true, hidden: true },
|
||||
{ value: "deny", label: "Заблокирован", description: "Запрет доступа", tone: "red" },
|
||||
];
|
||||
|
||||
const operationalCoreRoleOptions: Array<NodeDcSelectOption<OperationalCoreRoleSelectValue>> = [
|
||||
...accessAssignmentOptions,
|
||||
...operationalCoreAccessAssignmentOptions,
|
||||
{ value: "pending", label: "Сохраняем...", disabled: true, hidden: true },
|
||||
];
|
||||
|
||||
@@ -1437,7 +1445,7 @@ const taskManagerProjectRoleOptions: Array<NodeDcSelectOption<OperationalCoreRol
|
||||
{ value: "unset", label: "—", description: "Не назначен" },
|
||||
{ value: "viewer", label: "Гость", description: "Просмотр", tone: "green" },
|
||||
{ value: "member", label: "Участник", description: "Рабочий доступ", tone: "green" },
|
||||
{ value: "admin", label: "Админ", description: "Администрирование", tone: "green" },
|
||||
{ value: "admin", label: "Админ", description: "Системная роль", tone: "green", disabled: true, hidden: true },
|
||||
{ value: "pending", label: "Сохраняем...", disabled: true, hidden: true },
|
||||
];
|
||||
|
||||
@@ -3083,7 +3091,6 @@ function AccessSection({
|
||||
|
||||
const protectedUser = isProtectedLauncherUser(user.id);
|
||||
const protectedFromActor = isProtectedFromCurrentActor(me, user.id);
|
||||
const protectedSelfManage = canSelfManageProtectedUser(me, user.id);
|
||||
const inviterMeta = getMembershipInviterMeta(data, membership);
|
||||
const pendingKey = `${matrix.client.id}:${user.id}:${primaryTaskManagerWorkspace?.slug ?? "primary"}`;
|
||||
const pendingTaskerAssignment = Boolean(pendingTaskManagerMemberships[pendingKey]);
|
||||
@@ -3110,7 +3117,7 @@ function AccessSection({
|
||||
<div className="access-grid-cell" role="cell">
|
||||
<MainRoleControl
|
||||
value={membership.role}
|
||||
protectedUser={protectedFromActor}
|
||||
protectedUser={protectedUser || protectedFromActor}
|
||||
onChange={(role) => onUpdateMembership(membership.id, { role })}
|
||||
/>
|
||||
</div>
|
||||
@@ -3129,14 +3136,16 @@ function AccessSection({
|
||||
pendingValue={pendingAccessAssignments[accessCellKey(user.id, service.id)]}
|
||||
busy={!usePublicTaskerAccess && isTaskManagerService && pendingTaskerAssignment}
|
||||
publicSelfService={usePublicTaskerAccess}
|
||||
readOnly={protectedFromActor}
|
||||
readOnly={protectedUser || protectedFromActor}
|
||||
onSelectCell={onSelectCell}
|
||||
onSetAccess={(value) => {
|
||||
if (protectedUser || protectedFromActor) return;
|
||||
|
||||
const nextValue = value;
|
||||
onSetUserServiceAccess({ userId: user.id, serviceId: service.id, value: nextValue });
|
||||
|
||||
if (usePublicTaskerAccess) return;
|
||||
if (!isTaskManagerService || !primaryTaskManagerWorkspace || protectedFromActor || (forcedTaskManagerAdmin && !protectedSelfManage)) return;
|
||||
if (!isTaskManagerService || !primaryTaskManagerWorkspace || forcedTaskManagerAdmin) return;
|
||||
|
||||
onSetTaskManagerWorkspaceMemberRole({
|
||||
clientId: matrix.client.id,
|
||||
@@ -3239,7 +3248,6 @@ function PublicAccessUsersPanel({
|
||||
if (!membership) return null;
|
||||
|
||||
const protectedUser = isProtectedLauncherUser(user.id);
|
||||
const protectedFromActor = isProtectedFromCurrentActor(me, user.id);
|
||||
const inviterMeta = getMembershipInviterMeta(data, membership);
|
||||
const operationalCoreCell = operationalCoreService
|
||||
? matrix.cells.find((cell) => cell.userId === user.id && cell.serviceId === operationalCoreService.id) ?? null
|
||||
@@ -3274,7 +3282,7 @@ function PublicAccessUsersPanel({
|
||||
)}
|
||||
</td>
|
||||
<td>
|
||||
{protectedFromActor ? (
|
||||
{protectedUser ? (
|
||||
<AdminStaticPill>{membershipRoleLabel(membership.role)}</AdminStaticPill>
|
||||
) : (
|
||||
<NodeDcSelect
|
||||
@@ -3289,12 +3297,16 @@ function PublicAccessUsersPanel({
|
||||
)}
|
||||
</td>
|
||||
<td>
|
||||
<AdminStatusDropdown
|
||||
value={membership.status}
|
||||
options={membershipStatusOptions}
|
||||
label={`Статус в открытом контуре ${user.name}`}
|
||||
onChange={(status) => onUpdateMembership(membership.id, { status })}
|
||||
/>
|
||||
{protectedUser ? (
|
||||
<AdminStaticPill>{statusOptionLabel(membershipStatusOptions, membership.status)}</AdminStaticPill>
|
||||
) : (
|
||||
<AdminStatusDropdown
|
||||
value={membership.status}
|
||||
options={membershipStatusOptions}
|
||||
label={`Статус в открытом контуре ${user.name}`}
|
||||
onChange={(status) => onUpdateMembership(membership.id, { status })}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
<td>
|
||||
{protectedUser ? (
|
||||
@@ -3479,12 +3491,13 @@ function OperationalCoreAccessModal({
|
||||
}) {
|
||||
const membership = data.memberships.find((item) => item.clientId === client.id && item.userId === user.id);
|
||||
const protectedSelfManage = canSelfManageProtectedUser(me, user.id);
|
||||
const protectedUser = isProtectedFromCurrentActor(me, user.id) || (membership?.role === "client_owner" && !protectedSelfManage);
|
||||
const protectedUser = isProtectedLauncherUser(user.id) || isProtectedFromCurrentActor(me, user.id) || (membership?.role === "client_owner" && !protectedSelfManage);
|
||||
const basePendingValue = pendingAccessAssignments[accessCellKey(user.id, service.id)];
|
||||
const baseAssignmentValue = basePendingValue ?? accessAssignmentValue(cell);
|
||||
const baseSelectOptions = publicSelfService ? publicOperationalCoreAccessOptions : accessAssignmentOptions;
|
||||
const baseSelectOptions = publicSelfService ? publicOperationalCoreAccessOptions : operationalCoreAccessAssignmentOptions;
|
||||
const baseSelectValue = publicSelfService ? publicOperationalCoreSelectValue(baseAssignmentValue) : baseAssignmentValue;
|
||||
const basePending = basePendingValue !== undefined;
|
||||
const baseAccessLocked = protectedUser || baseSelectValue === "admin";
|
||||
|
||||
return (
|
||||
<div className="service-content-modal-layer" role="dialog" aria-modal="true" aria-label={`Operational Core доступы ${user.name}`}>
|
||||
@@ -3507,8 +3520,8 @@ function OperationalCoreAccessModal({
|
||||
: "Глобальная роль пользователя в Operational Core для выбранного клиента."}
|
||||
</small>
|
||||
</div>
|
||||
{protectedUser ? (
|
||||
<AdminStaticPill>{accessAssignmentLabel("admin")}</AdminStaticPill>
|
||||
{baseAccessLocked ? (
|
||||
<AdminStaticPill>{operationalCoreStaticRoleLabel(baseSelectValue)}</AdminStaticPill>
|
||||
) : (
|
||||
<NodeDcSelect
|
||||
className="admin-table-select-wrap"
|
||||
@@ -3546,7 +3559,7 @@ function OperationalCoreAccessModal({
|
||||
className={cn("task-module-access-row", enabled && "task-module-access-row--enabled", pending && "task-module-access-row--pending")}
|
||||
type="button"
|
||||
aria-pressed={enabled}
|
||||
disabled={pending || systemEnabled}
|
||||
disabled={pending || systemEnabled || protectedUser}
|
||||
onClick={() =>
|
||||
onSetServiceModuleEntitlement({
|
||||
clientId: client.id,
|
||||
@@ -3595,8 +3608,8 @@ function OperationalCoreAccessModal({
|
||||
{workspace.isPrimary ? " · основной workspace" : ""}
|
||||
</small>
|
||||
</div>
|
||||
{protectedUser ? (
|
||||
<AdminStaticPill>{accessAssignmentLabel("admin")}</AdminStaticPill>
|
||||
{protectedUser || value === "admin" ? (
|
||||
<AdminStaticPill>{operationalCoreStaticRoleLabel(value)}</AdminStaticPill>
|
||||
) : (
|
||||
<NodeDcSelect
|
||||
className="admin-table-select-wrap"
|
||||
@@ -3608,6 +3621,7 @@ function OperationalCoreAccessModal({
|
||||
disabled={pending}
|
||||
onChange={(nextValue) => {
|
||||
if (nextValue === "pending") return;
|
||||
if (nextValue === "admin") return;
|
||||
onSetUserServiceAccess({ userId: user.id, serviceId: service.id, value: nextValue });
|
||||
onSetTaskManagerWorkspaceMemberRole({
|
||||
clientId: client.id,
|
||||
@@ -3639,8 +3653,8 @@ function OperationalCoreAccessModal({
|
||||
{project.memberCount ? ` · ${project.memberCount} участников` : ""}
|
||||
</small>
|
||||
</div>
|
||||
{protectedUser ? (
|
||||
<AdminStaticPill>{accessAssignmentLabel("admin")}</AdminStaticPill>
|
||||
{protectedUser || projectValue === "admin" ? (
|
||||
<AdminStaticPill>{operationalCoreStaticRoleLabel(projectValue)}</AdminStaticPill>
|
||||
) : (
|
||||
<NodeDcSelect
|
||||
className="admin-table-select-wrap"
|
||||
@@ -3652,6 +3666,7 @@ function OperationalCoreAccessModal({
|
||||
disabled={projectPending}
|
||||
onChange={(nextValue) => {
|
||||
if (nextValue === "pending") return;
|
||||
if (nextValue === "admin") return;
|
||||
|
||||
const nextTaskManagerRole = accessAssignmentToTaskManagerRole(nextValue);
|
||||
if (nextTaskManagerRole !== "unset") {
|
||||
@@ -3735,6 +3750,8 @@ function AccessCellControl({
|
||||
? publicOperationalCoreAccessOptions
|
||||
: isEngineServiceForAccessControl(service)
|
||||
? engineAccessAssignmentOptions
|
||||
: isOperationalCoreService(service)
|
||||
? operationalCoreAccessAssignmentOptions
|
||||
: accessAssignmentOptions;
|
||||
const displayTitle = isPending
|
||||
? publicSelfService
|
||||
@@ -5139,6 +5156,11 @@ function taskManagerRoleToAccessAssignment(role: TaskManagerWorkspaceMemberRole)
|
||||
return "unset";
|
||||
}
|
||||
|
||||
function operationalCoreStaticRoleLabel(value: OperationalCoreRoleSelectValue): string {
|
||||
if (value === "pending") return "Сохраняем...";
|
||||
return accessAssignmentLabel(value);
|
||||
}
|
||||
|
||||
function sourceLabel(source?: AccessMatrixCell["effectiveAccess"]["source"]): string {
|
||||
if (!source) return "—";
|
||||
const labels = {
|
||||
|
||||
Reference in New Issue
Block a user