From d129947a972d284a56d4eda3da37b7cd73d1a762 Mon Sep 17 00:00:00 2001 From: Jared Date: Thu, 13 Feb 2020 15:08:46 -0500 Subject: [PATCH] M3-3857 Add: Cloud Firewall SDK methods (#6094) * Replace stub getFirewalls and getFirewallDevices with real requests * Add createFirewall * Add DELETE and POST for devices, GET for rules * Add update methods and event messages - Add update methods - Add eventMessageGenerator handlers for the 6 new firewalls events * Add documentation and deleteFirewall * Add enableFirewall and disableFirewall helper methods * Add firewall event types to EventAction union type * Add some missing URL slashes * Make UpdateFirewallPayload typing more precise * Rename firewall_device_delete to firewall_device_remove --- packages/linode-js-sdk/src/account/types.ts | 7 + .../src/firewalls/firewalls.schema.ts | 20 ++ .../linode-js-sdk/src/firewalls/firewalls.ts | 263 +++++++++++++++--- packages/linode-js-sdk/src/firewalls/types.ts | 25 +- .../src/containers/firewalls.container.ts | 5 +- packages/manager/src/eventMessageGenerator.ts | 23 ++ .../FirewallLanding/FirewallLanding.tsx | 2 +- .../src/store/firewalls/firewalls.requests.ts | 3 +- 8 files changed, 298 insertions(+), 50 deletions(-) create mode 100644 packages/linode-js-sdk/src/firewalls/firewalls.schema.ts diff --git a/packages/linode-js-sdk/src/account/types.ts b/packages/linode-js-sdk/src/account/types.ts index 710cb3e9a38..e1c5e67042a 100644 --- a/packages/linode-js-sdk/src/account/types.ts +++ b/packages/linode-js-sdk/src/account/types.ts @@ -187,6 +187,13 @@ export type EventAction = | 'domain_record_create' | 'domain_record_updated' | 'domain_record_delete' + | 'firewall_create' + | 'firewall_delete' + | 'firewall_device_add' + | 'firewall_device_remove' + | 'firewall_disable' + | 'firewall_enable' + | 'firewall_update' | 'image_update' | 'image_delete' | 'lassie_reboot' diff --git a/packages/linode-js-sdk/src/firewalls/firewalls.schema.ts b/packages/linode-js-sdk/src/firewalls/firewalls.schema.ts new file mode 100644 index 00000000000..65643ac3282 --- /dev/null +++ b/packages/linode-js-sdk/src/firewalls/firewalls.schema.ts @@ -0,0 +1,20 @@ +import { array, number, object, string } from 'yup'; + +export const CreateFirewallSchema = object({ + label: string(), + tags: array().of(string()), + rules: object().required('You must provide a set of Firewall rules.') +}); + +export const UpdateFirewallSchema = object().shape({ + label: string(), + tags: array().of(string()), + status: string().oneOf(['enabled', 'disabled']) // 'deleted' is also a status but it's not settable +}); + +export const FirewallDeviceSchema = object({ + type: string() + .oneOf(['linode', 'nodebalancer']) + .required('Device type is required.'), + id: number().required('ID is required.') +}); diff --git a/packages/linode-js-sdk/src/firewalls/firewalls.ts b/packages/linode-js-sdk/src/firewalls/firewalls.ts index 710066cdfb6..7af68c51254 100644 --- a/packages/linode-js-sdk/src/firewalls/firewalls.ts +++ b/packages/linode-js-sdk/src/firewalls/firewalls.ts @@ -1,48 +1,221 @@ -// import { API_ROOT } from '../constants'; -// import Request, { -// setData, -// setMethod, -// setParams, -// setURL, -// setXFilter -// } from '../request'; +import { BETA_API_ROOT } from '../constants'; +import Request, { + setData, + setMethod, + setParams, + setURL, + setXFilter +} from '../request'; import { ResourcePage as Page } from '../types'; -import { Firewall, FirewallDevice } from './types'; - -/** - * mocked GET firewalls - */ -export const getFirewalls = ( - mockData: Firewall[], - params: any = {}, - filter: any = {} -): Promise> => { - return new Promise((resolve, reject) => { - setTimeout(() => { - resolve({ - data: mockData, - page: 1, - pages: 1, - results: mockData.length - }); - }, 1000); - }).then((data: any) => { - return data; - }); -}; +import { + CreateFirewallSchema, + FirewallDeviceSchema, + UpdateFirewallSchema +} from './firewalls.schema'; +import { + CreateFirewallPayload, + Firewall, + FirewallDevice, + FirewallDevicePayload, + FirewallRules, + UpdateFirewallPayload +} from './types'; +// FIREWALLS + +/** + * getFirewalls + * + * Returns a paginated list of all Cloud Firewalls on this account. + */ +export const getFirewalls = (params?: any, filters?: any) => + Request>( + setMethod('GET'), + setParams(params), + setXFilter(filters), + setURL(`${BETA_API_ROOT}/networking/firewalls`) + ).then(response => response.data); + +/** + * getFirewall + * + * Get a specific Firewall resource by its ID. The Firewall's Devices will not be + * returned in the response. Use getFirewallDevices() to view the Devices. + * + */ +export const getFirewall = (firewallID: number) => + Request( + setMethod('GET'), + setURL(`${BETA_API_ROOT}/networking/firewalls/${firewallID}`) + ).then(response => response.data); + +/** + * createFirewall + * + * Creates a Firewall to filter network traffic. Use the `rules` property to + * create inbound and outbound access rules. Use the `devices` property to assign the + * Firewall to a Linode service. + * A Firewall can be assigned to multiple Linode services, and up to three active Firewalls + * can be assigned to a single Linode service. Additional disabled Firewalls can be + * assigned to a service, but they cannot be enabled if three other active Firewalls + * are already assigned to the same service. + */ +export const createFirewall = (data: CreateFirewallPayload) => + Request( + setMethod('POST'), + setData(data, CreateFirewallSchema), + setURL(`${BETA_API_ROOT}/networking/firewalls`) + ).then(response => response.data); + +/** + * updateFirewall + * + * Updates the Cloud Firewall with the provided ID. Only label, tags, and status can be updated + * through this method. + * + */ +export const updateFirewall = ( + firewallID: number, + data: UpdateFirewallPayload +) => + Request( + setMethod('PUT'), + setData(data, UpdateFirewallSchema), + setURL(`${BETA_API_ROOT}/networking/firewalls/${firewallID}`) + ).then(response => response.data); + +/** + * enableFirewall + * + * Convenience method for enabling a Cloud Firewall. Calls updateFirewall internally + * with { status: 'enabled' } + * + */ +export const enableFirewall = (firewallID: number) => + updateFirewall(firewallID, { status: 'enabled' }); + +/** + * disableFirewall + * + * Convenience method for disabling a Cloud Firewall. Calls updateFirewall internally + * with { status: 'disabled' } + * + */ +export const disableFirewall = (firewallID: number) => + updateFirewall(firewallID, { status: 'disabled' }); + +/** + * deleteFirewall + * + * Deletes a single Cloud Firewall. + * + */ +export const deleteFirewall = (firewallID: number) => + Request<{}>( + setMethod('DELETE'), + setURL(`${BETA_API_ROOT}/networking/firewalls/${firewallID}`) + ).then(response => response.data); + +// FIREWALL RULES + +/** + * getFirewallRules + * + * Returns the current set of rules for a single Cloud Firewall. + */ +export const getFirewallRules = ( + firewallID: number, + params?: any, + filters?: any +) => + Request>( + setMethod('GET'), + setParams(params), + setXFilter(filters), + setURL(`${BETA_API_ROOT}/networking/firewalls/${firewallID}/rules`) + ).then(response => response.data); + +/** + * updateFirewallRules + * + * Updates the inbound and outbound Rules for a Firewall. Using this endpoint will + * replace all of a Firewall's ruleset with the Rules specified in your request. + */ +export const updateFirewallRules = (firewallID: number, data: FirewallRules) => + Request( + setMethod('PUT'), + setData(data), // Validation is too complicated for these; leave it to the API. + setURL(`${BETA_API_ROOT}/networking/firewalls/${firewallID}/rules`) + ).then(response => response.data); + +// DEVICES + +/** + * getFirewallDevices + * + * Returns a paginated list of a Firewall's Devices. A Firewall Device assigns a + * Firewall to a Linode service (referred to as the Device's `entity`). + */ export const getFirewallDevices = ( - id: number, - mockData: FirewallDevice[] -): Promise> => { - return new Promise(resolve => { - setTimeout(() => { - resolve({ - data: mockData, - page: 1, - pages: 1, - results: mockData.length - }); - }); - }).then((data: any) => data); -}; + firewallID: number, + params?: any, + filters?: any +) => + Request>( + setMethod('GET'), + setParams(params), + setXFilter(filters), + setURL(`${BETA_API_ROOT}/networking/firewalls/${firewallID}/devices`) + ).then(response => response.data); + +/** + * getFirewallDevice + * + * Returns information about a single Firewall Device. A Firewall Device assigns a + * Firewall to a Linode service (referred to as the Device's `entity`). + */ +export const getFirewallDevice = (firewallID: number, deviceID: number) => + Request( + setMethod('GET'), + setURL( + `${BETA_API_ROOT}/networking/firewalls/${firewallID}/devices/${deviceID}` + ) + ).then(response => response.data); + +/** + * addFirewallDevice + * + * Creates a Firewall Device, which assigns a Firewall to a Linode service (referred to + * as the Device's `entity`). + * A Firewall can be assigned to multiple Linode services, and up to three active Firewalls can + * be assigned to a single Linode service. Additional disabled Firewalls can be + * assigned to a service, but they cannot be enabled if three other active Firewalls + * are already assigned to the same service. + * Creating a Firewall Device will apply the Rules from a Firewall to a Linode service. + * A `firewall_device_add` Event is generated when the Firewall Device is added successfully. + */ +export const addFirewallDevice = ( + firewallID: number, + data: FirewallDevicePayload +) => + Request( + setMethod('POST'), + setURL(`${BETA_API_ROOT}/networking/firewalls/${firewallID}/devices`), + setData(data, FirewallDeviceSchema) + ).then(response => response.data); + +/** + * deleteFirewallDevice + * + * Removes a Firewall Device, which removes a Firewall from the Linode service it was + * assigned to by the Device. This will remove all of the Firewall's Rules from the Linode + * service. If any other Firewalls have been assigned to the Linode service, then those Rules + * will remain in effect. + */ +export const deleteFirewallDevice = (firewallID: number, deviceID: number) => + Request<{}>( + setMethod('DELETE'), + setURL( + `${BETA_API_ROOT}/networking/firewalls/${firewallID}/devices/${deviceID}` + ) + ).then(response => response.data); diff --git a/packages/linode-js-sdk/src/firewalls/types.ts b/packages/linode-js-sdk/src/firewalls/types.ts index 37e8e130aab..ae5cb3f7bbe 100644 --- a/packages/linode-js-sdk/src/firewalls/types.ts +++ b/packages/linode-js-sdk/src/firewalls/types.ts @@ -2,6 +2,8 @@ export type FirewallStatus = 'enabled' | 'disabled' | 'deleted'; export type FirewallRuleProtocol = 'ALL' | 'TCP' | 'UDP' | 'ICMP'; +export type FirewallDeviceEntityType = 'linode' | 'nodebalancer'; + export interface Firewall { id: number; status: FirewallStatus; @@ -29,7 +31,7 @@ export interface FirewallRuleType { export interface FirewallDeviceEntity { id: number; - type: 'linode' | 'nodebalancer'; + type: FirewallDeviceEntityType; label: string; url: string; } @@ -38,3 +40,24 @@ export interface FirewallDevice { id: number; entity: FirewallDeviceEntity; } + +export interface CreateFirewallPayload { + label?: string; + tags?: string[]; + rules: FirewallRules; + devices?: { + linodes?: number[]; + nodebalancers?: number[]; + }; +} + +export interface UpdateFirewallPayload { + label?: string; + tags?: string[]; + status?: Omit; +} + +export interface FirewallDevicePayload { + id: number; + type: FirewallDeviceEntityType; +} diff --git a/packages/manager/src/containers/firewalls.container.ts b/packages/manager/src/containers/firewalls.container.ts index e335704a480..042a100b960 100644 --- a/packages/manager/src/containers/firewalls.container.ts +++ b/packages/manager/src/containers/firewalls.container.ts @@ -7,7 +7,10 @@ import { ThunkDispatch } from 'src/store/types'; import { GetAllData } from 'src/utilities/getAll'; interface DispatchProps { - getFirewalls: (params: any, filters: any) => Promise>; + getFirewalls: ( + params?: any, + filters?: any + ) => Promise>; } /* tslint:disable-next-line */ diff --git a/packages/manager/src/eventMessageGenerator.ts b/packages/manager/src/eventMessageGenerator.ts index 74dd72f7987..0b7f422c41a 100644 --- a/packages/manager/src/eventMessageGenerator.ts +++ b/packages/manager/src/eventMessageGenerator.ts @@ -164,6 +164,29 @@ export const eventMessageCreators: { [index: string]: CreatorsForStatus } = { notification: e => `A domain record has been deleted from ${e.entity!.label}` }, + firewall_enable: { + notification: e => `Firewall ${e.entity?.label ?? ''} has been enabled.` + }, + firewall_disable: { + notification: e => `Firewall ${e.entity?.label ?? ''} has been disabled.` + }, + firewall_update: { + notification: e => `Firewall ${e.entity?.label ?? ''} has been updated.` + }, + firewall_device_add: { + notification: e => + `A device has been added to Firewall ${e.entity?.label ?? ''}.` + }, + firewall_device_remove: { + notification: e => + `A device has been removed from Firewall ${e.entity?.label ?? ''}.` + }, + firewall_delete: { + notification: e => `Firewall ${e.entity?.label ?? ''} has been deleted.` + }, + firewall_create: { + notification: e => `Firewall ${e.entity?.label ?? ''} has been created.` + }, image_update: { notification: e => `Image ${e.entity?.label ?? ''} has been updated.` }, diff --git a/packages/manager/src/features/Firewalls/FirewallLanding/FirewallLanding.tsx b/packages/manager/src/features/Firewalls/FirewallLanding/FirewallLanding.tsx index 937e26d4638..b3c605c6646 100644 --- a/packages/manager/src/features/Firewalls/FirewallLanding/FirewallLanding.tsx +++ b/packages/manager/src/features/Firewalls/FirewallLanding/FirewallLanding.tsx @@ -67,7 +67,7 @@ const FirewallLanding: React.FC = props => { firewallsLastUpdated === 0 && !firewallsLoading ) { - props.getFirewalls({ hello: 'hello' }, { world: 'world' }); + props.getFirewalls(); } }, [firewallsLastUpdated, firewallsLoading, firewallsKeys]); diff --git a/packages/manager/src/store/firewalls/firewalls.requests.ts b/packages/manager/src/store/firewalls/firewalls.requests.ts index f0faa56784b..ead41e76860 100644 --- a/packages/manager/src/store/firewalls/firewalls.requests.ts +++ b/packages/manager/src/store/firewalls/firewalls.requests.ts @@ -1,12 +1,11 @@ import { Firewall, getFirewalls } from 'linode-js-sdk/lib/firewalls'; -import { firewalls as mockFirewalls } from 'src/__data__/firewalls'; import { getAll } from 'src/utilities/getAll'; import { createRequestThunk } from '../store.helpers'; import { getFirewalls as _getFirewallsAction } from './firewalls.actions'; const getAllFirewallsRequest = (payload: { params?: any; filter?: any }) => getAll((passedParams, passedFilter) => - getFirewalls(mockFirewalls, passedParams, passedFilter) + getFirewalls(passedParams, passedFilter) )(payload.params, payload.filter); export const getAllFirewalls = createRequestThunk(