diff --git a/packages/api-v4/.changeset/pr-13305-upcoming-features-1769072638720.md b/packages/api-v4/.changeset/pr-13305-upcoming-features-1769072638720.md new file mode 100644 index 00000000000..4581ff86c93 --- /dev/null +++ b/packages/api-v4/.changeset/pr-13305-upcoming-features-1769072638720.md @@ -0,0 +1,5 @@ +--- +"@linode/api-v4": Upcoming Features +--- + +RESPROT2- Added lock permissions to IAM types (AccountAdmin and AccountViewer ) ([#13305](https://github.com/linode/manager/pull/13305)) diff --git a/packages/api-v4/src/iam/types.ts b/packages/api-v4/src/iam/types.ts index 4196c0c25cb..1da75c56215 100644 --- a/packages/api-v4/src/iam/types.ts +++ b/packages/api-v4/src/iam/types.ts @@ -76,11 +76,13 @@ export type AccountAdmin = | 'cancel_account' | 'cancel_service_transfer' | 'create_child_account_token' + | 'create_lock' | 'create_profile_pat' | 'create_profile_ssh_key' | 'create_profile_tfa_secret' | 'create_service_transfer' | 'create_user' + | 'delete_lock' | 'delete_profile_pat' | 'delete_profile_phone_number' | 'delete_profile_ssh_key' @@ -98,6 +100,7 @@ export type AccountAdmin = | 'list_delegate_users' | 'list_enrolled_beta_programs' | 'list_entities' + | 'list_locks' | 'list_role_permissions' | 'list_service_transfers' | 'list_user_delegate_accounts' @@ -123,6 +126,7 @@ export type AccountAdmin = | 'view_account_settings' | 'view_child_account' | 'view_enrolled_beta_program' + | 'view_lock' | 'view_network_usage' | 'view_profile_security_question' | 'view_region_available_service' @@ -258,6 +262,7 @@ export type AccountViewer = | 'list_default_firewalls' | 'list_enrolled_beta_programs' | 'list_entities' + | 'list_locks' | 'list_role_permissions' | 'list_service_transfers' | 'list_user_grants' @@ -266,6 +271,7 @@ export type AccountViewer = | 'view_account_login' | 'view_account_settings' | 'view_enrolled_beta_program' + | 'view_lock' | 'view_network_usage' | 'view_region_available_service' | 'view_service_transfer' diff --git a/packages/manager/.changeset/pr-13305-upcoming-features-1769072525125.md b/packages/manager/.changeset/pr-13305-upcoming-features-1769072525125.md new file mode 100644 index 00000000000..53c55b7b3a5 --- /dev/null +++ b/packages/manager/.changeset/pr-13305-upcoming-features-1769072525125.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Upcoming Features +--- + +RESPROT2 - Display/Disable Lock/Unlock action in Linode list and detail action menu ([#13305](https://github.com/linode/manager/pull/13305)) diff --git a/packages/manager/src/dev-tools/FeatureFlagTool.tsx b/packages/manager/src/dev-tools/FeatureFlagTool.tsx index 6a6d8e27948..bc87d9d0a29 100644 --- a/packages/manager/src/dev-tools/FeatureFlagTool.tsx +++ b/packages/manager/src/dev-tools/FeatureFlagTool.tsx @@ -47,6 +47,7 @@ const options: { flag: keyof Flags; label: string }[] = [ { flag: 'objMultiCluster', label: 'OBJ Multi-Cluster' }, { flag: 'objectStorageGen2', label: 'OBJ Gen2' }, { flag: 'privateImageSharing', label: 'Private Image Sharing' }, + { flag: 'resourceLock', label: 'Resource Lock' }, { flag: 'selfServeBetas', label: 'Self Serve Betas' }, { flag: 'supportTicketSeverity', label: 'Support Ticket Severity' }, { flag: 'dbaasV2', label: 'Databases V2 Beta' }, diff --git a/packages/manager/src/featureFlags.ts b/packages/manager/src/featureFlags.ts index 1090d4e24ae..6551904ed57 100644 --- a/packages/manager/src/featureFlags.ts +++ b/packages/manager/src/featureFlags.ts @@ -193,6 +193,10 @@ interface FirewallRulesetsAndPrefixLists extends BetaFeatureFlag { la: boolean; } +interface ResourceLockFlag { + linodes: boolean; +} + export interface Flags { acceleratedPlans: AcceleratedPlansFlag; aclp: AclpFlag; @@ -255,6 +259,7 @@ export interface Flags { promos: boolean; promotionalOffers: PromotionalOffer[]; referralBannerText: BannerContent; + resourceLock: ResourceLockFlag; secureVmCopy: SecureVMCopy; selfServeBetas: boolean; soldOutChips: boolean; diff --git a/packages/manager/src/features/Linodes/LinodesLanding/LinodeActionMenu/LinodeActionMenu.tsx b/packages/manager/src/features/Linodes/LinodesLanding/LinodeActionMenu/LinodeActionMenu.tsx index fd96b52d12d..9790af2da33 100644 --- a/packages/manager/src/features/Linodes/LinodesLanding/LinodeActionMenu/LinodeActionMenu.tsx +++ b/packages/manager/src/features/Linodes/LinodesLanding/LinodeActionMenu/LinodeActionMenu.tsx @@ -9,6 +9,7 @@ import { getRestrictedResourceText } from 'src/features/Account/utils'; import { isMTCPlan } from 'src/features/components/PlansPanel/utils'; import { usePermissions } from 'src/features/IAM/hooks/usePermissions'; import { lishLaunch } from 'src/features/Lish/lishUtils'; +import { useFlags } from 'src/hooks/useFlags'; import { sendLinodeActionEvent, sendLinodeActionMenuItemEvent, @@ -18,7 +19,7 @@ import { import { buildQueryStringForLinodeClone } from './LinodeActionMenuUtils'; import type { LinodeHandlers } from '../LinodesLanding'; -import type { LinodeBackups, LinodeType } from '@linode/api-v4'; +import type { LinodeBackups, LinodeType, LockType } from '@linode/api-v4'; import type { ActionType } from 'src/features/Account/utils'; const MAINTENANCE_TOOLTIP_TEXT = @@ -36,6 +37,7 @@ export interface LinodeActionMenuProps extends LinodeHandlers { linodeRegion: string; linodeStatus: string; linodeType?: LinodeType; + locks?: LockType[]; } interface ActionConfig { @@ -49,16 +51,22 @@ interface ActionConfig { } export const LinodeActionMenu = (props: LinodeActionMenuProps) => { - const { linodeId, linodeRegion, linodeStatus, linodeType } = props; + const { linodeId, linodeRegion, linodeStatus, linodeType, locks } = props; const navigate = useNavigate(); + const flags = useFlags(); const regions = useRegionsQuery().data ?? []; const isBareMetalInstance = linodeType?.class === 'metal'; const hasHostMaintenance = linodeStatus === 'stopped'; const [isOpen, setIsOpen] = React.useState(false); + const isResourceLockEnabled = flags.resourceLock?.linodes; + const isLocked = !!locks?.length; + const { data: accountPermissions } = usePermissions('account', [ 'create_linode', + 'create_lock', + 'delete_lock', ]); const { data: permissions, isLoading } = usePermissions( @@ -234,6 +242,33 @@ export const LinodeActionMenu = (props: LinodeActionMenuProps) => { ? MAINTENANCE_TOOLTIP_TEXT : undefined, }, + { + condition: Boolean(isResourceLockEnabled), + disabled: isLocked + ? !accountPermissions.delete_lock + : !accountPermissions.create_lock, + isReadOnly: isLocked + ? !accountPermissions.delete_lock + : !accountPermissions.create_lock, + onClick: () => { + if (isLocked) { + sendLinodeActionMenuItemEvent('Unlock Linode'); + // props.onOpenUnlockDialog(); + } else { + sendLinodeActionMenuItemEvent('Lock Linode'); + // props.onOpenAddLockDialog(); + } + }, + title: isLocked ? 'Unlock' : 'Lock', + tooltipAction: 'edit', + tooltipText: isLocked + ? !accountPermissions.delete_lock + ? NO_PERMISSION_TOOLTIP_TEXT + : undefined + : !accountPermissions.create_lock + ? NO_PERMISSION_TOOLTIP_TEXT + : undefined, + }, { condition: true, disabled: !permissions.delete_linode || hasHostMaintenance, diff --git a/packages/manager/src/features/Linodes/LinodesLanding/LinodeRow/LinodeRow.tsx b/packages/manager/src/features/Linodes/LinodesLanding/LinodeRow/LinodeRow.tsx index 7aacd7ad18a..1c6e87534cc 100644 --- a/packages/manager/src/features/Linodes/LinodesLanding/LinodeRow/LinodeRow.tsx +++ b/packages/manager/src/features/Linodes/LinodesLanding/LinodeRow/LinodeRow.tsx @@ -46,6 +46,7 @@ export const LinodeRow = (props: Props) => { id, ipv4, label, + locks, maintenance, region, status, @@ -189,6 +190,7 @@ export const LinodeRow = (props: Props) => { linodeRegion={region} linodeStatus={status} linodeType={linodeType} + locks={locks} {...handlers} /> diff --git a/packages/queries/src/locks/locks.ts b/packages/queries/src/locks/locks.ts index 4994d47c40a..a10ab6d50e5 100644 --- a/packages/queries/src/locks/locks.ts +++ b/packages/queries/src/locks/locks.ts @@ -37,9 +37,14 @@ export const lockQueries = createQueryKeys('locks', { * @example * const { data, isLoading } = useLocksQuery(); */ -export const useLocksQuery = (params: Params = {}, filter: Filter = {}) => { +export const useLocksQuery = ( + params: Params = {}, + filter: Filter = {}, + enabled: boolean = true, +) => { return useQuery, APIError[]>({ ...lockQueries.locks._ctx.paginated(params, filter), + enabled, }); };