Skip to content
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
6 changes: 5 additions & 1 deletion mock-api/msw/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import { GiB } from '~/util/units'
import { genCumulativeI64Data } from '../metrics'
import { serial } from '../serial'
import { defaultSilo, toIdp } from '../silo'
import { getTimestamps } from '../util'
import { defaultFirewallRules } from '../vpc'
import {
db,
getIpFromPool,
Expand All @@ -41,7 +43,6 @@ import {
errIfInvalidDiskSize,
forbiddenErr,
getStartAndEndTime,
getTimestamps,
handleMetrics,
ipInAnyRange,
ipRangeLen,
Expand Down Expand Up @@ -997,6 +998,9 @@ export const handlers = makeHandlers({
}
db.vpcSubnets.push(newSubnet)

// populate default firewall rules
db.vpcFirewallRules.push(...defaultFirewallRules(newVpc.id))

return json(newVpc, { status: 201 })
},
vpcView: ({ path, query }) => lookup.vpc({ ...path, ...query }),
Expand Down
5 changes: 0 additions & 5 deletions mock-api/msw/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,6 @@ export function getStartAndEndTime(params: { startTime?: Date; endTime?: Date })
return { startTime, endTime }
}

export function getTimestamps() {
const now = new Date().toISOString()
return { time_created: now, time_modified: now }
}

export const forbiddenErr = () =>
json({ error_code: 'Forbidden', request_id: 'fake-id' }, { status: 403 })

Expand Down
15 changes: 15 additions & 0 deletions mock-api/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* Copyright Oxide Computer Company
*/

// this is only in its own file so it can be used in both the mock resources and
// the mock handlers without circular import issues

export function getTimestamps() {
const now = new Date().toISOString()
return { time_created: now, time_modified: now }
}
126 changes: 59 additions & 67 deletions mock-api/vpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@
* Copyright Oxide Computer Company
*/

import { v4 as uuid } from 'uuid'

import type { Vpc, VpcFirewallRule, VpcSubnet } from '@oxide/api'

import type { Json } from './json-type'
import { project, project2 } from './project'
import { getTimestamps } from './util'

const time_created = new Date(2021, 0, 1).toISOString()
const time_modified = new Date(2021, 0, 2).toISOString()
Expand Down Expand Up @@ -63,78 +66,67 @@ export const vpcSubnet2: Json<VpcSubnet> = {
ipv4_block: '10.1.1.2/24',
}

export const firewallRules: Json<VpcFirewallRule[]> = [
{
id: 'b74aeea8-1201-4efd-b6ec-011f10a0b176',
name: 'allow-internal-inbound',
status: 'enabled',
direction: 'inbound',
targets: [{ type: 'vpc', value: 'default' }],
action: 'allow',
description:
'allow inbound traffic to all instances within the VPC if originated within the VPC',
filters: {
hosts: [{ type: 'vpc', value: 'default' }],
export function defaultFirewallRules(vpcId: string): Json<VpcFirewallRule[]> {
return [
{
id: uuid(),
vpc_id: vpcId,
name: 'allow-internal-inbound',
status: 'enabled',
direction: 'inbound',
targets: [{ type: 'vpc', value: 'default' }],
action: 'allow',
description:
'allow inbound traffic to all instances within the VPC if originated within the VPC',
filters: {
hosts: [{ type: 'vpc', value: 'default' }],
},
priority: 65534,
...getTimestamps(),
},
priority: 65534,
time_created,
time_modified,
vpc_id: vpc.id,
},
{
id: '9802cd8e-1e59-4fdf-9b40-99c189f7a19b',
name: 'allow-ssh',
status: 'enabled',
direction: 'inbound',
targets: [{ type: 'vpc', value: 'default' }],
description: 'allow inbound TCP connections on port 22 from anywhere',
filters: {
ports: ['22'],
protocols: ['TCP'],
{
id: uuid(),
vpc_id: vpcId,
name: 'allow-ssh',
status: 'enabled',
direction: 'inbound',
targets: [{ type: 'vpc', value: 'default' }],
description: 'allow inbound TCP connections on port 22 from anywhere',
filters: {
ports: ['22'],
protocols: ['TCP'],
},
action: 'allow',
priority: 65534,
...getTimestamps(),
},
action: 'allow',
priority: 65534,
time_created,
time_modified,
vpc_id: vpc.id,
},
{
id: 'cde07d86-b8c0-49ed-8754-55f1bdee20fe',
name: 'allow-icmp',
status: 'enabled',
direction: 'inbound',
targets: [{ type: 'vpc', value: 'default' }],
description: 'allow inbound ICMP traffic from anywhere',
filters: {
protocols: ['ICMP'],
},
action: 'allow',
priority: 65534,
time_created,
time_modified,
vpc_id: vpc.id,
},
{
id: '5ed562d9-2566-496d-b7b3-7976b04a0b80',
name: 'allow-rdp',
status: 'enabled',
direction: 'inbound',
targets: [{ type: 'vpc', value: 'default' }],
description: 'allow inbound TCP connections on port 3389 from anywhere',
filters: {
ports: ['3389'],
protocols: ['TCP'],
{
id: uuid(),
vpc_id: vpcId,
name: 'allow-icmp',
status: 'enabled',
direction: 'inbound',
targets: [{ type: 'vpc', value: 'default' }],
description: 'allow inbound ICMP traffic from anywhere',
filters: {
protocols: ['ICMP'],
},
action: 'allow',
priority: 65534,
...getTimestamps(),
},
action: 'allow',
priority: 65534,
time_created,
time_modified,
vpc_id: vpc.id,
},
]
}

// usually we try to hard-code resource IDs, but in this case
// we don't rely on them anywhere and it's easier to wrap up if they're dynamic

export const firewallRules: Json<VpcFirewallRule[]> = [
...defaultFirewallRules(vpc.id),
// second mock VPC in other project, meant to test display with lots of
// targets and filters
{
id: '097c849e-68c8-43f7-9ceb-b1855c51f178',
id: uuid(),
name: 'lots-of-filters',
status: 'enabled',
direction: 'inbound',
Expand All @@ -156,7 +148,7 @@ export const firewallRules: Json<VpcFirewallRule[]> = [
vpc_id: vpc2.id,
},
{
id: '097c849e-68c8-43f7-9ceb-b1855c51f178',
id: uuid(),
name: 'lots-of-targets',
status: 'enabled',
direction: 'inbound',
Expand Down
52 changes: 34 additions & 18 deletions test/e2e/firewall-rules.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import { clickRowAction, expect, expectRowVisible, test } from './utils'

const defaultRules = ['allow-internal-inbound', 'allow-ssh', 'allow-icmp', 'allow-rdp']
const defaultRules = ['allow-internal-inbound', 'allow-ssh', 'allow-icmp']

test('can create firewall rule', async ({ page }) => {
await page.goto('/projects/mock-project/vpcs/mock-vpc')
Expand All @@ -19,7 +19,7 @@ test('can create firewall rule', async ({ page }) => {
await expect(page.locator(`text="${name}"`)).toBeVisible()
}
const rows = page.locator('tbody >> tr')
await expect(rows).toHaveCount(4)
await expect(rows).toHaveCount(3)

const modal = page.getByRole('dialog', { name: 'Add firewall rule' })
await expect(modal).toBeHidden()
Expand Down Expand Up @@ -100,7 +100,7 @@ test('can create firewall rule', async ({ page }) => {
const tooltip = page.getByRole('tooltip', { name: 'Other filters UDP Port 123-' })
await expect(tooltip).toBeVisible()

await expect(rows).toHaveCount(5)
await expect(rows).toHaveCount(4)
for (const name of defaultRules) {
await expect(page.locator(`text="${name}"`)).toBeVisible()
}
Expand Down Expand Up @@ -241,7 +241,7 @@ test('can update firewall rule', async ({ page }) => {
await page.getByRole('tab', { name: 'Firewall Rules' }).click()

const rows = page.locator('tbody >> tr')
await expect(rows).toHaveCount(4)
await expect(rows).toHaveCount(3)

// allow-icmp is the one we're doing to change
const oldNameCell = page.locator('td >> text="allow-icmp"')
Expand Down Expand Up @@ -298,7 +298,7 @@ test('can update firewall rule', async ({ page }) => {
await expect(newNameCell).toBeVisible()
await expect(oldNameCell).toBeHidden()

await expect(rows).toHaveCount(4)
await expect(rows).toHaveCount(3)

// new target shows up in target cell
await expect(page.locator('text=subnetedit-filter-subnetICMP')).toBeVisible()
Expand All @@ -317,28 +317,44 @@ test('create from existing rule', async ({ page }) => {
const modal = page.getByRole('dialog', { name: 'Add firewall rule' })
await expect(modal).toBeHidden()

await clickRowAction(page, 'allow-rdp', 'Clone')
await clickRowAction(page, 'allow-icmp', 'Clone')

await expect(page).toHaveURL(url + '-new/allow-icmp')
await expect(modal).toBeVisible()
await expect(modal.getByRole('textbox', { name: 'Name', exact: true })).toHaveValue(
'allow-icmp-copy'
)

await expect(modal.getByRole('checkbox', { name: 'TCP' })).not.toBeChecked()
await expect(modal.getByRole('checkbox', { name: 'UDP' })).not.toBeChecked()
await expect(modal.getByRole('checkbox', { name: 'ICMP' })).toBeChecked()

// no port filters
const portFilters = modal.getByRole('table', { name: 'Port filters' })
await expect(portFilters).toBeHidden()

const targets = modal.getByRole('table', { name: 'Targets' })
await expect(targets.getByRole('row', { name: 'Name: default, Type: vpc' })).toBeVisible()

// close the modal
await page.keyboard.press('Escape')
await expect(modal).toBeHidden()

// do it again with a different rule
await clickRowAction(page, 'allow-ssh', 'Clone')

await expect(page).toHaveURL(url + '-new/allow-rdp')
await expect(modal).toBeVisible()
await expect(modal.getByRole('textbox', { name: 'Name', exact: true })).toHaveValue(
'allow-rdp-copy'
'allow-ssh-copy'
)

await expect(portFilters.getByRole('cell', { name: '22', exact: true })).toBeVisible()

await expect(modal.getByRole('checkbox', { name: 'TCP' })).toBeChecked()
await expect(modal.getByRole('checkbox', { name: 'UDP' })).not.toBeChecked()
await expect(modal.getByRole('checkbox', { name: 'ICMP' })).not.toBeChecked()

await expect(
modal
.getByRole('table', { name: 'Port filters' })
.getByRole('cell', { name: '3389', exact: true })
).toBeVisible()
await expect(
modal
.getByRole('table', { name: 'Targets' })
.getByRole('row', { name: 'Name: default, Type: vpc' })
).toBeVisible()
await expect(targets.getByRole('row', { name: 'Name: default, Type: vpc' })).toBeVisible()
})

const rulePath = '/projects/mock-project/vpcs/mock-vpc/firewall-rules/allow-icmp/edit'
Expand Down
Loading