diff --git a/e2e-tests/admin/tests/alias-ent.spec.js b/e2e-tests/admin/tests/alias-ent.spec.js index 14a38a101..a8cdaef13 100644 --- a/e2e-tests/admin/tests/alias-ent.spec.js +++ b/e2e-tests/admin/tests/alias-ent.spec.js @@ -20,178 +20,277 @@ test.beforeAll(async () => { }); test.describe('Aliases (Enterprise)', () => { - test('Set up alias from target details page @ent @aws @docker', async ({ - page, - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - sshUser, - sshKeyPath, - targetAddress, - targetPort, - }) => { - await page.goto('/'); - let orgName; - let alias; - let connect; - const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 10); - try { - const orgsPage = new OrgsPage(page); - orgName = await orgsPage.createOrg(); - const projectsPage = new ProjectsPage(page); - await projectsPage.createProject(); + test( + 'Set up alias from target details page', + { tag: ['@ent', '@aws', '@docker'] }, + async ({ + page, + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + sshUser, + sshKeyPath, + targetAddress, + targetPort, + }) => { + await page.goto('/'); + let orgName; + let alias; + let connect; + const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 10); + try { + const orgsPage = new OrgsPage(page); + orgName = await orgsPage.createOrg(); + const projectsPage = new ProjectsPage(page); + await projectsPage.createProject(); - const targetsPage = new TargetsPage(page); - const targetName = await targetsPage.createSshTargetWithAddressEnt( - targetAddress, - targetPort, - ); - const credentialStoresPage = new CredentialStoresPage(page); - await credentialStoresPage.createStaticCredentialStore(); - const credentialName = - await credentialStoresPage.createStaticCredentialKeyPair( - sshUser, - sshKeyPath, + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createSshTargetWithAddressEnt( + targetAddress, + targetPort, + ); + const credentialStoresPage = new CredentialStoresPage(page); + await credentialStoresPage.createStaticCredentialStore(); + const credentialName = + await credentialStoresPage.createStaticCredentialKeyPair( + sshUser, + sshKeyPath, + ); + await targetsPage.addInjectedCredentialsToTarget( + targetName, + credentialName, ); - await targetsPage.addInjectedCredentialsToTarget( - targetName, - credentialName, - ); - // Create alias for target - await page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText(targetName) - .click(); - const aliasName = 'Alias ' + nanoid(); - alias = 'example.alias.' + nanoid(); - await page.getByRole('link', { name: 'Add an alias' }).click(); - await page.getByLabel('Name').fill(aliasName); - await page.getByLabel('Description').fill('This is an automated test'); - await page.getByLabel('Alias Value').fill(alias); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); + // Create alias for target + await page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(targetName) + .click(); + const aliasName = 'Alias ' + nanoid(); + alias = 'example.alias.' + nanoid(); + await page.getByRole('link', { name: 'Add an alias' }).click(); + await page.getByLabel('Name').fill(aliasName); + await page.getByLabel('Description').fill('This is an automated test'); + await page.getByLabel('Alias Value').fill(alias); + await page.getByRole('button', { name: 'Save' }).click(); + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); - // Connect to target using alias - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - connect = await boundaryCli.connectSshToAlias(alias); - const sessionsPage = new SessionsPage(page); - await sessionsPage.waitForSessionToBeVisible(targetName); + // Connect to target using alias + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + connect = await boundaryCli.connectSshToAlias(alias); + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); - // Clear destination from alias - await page - .getByRole('navigation', { name: 'Resources' }) - .getByRole('link', { name: 'Targets' }) - .click(); - await page.getByRole('link', { name: targetName }).click(); - await page.getByRole('link', { name: alias }).click(); - // Note: On the Target details page, there is a section with the header - // "Aliases". The extra check here is to ensure that we are on the Alias - // details page and not the Target details page. - await expect(page.getByRole('heading', { name: 'Alias' })).not.toHaveText( - 'Aliases', - ); - await page.getByRole('button', { name: 'Manage' }).click(); - await page.getByText('Clear', { exact: true }).click(); - await page.getByText('OK', { exact: true }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - } finally { - if (connect) { - connect.kill('SIGTERM'); - } - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - if (orgName) { - const orgId = await boundaryCli.getOrgIdFromName(orgName); - if (orgId) { - await boundaryCli.deleteScope(orgId); + // Clear destination from alias + await page + .getByRole('navigation', { name: 'Resources' }) + .getByRole('link', { name: 'Targets' }) + .click(); + await page.getByRole('link', { name: targetName }).click(); + await page.getByRole('link', { name: alias }).click(); + // Note: On the Target details page, there is a section with the header + // "Aliases". The extra check here is to ensure that we are on the Alias + // details page and not the Target details page. + await expect( + page.getByRole('heading', { name: 'Alias' }), + ).not.toHaveText('Aliases'); + await page.getByRole('button', { name: 'Manage' }).click(); + await page.getByText('Clear', { exact: true }).click(); + await page.getByText('OK', { exact: true }).click(); + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); + } finally { + if (connect) { + connect.kill('SIGTERM'); + } + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + if (orgName) { + const orgId = await boundaryCli.getOrgIdFromName(orgName); + if (orgId) { + await boundaryCli.deleteScope(orgId); + } + } + if (alias) { + await boundaryCli.deleteAlias(alias); } } - if (alias) { - await boundaryCli.deleteAlias(alias); - } - } - }); + }, + ); - test('Set up alias from new target page @ent @aws @docker', async ({ - page, - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - sshUser, - sshKeyPath, - targetAddress, - targetPort, - }) => { - await page.goto('/'); - let orgName; - let alias; - let connect; - const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 10); - try { - const orgsPage = new OrgsPage(page); - orgName = await orgsPage.createOrg(); - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - const projectsPage = new ProjectsPage(page); - await projectsPage.createProject(); - alias = 'example.alias.' + nanoid(); - const targetsPage = new TargetsPage(page); - const targetName = await targetsPage.createTargetWithAddressAndAlias( - targetAddress, - targetPort, - alias, - ); - const credentialStoresPage = new CredentialStoresPage(page); - await credentialStoresPage.createStaticCredentialStore(); - const credentialName = - await credentialStoresPage.createStaticCredentialKeyPair( - sshUser, - sshKeyPath, + test( + 'Set up alias from new target page', + { tag: ['@ent', '@aws', '@docker'] }, + async ({ + page, + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + sshUser, + sshKeyPath, + targetAddress, + targetPort, + }) => { + await page.goto('/'); + let orgName; + let alias; + let connect; + const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 10); + try { + const orgsPage = new OrgsPage(page); + orgName = await orgsPage.createOrg(); + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + const projectsPage = new ProjectsPage(page); + await projectsPage.createProject(); + alias = 'example.alias.' + nanoid(); + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createTargetWithAddressAndAlias( + targetAddress, + targetPort, + alias, + ); + const credentialStoresPage = new CredentialStoresPage(page); + await credentialStoresPage.createStaticCredentialStore(); + const credentialName = + await credentialStoresPage.createStaticCredentialKeyPair( + sshUser, + sshKeyPath, + ); + await targetsPage.addInjectedCredentialsToTarget( + targetName, + credentialName, ); - await targetsPage.addInjectedCredentialsToTarget( - targetName, - credentialName, - ); - // Connect to target using alias - connect = await boundaryCli.connectSshToAlias(alias); - const sessionsPage = new SessionsPage(page); - await sessionsPage.waitForSessionToBeVisible(targetName); - } finally { - if (connect) { - connect.kill('SIGTERM'); + // Connect to target using alias + connect = await boundaryCli.connectSshToAlias(alias); + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); + } finally { + if (connect) { + connect.kill('SIGTERM'); + } + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + + if (orgName) { + const orgId = await boundaryCli.getOrgIdFromName(orgName); + if (orgId) { + await boundaryCli.deleteScope(orgId); + } + if (alias) { + await boundaryCli.deleteAlias(alias); + } + } } - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); + }, + ); + + test( + 'Set up alias from aliases page', + { tag: ['@ent', '@aws', '@docker'] }, + async ({ + page, + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + sshUser, + sshKeyPath, + targetAddress, + targetPort, + }) => { + await page.goto('/'); + const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 10); + let orgId; + let alias; + let connect; + try { + const orgsPage = new OrgsPage(page); + const orgName = await orgsPage.createOrg(); + const projectsPage = new ProjectsPage(page); + const projectName = await projectsPage.createProject(); + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createSshTargetWithAddressEnt( + targetAddress, + targetPort, + ); + const credentialStoresPage = new CredentialStoresPage(page); + await credentialStoresPage.createStaticCredentialStore(); + const credentialName = + await credentialStoresPage.createStaticCredentialKeyPair( + sshUser, + sshKeyPath, + ); + await targetsPage.addInjectedCredentialsToTarget( + targetName, + credentialName, + ); + + // Create new alias from scope page + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + orgId = await boundaryCli.getOrgIdFromName(orgName); + const projectId = await boundaryCli.getProjectIdFromName( + orgId, + projectName, + ); + const targetId = await boundaryCli.getTargetIdFromName( + projectId, + targetName, + ); + + alias = 'example.alias.' + nanoid(); + const aliasesPage = new AliasesPage(page); + await aliasesPage.createAliasForTarget(alias, targetId); + connect = await boundaryCli.connectSshToAlias(alias); + await page + .getByRole('navigation', { name: 'General' }) + .getByRole('link', { name: 'Orgs' }) + .click(); + await page.getByRole('link', { name: orgName }).click(); + await page.getByRole('link', { name: projectName }).click(); + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); + } finally { + if (connect) { + connect.kill('SIGTERM'); + } + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); - if (orgName) { - const orgId = await boundaryCli.getOrgIdFromName(orgName); if (orgId) { await boundaryCli.deleteScope(orgId); } @@ -199,93 +298,6 @@ test.describe('Aliases (Enterprise)', () => { await boundaryCli.deleteAlias(alias); } } - } - }); - - test('Set up alias from aliases page @ent @aws @docker', async ({ - page, - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - sshUser, - sshKeyPath, - targetAddress, - targetPort, - }) => { - await page.goto('/'); - const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 10); - let orgId; - let alias; - let connect; - try { - const orgsPage = new OrgsPage(page); - const orgName = await orgsPage.createOrg(); - const projectsPage = new ProjectsPage(page); - const projectName = await projectsPage.createProject(); - const targetsPage = new TargetsPage(page); - const targetName = await targetsPage.createSshTargetWithAddressEnt( - targetAddress, - targetPort, - ); - const credentialStoresPage = new CredentialStoresPage(page); - await credentialStoresPage.createStaticCredentialStore(); - const credentialName = - await credentialStoresPage.createStaticCredentialKeyPair( - sshUser, - sshKeyPath, - ); - await targetsPage.addInjectedCredentialsToTarget( - targetName, - credentialName, - ); - - // Create new alias from scope page - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - orgId = await boundaryCli.getOrgIdFromName(orgName); - const projectId = await boundaryCli.getProjectIdFromName( - orgId, - projectName, - ); - const targetId = await boundaryCli.getTargetIdFromName( - projectId, - targetName, - ); - - alias = 'example.alias.' + nanoid(); - const aliasesPage = new AliasesPage(page); - await aliasesPage.createAliasForTarget(alias, targetId); - connect = await boundaryCli.connectSshToAlias(alias); - await page - .getByRole('navigation', { name: 'General' }) - .getByRole('link', { name: 'Orgs' }) - .click(); - await page.getByRole('link', { name: orgName }).click(); - await page.getByRole('link', { name: projectName }).click(); - const sessionsPage = new SessionsPage(page); - await sessionsPage.waitForSessionToBeVisible(targetName); - } finally { - if (connect) { - connect.kill('SIGTERM'); - } - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - - if (orgId) { - await boundaryCli.deleteScope(orgId); - } - if (alias) { - await boundaryCli.deleteAlias(alias); - } - } - }); + }, + ); }); diff --git a/e2e-tests/admin/tests/alias.spec.js b/e2e-tests/admin/tests/alias.spec.js index 6a582f211..b296e1606 100644 --- a/e2e-tests/admin/tests/alias.spec.js +++ b/e2e-tests/admin/tests/alias.spec.js @@ -19,150 +19,236 @@ test.beforeAll(async () => { }); test.describe('Aliases', () => { - test('Set up alias from target details page @ce @aws @docker', async ({ - page, - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - targetAddress, - targetPort, - sshUser, - sshKeyPath, - }) => { - await page.goto('/'); - let orgName; - let alias; - let connect; - const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 10); - try { - const orgsPage = new OrgsPage(page); - orgName = await orgsPage.createOrg(); - const projectsPage = new ProjectsPage(page); - await projectsPage.createProject(); - const targetsPage = new TargetsPage(page); - const targetName = await targetsPage.createTargetWithAddress( - targetAddress, - targetPort, - ); + test( + 'Set up alias from target details page', + { tag: ['@ce', '@aws', '@docker'] }, + async ({ + page, + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + targetAddress, + targetPort, + sshUser, + sshKeyPath, + }) => { + await page.goto('/'); + let orgName; + let alias; + let connect; + const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 10); + try { + const orgsPage = new OrgsPage(page); + orgName = await orgsPage.createOrg(); + const projectsPage = new ProjectsPage(page); + await projectsPage.createProject(); + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createTargetWithAddress( + targetAddress, + targetPort, + ); - // Create alias for target - const aliasName = 'Alias ' + nanoid(); - alias = 'example.alias.' + nanoid(); - await page.getByRole('link', { name: 'Add an alias' }).click(); - await page.getByLabel('Name').fill(aliasName); - await page.getByLabel('Description').fill('This is an automated test'); - await page.getByLabel('Alias Value').fill(alias); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); + // Create alias for target + const aliasName = 'Alias ' + nanoid(); + alias = 'example.alias.' + nanoid(); + await page.getByRole('link', { name: 'Add an alias' }).click(); + await page.getByLabel('Name').fill(aliasName); + await page.getByLabel('Description').fill('This is an automated test'); + await page.getByLabel('Alias Value').fill(alias); + await page.getByRole('button', { name: 'Save' }).click(); + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); - // Connect to target using alias - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - connect = await boundaryCli.connectToAlias(alias, sshUser, sshKeyPath); - const sessionsPage = new SessionsPage(page); - await sessionsPage.waitForSessionToBeVisible(targetName); + // Connect to target using alias + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + connect = await boundaryCli.connectToAlias(alias, sshUser, sshKeyPath); + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); - // Clear destination from alias - await page - .getByRole('navigation', { name: 'Resources' }) - .getByRole('link', { name: 'Targets' }) - .click(); - await page.getByRole('link', { name: targetName }).click(); - await page.getByRole('link', { name: alias }).click(); - // Note: On the Target details page, there is a section with the header - // "Aliases". The extra check here is to ensure that we are on the Alias - // details page and not the Target details page. - await expect(page.getByRole('heading', { name: 'Alias' })).not.toHaveText( - 'Aliases', - ); - await page.getByRole('button', { name: 'Manage' }).click(); - await page.getByText('Clear', { exact: true }).click(); - await page.getByText('OK', { exact: true }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - } finally { - if (connect) { - connect.kill('SIGTERM'); - } - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - if (orgName) { - const orgId = await boundaryCli.getOrgIdFromName(orgName); - if (orgId) { - await boundaryCli.deleteScope(orgId); + // Clear destination from alias + await page + .getByRole('navigation', { name: 'Resources' }) + .getByRole('link', { name: 'Targets' }) + .click(); + await page.getByRole('link', { name: targetName }).click(); + await page.getByRole('link', { name: alias }).click(); + // Note: On the Target details page, there is a section with the header + // "Aliases". The extra check here is to ensure that we are on the Alias + // details page and not the Target details page. + await expect( + page.getByRole('heading', { name: 'Alias' }), + ).not.toHaveText('Aliases'); + await page.getByRole('button', { name: 'Manage' }).click(); + await page.getByText('Clear', { exact: true }).click(); + await page.getByText('OK', { exact: true }).click(); + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); + } finally { + if (connect) { + connect.kill('SIGTERM'); + } + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + if (orgName) { + const orgId = await boundaryCli.getOrgIdFromName(orgName); + if (orgId) { + await boundaryCli.deleteScope(orgId); + } + } + if (alias) { + await boundaryCli.deleteAlias(alias); } } - if (alias) { - await boundaryCli.deleteAlias(alias); - } - } - }); + }, + ); - test('Set up alias from new target page @ce @aws @docker', async ({ - page, - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - targetAddress, - targetPort, - sshUser, - sshKeyPath, - }) => { - await page.goto('/'); - let orgName; - let alias; - let connect; - const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 10); - try { - const orgsPage = new OrgsPage(page); - orgName = await orgsPage.createOrg(); - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - const projectsPage = new ProjectsPage(page); - await projectsPage.createProject(); - alias = 'example.alias.' + nanoid(); - const targetsPage = new TargetsPage(page); - const targetName = await targetsPage.createTargetWithAddressAndAlias( - targetAddress, - targetPort, - alias, - ); + test( + 'Set up alias from new target page', + { tag: ['@ce', '@aws', '@docker'] }, + async ({ + page, + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + targetAddress, + targetPort, + sshUser, + sshKeyPath, + }) => { + await page.goto('/'); + let orgName; + let alias; + let connect; + const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 10); + try { + const orgsPage = new OrgsPage(page); + orgName = await orgsPage.createOrg(); + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + const projectsPage = new ProjectsPage(page); + await projectsPage.createProject(); + alias = 'example.alias.' + nanoid(); + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createTargetWithAddressAndAlias( + targetAddress, + targetPort, + alias, + ); - // Connect to target using alias - connect = await boundaryCli.connectToAlias(alias, sshUser, sshKeyPath); - const sessionsPage = new SessionsPage(page); - await sessionsPage.waitForSessionToBeVisible(targetName); - } finally { - if (connect) { - connect.kill('SIGTERM'); + // Connect to target using alias + connect = await boundaryCli.connectToAlias(alias, sshUser, sshKeyPath); + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); + } finally { + if (connect) { + connect.kill('SIGTERM'); + } + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + if (orgName) { + const orgId = await boundaryCli.getOrgIdFromName(orgName); + if (orgId) { + await boundaryCli.deleteScope(orgId); + } + if (alias) { + await boundaryCli.deleteAlias(alias); + } + } } - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - if (orgName) { - const orgId = await boundaryCli.getOrgIdFromName(orgName); + }, + ); + + test( + 'Set up alias from aliases page', + { tag: ['@ce', '@aws', '@docker'] }, + async ({ + page, + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + targetAddress, + targetPort, + sshUser, + sshKeyPath, + }) => { + await page.goto('/'); + const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 10); + let orgId; + let alias; + let connect; + try { + const orgsPage = new OrgsPage(page); + const orgName = await orgsPage.createOrg(); + const projectsPage = new ProjectsPage(page); + const projectName = await projectsPage.createProject(); + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createTargetWithAddress( + targetAddress, + targetPort, + ); + + // Create new alias from scope page + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + orgId = await boundaryCli.getOrgIdFromName(orgName); + const projectId = await boundaryCli.getProjectIdFromName( + orgId, + projectName, + ); + const targetId = await boundaryCli.getTargetIdFromName( + projectId, + targetName, + ); + + alias = 'example.alias.' + nanoid(); + const aliasesPage = new AliasesPage(page); + await aliasesPage.createAliasForTarget(alias, targetId); + await page.getByRole('link', { name: 'Orgs', exact: true }).click(); + await expect(page.getByRole('heading', { name: 'Orgs' })).toBeVisible(); + await page.getByRole('link', { name: orgName }).click(); + await page.getByRole('link', { name: projectName }).click(); + + connect = await boundaryCli.connectToAlias(alias, sshUser, sshKeyPath); + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); + } finally { + if (connect) { + connect.kill('SIGTERM'); + } + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); if (orgId) { await boundaryCli.deleteScope(orgId); } @@ -170,80 +256,6 @@ test.describe('Aliases', () => { await boundaryCli.deleteAlias(alias); } } - } - }); - - test('Set up alias from aliases page @ce @aws @docker', async ({ - page, - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - targetAddress, - targetPort, - sshUser, - sshKeyPath, - }) => { - await page.goto('/'); - const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 10); - let orgId; - let alias; - let connect; - try { - const orgsPage = new OrgsPage(page); - const orgName = await orgsPage.createOrg(); - const projectsPage = new ProjectsPage(page); - const projectName = await projectsPage.createProject(); - const targetsPage = new TargetsPage(page); - const targetName = await targetsPage.createTargetWithAddress( - targetAddress, - targetPort, - ); - - // Create new alias from scope page - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - orgId = await boundaryCli.getOrgIdFromName(orgName); - const projectId = await boundaryCli.getProjectIdFromName( - orgId, - projectName, - ); - const targetId = await boundaryCli.getTargetIdFromName( - projectId, - targetName, - ); - - alias = 'example.alias.' + nanoid(); - const aliasesPage = new AliasesPage(page); - await aliasesPage.createAliasForTarget(alias, targetId); - await page.getByRole('link', { name: 'Orgs', exact: true }).click(); - await expect(page.getByRole('heading', { name: 'Orgs' })).toBeVisible(); - await page.getByRole('link', { name: orgName }).click(); - await page.getByRole('link', { name: projectName }).click(); - - connect = await boundaryCli.connectToAlias(alias, sshUser, sshKeyPath); - const sessionsPage = new SessionsPage(page); - await sessionsPage.waitForSessionToBeVisible(targetName); - } finally { - if (connect) { - connect.kill('SIGTERM'); - } - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - if (orgId) { - await boundaryCli.deleteScope(orgId); - } - if (alias) { - await boundaryCli.deleteAlias(alias); - } - } - }); + }, + ); }); diff --git a/e2e-tests/admin/tests/auth-method-ldap.spec.js b/e2e-tests/admin/tests/auth-method-ldap.spec.js index 30a2db75d..8ac7fa288 100644 --- a/e2e-tests/admin/tests/auth-method-ldap.spec.js +++ b/e2e-tests/admin/tests/auth-method-ldap.spec.js @@ -21,288 +21,292 @@ test.beforeAll(async () => { await boundaryCli.checkBoundaryCli(); }); -test('Set up LDAP auth method @ce @ent @docker', async ({ - page, - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ldapAddr, - ldapAdminDn, - ldapAdminPassword, - ldapDomainDn, - ldapGroupName, - ldapUserName, - ldapUserPassword, -}) => { - await page.goto('/'); - let orgName; - try { - // Log in - const loginPage = new LoginPage(page); - await loginPage.login(adminLoginName, adminPassword); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText('Orgs'), - ).toBeVisible(); +test( + 'Set up LDAP auth method', + { tag: ['@ce', '@ent', '@docker'] }, + async ({ + page, + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ldapAddr, + ldapAdminDn, + ldapAdminPassword, + ldapDomainDn, + ldapGroupName, + ldapUserName, + ldapUserPassword, + }) => { + await page.goto('/'); + let orgName; + try { + // Log in + const loginPage = new LoginPage(page); + await loginPage.login(adminLoginName, adminPassword); + await expect( + page.getByRole('navigation', { name: 'breadcrumbs' }).getByText('Orgs'), + ).toBeVisible(); - // Create an LDAP auth method - const orgsPage = new OrgsPage(page); - orgName = await orgsPage.createOrg(); - await page - .getByRole('navigation', { name: 'IAM' }) - .getByRole('link', { name: 'Auth Methods' }) - .click(); - await page.getByRole('button', { name: 'New' }).click(); - await page.getByRole('link', { name: 'LDAP' }).click(); - - const ldapAuthMethodName = 'LDAP ' + nanoid(); - await page.getByLabel('Name').fill(ldapAuthMethodName); - await page.getByLabel('Description').fill('LDAP Auth Method'); - await page.getByLabel('Address').fill(ldapAddr); - await page.getByLabel('Bind DN').fill(ldapAdminDn); - await page.getByLabel('Bind Password').fill(ldapAdminPassword); - await page.getByRole('switch', { name: 'Discover DN' }).click(); - await page.getByLabel('User DN').fill(ldapDomainDn); - await page.getByLabel('User Attribute').fill('uid'); - await page.getByLabel('Group DN').fill(ldapDomainDn); - await page.getByRole('switch', { name: 'Enable Groups' }).click(); - - await page - .getByRole('group', { name: 'Account Attribute Maps' }) - .getByLabel('From Attribute') - .last() - .fill('cn'); - await page - .getByRole('group', { name: 'Account Attribute Maps' }) - .getByLabel('To Attribute') - .last() - .selectOption('fullName'); - await page - .getByRole('group', { name: 'Account Attribute Maps' }) - .getByRole('button', { name: 'Add' }) - .click(); - await page - .getByRole('group', { name: 'Account Attribute Maps' }) - .getByLabel('From Attribute') - .last() - .fill('mail'); - await page - .getByRole('group', { name: 'Account Attribute Maps' }) - .getByLabel('To Attribute') - .last() - .selectOption('email'); - await page - .getByRole('group', { name: 'Account Attribute Maps' }) - .getByRole('button', { name: 'Add' }) - .click(); - - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText(ldapAuthMethodName), - ).toBeVisible(); - - // Change state to active-public - await page.getByRole('button', { name: 'Inactive' }).click(); - await page.getByText('Public').click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - - // Create an LDAP account - const ldapAccountName = 'LDAP Account ' + nanoid(); - await page.getByText('Manage', { exact: true }).click(); - await page.getByRole('link', { name: 'Create Account' }).click(); - await page - .getByLabel('Name (Optional)', { exact: true }) - .fill(ldapAccountName); - await page.getByLabel('Description').fill('This is an automated test'); - await page.getByLabel('Login Name').fill(ldapUserName); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - - // Create an LDAP managed group - await page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText(ldapAuthMethodName) - .click(); - await page.getByText('Manage', { exact: true }).click(); - await page.getByRole('link', { name: 'Create Managed Group' }).click(); - const ldapManagedGroupName = 'LDAP Managed Group ' + nanoid(); - await page.getByLabel('Name (Optional)').fill(ldapManagedGroupName); - await page.getByLabel('Description').fill('This is an automated test'); - await page.getByRole('textbox', { name: 'Value' }).fill(ldapGroupName); - await page.getByRole('button', { name: 'Add' }).click(); - - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - - // Create a role and add LDAP managed group to role - const rolesPage = new RolesPage(page); - await rolesPage.createRole(); - await rolesPage.addPrincipalToRole(ldapManagedGroupName); - - // Create a user and attach LDAP account to it - const usersPage = new UsersPage(page); - const userName = await usersPage.createUser(); - await usersPage.addAccountToUser(ldapUserName); - - // Create a second auth method so that there's multiple auth methods on the - // login screen - const authMethodsPage = new AuthMethodsPage(page); - await authMethodsPage.createPasswordAuthMethod(); - - // Log in using ldap account - await loginPage.logout(adminLoginName); - - await page.getByText('Choose a different scope').click(); - await page.getByRole('link', { name: orgName }).click(); - await page.getByRole('link', { name: ldapAuthMethodName }).click(); - await loginPage.login(ldapUserName, ldapUserPassword); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText('Projects'), - ).toBeVisible(); - - // Log back in as an admin - await loginPage.logout(ldapUserName); - await loginPage.login(adminLoginName, adminPassword); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText('Orgs'), - ).toBeVisible(); - - // View the LDAP account and verify account attributes - await page.getByRole('link', { name: orgName }).click(); - await page - .getByRole('navigation', { name: 'IAM' }) - .getByRole('link', { name: 'Auth Methods' }) - .click(); - await page.getByRole('link', { name: ldapAuthMethodName }).click(); - await page.getByRole('link', { name: 'Accounts' }).click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText('Accounts'), - ).toBeVisible(); - - let headersCount = await page - .getByRole('table') - .getByRole('columnheader') - .count(); - let fullNameIndex; - let emailIndex; - for (let i = 0; i < headersCount; i++) { - const header = await page - .getByRole('table') - .getByRole('columnheader') - .nth(i) - .innerText(); - if (header == 'Full Name') { - fullNameIndex = i; - } else if (header == 'Email') { - emailIndex = i; - } - } - - await expect( - page - .getByRole('cell', { name: ldapAccountName }) - .locator('..') - .getByRole('cell') - .nth(fullNameIndex), - ).toHaveText(ldapUserName); - await expect( - page - .getByRole('cell', { name: ldapAccountName }) - .locator('..') - .getByRole('cell') - .nth(emailIndex), - ).toHaveText(ldapUserName + '@mail.com'); - - // View the Managed Group - await page.getByRole('link', { name: 'Managed Groups' }).click(); - await page.getByRole('link', { name: ldapManagedGroupName }).click(); - await page.getByRole('link', { name: 'Members' }).click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText('Members'), - ).toBeVisible(); - - headersCount = await page - .getByRole('table') - .getByRole('columnheader') - .count(); - for (let i = 0; i < headersCount; i++) { - const header = await page - .getByRole('table') - .getByRole('columnheader') - .nth(i) - .innerText(); - if (header == 'Full Name') { - fullNameIndex = i; - } else if (header == 'Email') { - emailIndex = i; - } - } - - await expect( - page - .getByRole('cell', { name: ldapAccountName }) - .locator('..') - .getByRole('cell') - .nth(fullNameIndex), - ).toHaveText(ldapUserName); - await expect( - page - .getByRole('cell', { name: ldapAccountName }) - .locator('..') - .getByRole('cell') - .nth(emailIndex), - ).toHaveText(ldapUserName + '@mail.com'); - - // View the User account and verify attributes - await page - .getByRole('navigation', { name: 'IAM' }) - .getByRole('link', { name: 'Users' }) - .click(); - await page.getByRole('link', { name: userName }).click(); - await page.getByRole('link', { name: 'Accounts' }).click(); - expect( + // Create an LDAP auth method + const orgsPage = new OrgsPage(page); + orgName = await orgsPage.createOrg(); await page + .getByRole('navigation', { name: 'IAM' }) + .getByRole('link', { name: 'Auth Methods' }) + .click(); + await page.getByRole('button', { name: 'New' }).click(); + await page.getByRole('link', { name: 'LDAP' }).click(); + + const ldapAuthMethodName = 'LDAP ' + nanoid(); + await page.getByLabel('Name').fill(ldapAuthMethodName); + await page.getByLabel('Description').fill('LDAP Auth Method'); + await page.getByLabel('Address').fill(ldapAddr); + await page.getByLabel('Bind DN').fill(ldapAdminDn); + await page.getByLabel('Bind Password').fill(ldapAdminPassword); + await page.getByRole('switch', { name: 'Discover DN' }).click(); + await page.getByLabel('User DN').fill(ldapDomainDn); + await page.getByLabel('User Attribute').fill('uid'); + await page.getByLabel('Group DN').fill(ldapDomainDn); + await page.getByRole('switch', { name: 'Enable Groups' }).click(); + + await page + .getByRole('group', { name: 'Account Attribute Maps' }) + .getByLabel('From Attribute') + .last() + .fill('cn'); + await page + .getByRole('group', { name: 'Account Attribute Maps' }) + .getByLabel('To Attribute') + .last() + .selectOption('fullName'); + await page + .getByRole('group', { name: 'Account Attribute Maps' }) + .getByRole('button', { name: 'Add' }) + .click(); + await page + .getByRole('group', { name: 'Account Attribute Maps' }) + .getByLabel('From Attribute') + .last() + .fill('mail'); + await page + .getByRole('group', { name: 'Account Attribute Maps' }) + .getByLabel('To Attribute') + .last() + .selectOption('email'); + await page + .getByRole('group', { name: 'Account Attribute Maps' }) + .getByRole('button', { name: 'Add' }) + .click(); + + await page.getByRole('button', { name: 'Save' }).click(); + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); + + await expect( + page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(ldapAuthMethodName), + ).toBeVisible(); + + // Change state to active-public + await page.getByRole('button', { name: 'Inactive' }).click(); + await page.getByText('Public').click(); + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); + + // Create an LDAP account + const ldapAccountName = 'LDAP Account ' + nanoid(); + await page.getByText('Manage', { exact: true }).click(); + await page.getByRole('link', { name: 'Create Account' }).click(); + await page + .getByLabel('Name (Optional)', { exact: true }) + .fill(ldapAccountName); + await page.getByLabel('Description').fill('This is an automated test'); + await page.getByLabel('Login Name').fill(ldapUserName); + await page.getByRole('button', { name: 'Save' }).click(); + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); + + // Create an LDAP managed group + await page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(ldapAuthMethodName) + .click(); + await page.getByText('Manage', { exact: true }).click(); + await page.getByRole('link', { name: 'Create Managed Group' }).click(); + const ldapManagedGroupName = 'LDAP Managed Group ' + nanoid(); + await page.getByLabel('Name (Optional)').fill(ldapManagedGroupName); + await page.getByLabel('Description').fill('This is an automated test'); + await page.getByRole('textbox', { name: 'Value' }).fill(ldapGroupName); + await page.getByRole('button', { name: 'Add' }).click(); + + await page.getByRole('button', { name: 'Save' }).click(); + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); + + // Create a role and add LDAP managed group to role + const rolesPage = new RolesPage(page); + await rolesPage.createRole(); + await rolesPage.addPrincipalToRole(ldapManagedGroupName); + + // Create a user and attach LDAP account to it + const usersPage = new UsersPage(page); + const userName = await usersPage.createUser(); + await usersPage.addAccountToUser(ldapUserName); + + // Create a second auth method so that there's multiple auth methods on the + // login screen + const authMethodsPage = new AuthMethodsPage(page); + await authMethodsPage.createPasswordAuthMethod(); + + // Log in using ldap account + await loginPage.logout(adminLoginName); + + await page.getByText('Choose a different scope').click(); + await page.getByRole('link', { name: orgName }).click(); + await page.getByRole('link', { name: ldapAuthMethodName }).click(); + await loginPage.login(ldapUserName, ldapUserPassword); + await expect( + page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText('Projects'), + ).toBeVisible(); + + // Log back in as an admin + await loginPage.logout(ldapUserName); + await loginPage.login(adminLoginName, adminPassword); + await expect( + page.getByRole('navigation', { name: 'breadcrumbs' }).getByText('Orgs'), + ).toBeVisible(); + + // View the LDAP account and verify account attributes + await page.getByRole('link', { name: orgName }).click(); + await page + .getByRole('navigation', { name: 'IAM' }) + .getByRole('link', { name: 'Auth Methods' }) + .click(); + await page.getByRole('link', { name: ldapAuthMethodName }).click(); + await page.getByRole('link', { name: 'Accounts' }).click(); + await expect( + page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText('Accounts'), + ).toBeVisible(); + + let headersCount = await page .getByRole('table') - .getByRole('row') - .nth(1) // Account row - .getByRole('cell') - .nth(0) // Name field - .innerText(), - ).toContain(ldapUserName + '@mail.com'); - } finally { - if (orgName) { - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - const orgId = await boundaryCli.getOrgIdFromName(orgName); - if (orgId) { - await boundaryCli.deleteScope(orgId); + .getByRole('columnheader') + .count(); + let fullNameIndex; + let emailIndex; + for (let i = 0; i < headersCount; i++) { + const header = await page + .getByRole('table') + .getByRole('columnheader') + .nth(i) + .innerText(); + if (header == 'Full Name') { + fullNameIndex = i; + } else if (header == 'Email') { + emailIndex = i; + } + } + + await expect( + page + .getByRole('cell', { name: ldapAccountName }) + .locator('..') + .getByRole('cell') + .nth(fullNameIndex), + ).toHaveText(ldapUserName); + await expect( + page + .getByRole('cell', { name: ldapAccountName }) + .locator('..') + .getByRole('cell') + .nth(emailIndex), + ).toHaveText(ldapUserName + '@mail.com'); + + // View the Managed Group + await page.getByRole('link', { name: 'Managed Groups' }).click(); + await page.getByRole('link', { name: ldapManagedGroupName }).click(); + await page.getByRole('link', { name: 'Members' }).click(); + await expect( + page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText('Members'), + ).toBeVisible(); + + headersCount = await page + .getByRole('table') + .getByRole('columnheader') + .count(); + for (let i = 0; i < headersCount; i++) { + const header = await page + .getByRole('table') + .getByRole('columnheader') + .nth(i) + .innerText(); + if (header == 'Full Name') { + fullNameIndex = i; + } else if (header == 'Email') { + emailIndex = i; + } + } + + await expect( + page + .getByRole('cell', { name: ldapAccountName }) + .locator('..') + .getByRole('cell') + .nth(fullNameIndex), + ).toHaveText(ldapUserName); + await expect( + page + .getByRole('cell', { name: ldapAccountName }) + .locator('..') + .getByRole('cell') + .nth(emailIndex), + ).toHaveText(ldapUserName + '@mail.com'); + + // View the User account and verify attributes + await page + .getByRole('navigation', { name: 'IAM' }) + .getByRole('link', { name: 'Users' }) + .click(); + await page.getByRole('link', { name: userName }).click(); + await page.getByRole('link', { name: 'Accounts' }).click(); + expect( + await page + .getByRole('table') + .getByRole('row') + .nth(1) // Account row + .getByRole('cell') + .nth(0) // Name field + .innerText(), + ).toContain(ldapUserName + '@mail.com'); + } finally { + if (orgName) { + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + const orgId = await boundaryCli.getOrgIdFromName(orgName); + if (orgId) { + await boundaryCli.deleteScope(orgId); + } } } - } -}); + }, +); diff --git a/e2e-tests/admin/tests/auth-method-oidc-vault.spec.js b/e2e-tests/admin/tests/auth-method-oidc-vault.spec.js index 41bdd2a25..e526b8f4d 100644 --- a/e2e-tests/admin/tests/auth-method-oidc-vault.spec.js +++ b/e2e-tests/admin/tests/auth-method-oidc-vault.spec.js @@ -26,242 +26,253 @@ test.beforeEach(async () => { execSync(`vault auth disable userpass`); }); -test('Set up OIDC auth method @ce @ent @docker @aws', async ({ - page, - context, - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - vaultAddr, -}) => { - await page.goto('/'); - let orgName; - let policyName; - try { - const userName = 'end-user'; - const password = 'password123'; - const email = 'vault@hashicorp.com'; - const { issuer, clientId, clientSecret, authPolicyName } = - await vaultCli.setupVaultOidc( - vaultAddr, - userName, - password, - email, +test( + 'Set up OIDC auth method', + { tag: ['@ce', '@ent', '@aws', '@docker'] }, + async ({ + page, + context, + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + vaultAddr, + }) => { + await page.goto('/'); + let orgName; + let policyName; + try { + const userName = 'end-user'; + const password = 'password123'; + const email = 'vault@hashicorp.com'; + const { issuer, clientId, clientSecret, authPolicyName } = + await vaultCli.setupVaultOidc( + vaultAddr, + userName, + password, + email, + baseURL, + ); + policyName = authPolicyName; + + // Log in + const loginPage = new LoginPage(page); + await loginPage.login(adminLoginName, adminPassword); + await expect( + page.getByRole('navigation', { name: 'breadcrumbs' }).getByText('Orgs'), + ).toBeVisible(); + + // Create OIDC Auth Method + const orgsPage = new OrgsPage(page); + orgName = await orgsPage.createOrg(); + const authMethodsPage = new AuthMethodsPage(page); + const oidcAuthMethodName = await authMethodsPage.createOidcAuthMethod( + issuer, + clientId, + clientSecret, baseURL, ); - policyName = authPolicyName; - // Log in - const loginPage = new LoginPage(page); - await loginPage.login(adminLoginName, adminPassword); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText('Orgs'), - ).toBeVisible(); + // Change OIDC Auth Method state to active-public + await page.getByRole('button', { name: 'Inactive' }).click(); + await page.getByText('Public').click(); + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); - // Create OIDC Auth Method - const orgsPage = new OrgsPage(page); - orgName = await orgsPage.createOrg(); - const authMethodsPage = new AuthMethodsPage(page); - const oidcAuthMethodName = await authMethodsPage.createOidcAuthMethod( - issuer, - clientId, - clientSecret, - baseURL, - ); + // Set auth method as primary + await page.getByText('Manage', { exact: true }).click(); + await page.getByRole('button', { name: 'Make Primary' }).click(); + await page.getByRole('button', { name: 'OK' }).click(); + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); - // Change OIDC Auth Method state to active-public - await page.getByRole('button', { name: 'Inactive' }).click(); - await page.getByText('Public').click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - - // Set auth method as primary - await page.getByText('Manage', { exact: true }).click(); - await page.getByRole('button', { name: 'Make Primary' }).click(); - await page.getByRole('button', { name: 'OK' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - - // Create an OIDC managed group - await page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText(oidcAuthMethodName) - .click(); - await page.getByText('Manage', { exact: true }).click(); - await page.getByRole('link', { name: 'Create Managed Group' }).click(); - const oidcManagedGroupName = 'OIDC Managed Group'; - await page.getByLabel('Name (Optional)').fill(oidcManagedGroupName); - await page.getByLabel('Description').fill('This is an automated test'); - await page.getByLabel('Filter').fill(`"engineering" in "/userinfo/groups"`); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - - // Create a role and add LDAP managed group to role - const rolesPage = new RolesPage(page); - await rolesPage.createRole(); - await rolesPage.addPrincipalToRole(oidcManagedGroupName); - - // Log in using oidc account - await loginPage.logout(adminLoginName); - await page.getByText('Choose a different scope').click(); - await page.getByRole('link', { name: orgName }).click(); - await page.getByRole('link', { name: oidcAuthMethodName }).click(); - const pagePromise = context.waitForEvent('page'); - await page.getByRole('button', { name: 'Sign In' }).click(); - const vaultPage = await pagePromise; - await vaultPage.getByLabel('Method').selectOption('Username'); - await vaultPage.getByLabel('Username').fill(userName); - await vaultPage.getByLabel('Password').fill(password); - await vaultPage.getByRole('button', { name: 'Sign In' }).click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText('Projects'), - ).toBeVisible(); - - // Log back in as an admin - // WORKAROUND: Currently, users logging using OIDC don't have a username - // displayed in the UI, so there's no simple locator to access this menu. - await page.locator('details').filter({ hasText: 'Sign Out' }).click(); - await page.getByRole('button', { name: 'Sign Out' }).click(); - await expect(page.getByRole('button', { name: 'Sign In' })).toBeVisible(); - await loginPage.login(adminLoginName, adminPassword); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText('Orgs'), - ).toBeVisible(); - - // View the OIDC account and verify account attributes - await page.getByRole('link', { name: orgName }).click(); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText(orgName), - ).toBeVisible(); - await page - .getByRole('navigation', { name: 'IAM' }) - .getByRole('link', { name: 'Auth Methods' }) - .click(); - await page.getByRole('link', { name: oidcAuthMethodName }).click(); - await page.getByRole('link', { name: 'Accounts' }).click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText('Accounts'), - ).toBeVisible(); - - let headersCount = await page - .getByRole('table') - .getByRole('columnheader') - .count(); - let fullNameIndex; - let emailIndex; - for (let i = 0; i < headersCount; i++) { - const header = await page - .getByRole('table') - .getByRole('columnheader') - .nth(i) - .innerText(); - if (header == 'Full Name') { - fullNameIndex = i; - } else if (header == 'Email') { - emailIndex = i; - } - } - - await expect( - page - .getByRole('cell', { name: userName }) - .locator('..') - .getByRole('cell') - .nth(fullNameIndex), - ).toHaveText(userName); - await expect( - page - .getByRole('cell', { name: userName }) - .locator('..') - .getByRole('cell') - .nth(emailIndex), - ).toHaveText(email); - - // View the OIDC Managed Group and verify member in managed group - await page.getByRole('link', { name: 'Managed Groups' }).click(); - await page.getByRole('link', { name: oidcManagedGroupName }).click(); - await page.getByRole('link', { name: 'Members' }).click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText('Members'), - ).toBeVisible(); - - headersCount = await page - .getByRole('table') - .getByRole('columnheader') - .count(); - for (let i = 0; i < headersCount; i++) { - const header = await page - .getByRole('table') - .getByRole('columnheader') - .nth(i) - .innerText(); - if (header == 'Full Name') { - fullNameIndex = i; - } else if (header == 'Email') { - emailIndex = i; - } - } - - await expect( - page - .getByRole('cell', { name: userName }) - .locator('..') - .getByRole('cell') - .nth(fullNameIndex), - ).toHaveText(userName); - await expect( - page - .getByRole('cell', { name: userName }) - .locator('..') - .getByRole('cell') - .nth(emailIndex), - ).toHaveText(email); - - // View the User account and verify attributes - await page - .getByRole('navigation', { name: 'IAM' }) - .getByRole('link', { name: 'Users' }) - .click(); - await page.getByRole('cell', { hasText: email }).getByRole('link').click(); - await page.getByRole('link', { name: 'Accounts' }).click(); - expect( + // Create an OIDC managed group await page - .getByRole('table') - .getByRole('row') - .nth(1) // Account row - .getByRole('cell') - .nth(0) // Name field - .innerText(), - ).toContain(email); - } finally { - execSync(`vault auth disable userpass`); - execSync(`vault policy delete ${policyName}`); + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(oidcAuthMethodName) + .click(); + await page.getByText('Manage', { exact: true }).click(); + await page.getByRole('link', { name: 'Create Managed Group' }).click(); + const oidcManagedGroupName = 'OIDC Managed Group'; + await page.getByLabel('Name (Optional)').fill(oidcManagedGroupName); + await page.getByLabel('Description').fill('This is an automated test'); + await page + .getByLabel('Filter') + .fill(`"engineering" in "/userinfo/groups"`); + await page.getByRole('button', { name: 'Save' }).click(); + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); - if (orgName) { - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - const orgId = await boundaryCli.getOrgIdFromName(orgName); - if (orgId) { - await boundaryCli.deleteScope(orgId); + // Create a role and add LDAP managed group to role + const rolesPage = new RolesPage(page); + await rolesPage.createRole(); + await rolesPage.addPrincipalToRole(oidcManagedGroupName); + + // Log in using oidc account + await loginPage.logout(adminLoginName); + await page.getByText('Choose a different scope').click(); + await page.getByRole('link', { name: orgName }).click(); + await page.getByRole('link', { name: oidcAuthMethodName }).click(); + const pagePromise = context.waitForEvent('page'); + await page.getByRole('button', { name: 'Sign In' }).click(); + const vaultPage = await pagePromise; + await vaultPage.getByLabel('Method').selectOption('Username'); + await vaultPage.getByLabel('Username').fill(userName); + await vaultPage.getByLabel('Password').fill(password); + await vaultPage.getByRole('button', { name: 'Sign In' }).click(); + await expect( + page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText('Projects'), + ).toBeVisible(); + + // Log back in as an admin + // WORKAROUND: Currently, users logging using OIDC don't have a username + // displayed in the UI, so there's no simple locator to access this menu. + await page.locator('details').filter({ hasText: 'Sign Out' }).click(); + await page.getByRole('button', { name: 'Sign Out' }).click(); + await expect(page.getByRole('button', { name: 'Sign In' })).toBeVisible(); + await loginPage.login(adminLoginName, adminPassword); + await expect( + page.getByRole('navigation', { name: 'breadcrumbs' }).getByText('Orgs'), + ).toBeVisible(); + + // View the OIDC account and verify account attributes + await page.getByRole('link', { name: orgName }).click(); + await expect( + page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(orgName), + ).toBeVisible(); + await page + .getByRole('navigation', { name: 'IAM' }) + .getByRole('link', { name: 'Auth Methods' }) + .click(); + await page.getByRole('link', { name: oidcAuthMethodName }).click(); + await page.getByRole('link', { name: 'Accounts' }).click(); + await expect( + page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText('Accounts'), + ).toBeVisible(); + + let headersCount = await page + .getByRole('table') + .getByRole('columnheader') + .count(); + let fullNameIndex; + let emailIndex; + for (let i = 0; i < headersCount; i++) { + const header = await page + .getByRole('table') + .getByRole('columnheader') + .nth(i) + .innerText(); + if (header == 'Full Name') { + fullNameIndex = i; + } else if (header == 'Email') { + emailIndex = i; + } + } + + await expect( + page + .getByRole('cell', { name: userName }) + .locator('..') + .getByRole('cell') + .nth(fullNameIndex), + ).toHaveText(userName); + await expect( + page + .getByRole('cell', { name: userName }) + .locator('..') + .getByRole('cell') + .nth(emailIndex), + ).toHaveText(email); + + // View the OIDC Managed Group and verify member in managed group + await page.getByRole('link', { name: 'Managed Groups' }).click(); + await page.getByRole('link', { name: oidcManagedGroupName }).click(); + await page.getByRole('link', { name: 'Members' }).click(); + await expect( + page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText('Members'), + ).toBeVisible(); + + headersCount = await page + .getByRole('table') + .getByRole('columnheader') + .count(); + for (let i = 0; i < headersCount; i++) { + const header = await page + .getByRole('table') + .getByRole('columnheader') + .nth(i) + .innerText(); + if (header == 'Full Name') { + fullNameIndex = i; + } else if (header == 'Email') { + emailIndex = i; + } + } + + await expect( + page + .getByRole('cell', { name: userName }) + .locator('..') + .getByRole('cell') + .nth(fullNameIndex), + ).toHaveText(userName); + await expect( + page + .getByRole('cell', { name: userName }) + .locator('..') + .getByRole('cell') + .nth(emailIndex), + ).toHaveText(email); + + // View the User account and verify attributes + await page + .getByRole('navigation', { name: 'IAM' }) + .getByRole('link', { name: 'Users' }) + .click(); + await page + .getByRole('cell', { hasText: email }) + .getByRole('link') + .click(); + await page.getByRole('link', { name: 'Accounts' }).click(); + expect( + await page + .getByRole('table') + .getByRole('row') + .nth(1) // Account row + .getByRole('cell') + .nth(0) // Name field + .innerText(), + ).toContain(email); + } finally { + execSync(`vault auth disable userpass`); + execSync(`vault policy delete ${policyName}`); + + if (orgName) { + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + const orgId = await boundaryCli.getOrgIdFromName(orgName); + if (orgId) { + await boundaryCli.deleteScope(orgId); + } } } - } -}); + }, +); diff --git a/e2e-tests/admin/tests/auth-method-password.spec.js b/e2e-tests/admin/tests/auth-method-password.spec.js index a4db707e9..736793a28 100644 --- a/e2e-tests/admin/tests/auth-method-password.spec.js +++ b/e2e-tests/admin/tests/auth-method-password.spec.js @@ -20,118 +20,124 @@ test.beforeAll(async () => { await boundaryCli.checkBoundaryCli(); }); -test('Verify new auth-method can be created and assigned to users @ce @ent @aws @docker', async ({ - page, - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, -}) => { - await page.goto('/'); - let orgName; - let authMethodName; - try { - // Log in - const loginPage = new LoginPage(page); - await loginPage.login(adminLoginName, adminPassword); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText('Orgs'), - ).toBeVisible(); +test( + 'Verify new auth-method can be created and assigned to users', + { tag: ['@ce', '@ent', '@aws', '@docker'] }, + async ({ + page, + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + }) => { + await page.goto('/'); + let orgName; + let authMethodName; + try { + // Log in + const loginPage = new LoginPage(page); + await loginPage.login(adminLoginName, adminPassword); + await expect( + page.getByRole('navigation', { name: 'breadcrumbs' }).getByText('Orgs'), + ).toBeVisible(); - // Create a new password auth method and account - const orgsPage = new OrgsPage(page); - orgName = await orgsPage.createOrg(); + // Create a new password auth method and account + const orgsPage = new OrgsPage(page); + orgName = await orgsPage.createOrg(); - const authMethodsPage = new AuthMethodsPage(page); - authMethodName = await authMethodsPage.createPasswordAuthMethod(); - const username = 'test-user'; - const password = 'password'; + const authMethodsPage = new AuthMethodsPage(page); + authMethodName = await authMethodsPage.createPasswordAuthMethod(); + const username = 'test-user'; + const password = 'password'; - await authMethodsPage.createPasswordAccount(username, password); - await authMethodsPage.makeAuthMethodPrimary(); - const usersPage = new UsersPage(page); - await usersPage.createUser(); - await usersPage.addAccountToUser(username); + await authMethodsPage.createPasswordAccount(username, password); + await authMethodsPage.makeAuthMethodPrimary(); + const usersPage = new UsersPage(page); + await usersPage.createUser(); + await usersPage.addAccountToUser(username); - // Log in using new account - await loginPage.logout(adminLoginName); - await page.getByText('Choose a different scope').click(); - await page.getByRole('link', { name: orgName }).click(); - await page.getByRole('link', { name: authMethodName }).click(); + // Log in using new account + await loginPage.logout(adminLoginName); + await page.getByText('Choose a different scope').click(); + await page.getByRole('link', { name: orgName }).click(); + await page.getByRole('link', { name: authMethodName }).click(); - await loginPage.login(username, password); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText('Projects'), - ).toBeVisible(); + await loginPage.login(username, password); + await expect( + page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText('Projects'), + ).toBeVisible(); - // Log back in as admin - await loginPage.logout(username); - await loginPage.login(adminLoginName, adminPassword); + // Log back in as admin + await loginPage.logout(username); + await loginPage.login(adminLoginName, adminPassword); - // Set a new password on the account - await page - .getByRole('navigation', { name: 'General' }) - .getByRole('link', { name: 'Orgs' }) - .click(); - await page.getByRole('link', { name: orgName }).click(); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText(orgName), - ).toBeVisible(); - await page - .getByRole('navigation', { name: 'IAM' }) - .getByRole('link', { name: 'Auth Methods' }) - .click(); - await page.getByRole('link', { name: authMethodName }).click(); - await page.getByRole('link', { name: 'Accounts', exact: true }).click(); - await page.getByRole('link', { name: username }).click(); - const newPassword = 'new-password'; - await authMethodsPage.setPasswordToAccount(newPassword); + // Set a new password on the account + await page + .getByRole('navigation', { name: 'General' }) + .getByRole('link', { name: 'Orgs' }) + .click(); + await page.getByRole('link', { name: orgName }).click(); + await expect( + page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(orgName), + ).toBeVisible(); + await page + .getByRole('navigation', { name: 'IAM' }) + .getByRole('link', { name: 'Auth Methods' }) + .click(); + await page.getByRole('link', { name: authMethodName }).click(); + await page.getByRole('link', { name: 'Accounts', exact: true }).click(); + await page.getByRole('link', { name: username }).click(); + const newPassword = 'new-password'; + await authMethodsPage.setPasswordToAccount(newPassword); - // Log in using new password - await loginPage.logout(adminLoginName); + // Log in using new password + await loginPage.logout(adminLoginName); - await page.getByText('Choose a different scope').click(); - await page.getByRole('link', { name: orgName }).click(); - await page.getByRole('link', { name: authMethodName }).click(); - await loginPage.login(username, newPassword); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText('Projects'), - ).toBeVisible(); - } finally { - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - - // There is an issue with deleting an org that has an auth method unless you - // delete the auth method first - if (authMethodName) { - const authMethods = JSON.parse( - execSync('boundary auth-methods list --recursive -format json'), + await page.getByText('Choose a different scope').click(); + await page.getByRole('link', { name: orgName }).click(); + await page.getByRole('link', { name: authMethodName }).click(); + await loginPage.login(username, newPassword); + await expect( + page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText('Projects'), + ).toBeVisible(); + } finally { + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, ); - const authMethod = authMethods.items.filter( - (obj) => obj.name == authMethodName, - )[0]; - if (authMethod) { - try { - execSync('boundary auth-methods delete -id=' + authMethod.id); - } catch (e) { - console.log(`${e.stderr}`); + + // There is an issue with deleting an org that has an auth method unless you + // delete the auth method first + if (authMethodName) { + const authMethods = JSON.parse( + execSync('boundary auth-methods list --recursive -format json'), + ); + const authMethod = authMethods.items.filter( + (obj) => obj.name == authMethodName, + )[0]; + if (authMethod) { + try { + execSync('boundary auth-methods delete -id=' + authMethod.id); + } catch (e) { + console.log(`${e.stderr}`); + } + } + } + + if (orgName) { + const orgId = await boundaryCli.getOrgIdFromName(orgName); + if (orgId) { + await boundaryCli.deleteScope(orgId); } } } - - if (orgName) { - const orgId = await boundaryCli.getOrgIdFromName(orgName); - if (orgId) { - await boundaryCli.deleteScope(orgId); - } - } - } -}); + }, +); diff --git a/e2e-tests/admin/tests/credential-store-static-ent.spec.js b/e2e-tests/admin/tests/credential-store-static-ent.spec.js index 6579bdaee..a9459a04a 100644 --- a/e2e-tests/admin/tests/credential-store-static-ent.spec.js +++ b/e2e-tests/admin/tests/credential-store-static-ent.spec.js @@ -18,120 +18,93 @@ test.beforeAll(async () => { await boundaryCli.checkBoundaryCli(); }); -test('Multiple Credential Stores (ENT) @ent @aws @docker', async ({ - page, - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - sshUser, - sshKeyPath, - targetAddress, - targetPort, -}) => { - let orgName; - try { - await page.goto('/'); +test( + 'Multiple Credential Stores (ENT)', + { tag: ['@ent', '@aws', '@docker'] }, + async ({ + page, + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + sshUser, + sshKeyPath, + targetAddress, + targetPort, + }) => { + let orgName; + try { + await page.goto('/'); - const orgsPage = new OrgsPage(page); - orgName = await orgsPage.createOrg(); - const projectsPage = new ProjectsPage(page); - const projectName = await projectsPage.createProject(); - const targetsPage = new TargetsPage(page); - const targetName = await targetsPage.createSshTargetWithAddressEnt( - targetAddress, - targetPort, - ); - const credentialStoresPage = new CredentialStoresPage(page); - await credentialStoresPage.createStaticCredentialStore(); - const credentialName = - await credentialStoresPage.createStaticCredentialKeyPair( - sshUser, - sshKeyPath, + const orgsPage = new OrgsPage(page); + orgName = await orgsPage.createOrg(); + const projectsPage = new ProjectsPage(page); + const projectName = await projectsPage.createProject(); + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createSshTargetWithAddressEnt( + targetAddress, + targetPort, ); - const credentialName2 = - await credentialStoresPage.createStaticCredentialUsernamePassword( - sshUser, - 'testPassword', + const credentialStoresPage = new CredentialStoresPage(page); + await credentialStoresPage.createStaticCredentialStore(); + const credentialName = + await credentialStoresPage.createStaticCredentialKeyPair( + sshUser, + sshKeyPath, + ); + const credentialName2 = + await credentialStoresPage.createStaticCredentialUsernamePassword( + sshUser, + 'testPassword', + ); + + await targetsPage.addBrokeredCredentialsToTarget( + targetName, + credentialName, + ); + await targetsPage.addBrokeredCredentialsToTarget( + targetName, + credentialName2, ); - await targetsPage.addBrokeredCredentialsToTarget( - targetName, - credentialName, - ); - await targetsPage.addBrokeredCredentialsToTarget( - targetName, - credentialName2, - ); + // Remove a credential from the target + await page + .getByRole('link', { name: credentialName2 }) + .locator('..') + .locator('..') + .getByRole('button', { name: 'Manage' }) + .click(); + await page.getByRole('button', { name: 'Remove' }).click(); + await page.getByRole('button', { name: 'OK', exact: true }).click(); + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); - // Remove a credential from the target - await page - .getByRole('link', { name: credentialName2 }) - .locator('..') - .locator('..') - .getByRole('button', { name: 'Manage' }) - .click(); - await page.getByRole('button', { name: 'Remove' }).click(); - await page.getByRole('button', { name: 'OK', exact: true }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); + await targetsPage.addInjectedCredentialsToTarget( + targetName, + credentialName, + ); + await targetsPage.addInjectedCredentialsToTarget( + targetName, + credentialName2, + ); - await targetsPage.addInjectedCredentialsToTarget( - targetName, - credentialName, - ); - await targetsPage.addInjectedCredentialsToTarget( - targetName, - credentialName2, - ); + // Remove a credential from the target + await page + .getByRole('link', { name: credentialName2 }) + .locator('..') + .locator('..') + .getByRole('button', { name: 'Manage' }) + .click(); + await page.getByRole('button', { name: 'Remove' }).click(); + await page.getByRole('button', { name: 'OK', exact: true }).click(); + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); - // Remove a credential from the target - await page - .getByRole('link', { name: credentialName2 }) - .locator('..') - .locator('..') - .getByRole('button', { name: 'Manage' }) - .click(); - await page.getByRole('button', { name: 'Remove' }).click(); - await page.getByRole('button', { name: 'OK', exact: true }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - - // Verify credentials - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - const orgId = await boundaryCli.getOrgIdFromName(orgName); - const projectId = await boundaryCli.getProjectIdFromName( - orgId, - projectName, - ); - const targetId = await boundaryCli.getTargetIdFromName( - projectId, - targetName, - ); - const session = await boundaryCli.authorizeSessionByTargetId(targetId); - const retrievedUser = session.item.credentials[0].credential.username; - const retrievedKey = session.item.credentials[0].credential.private_key; - - expect(retrievedUser).toBe(sshUser); - - const keyData = await readFile(sshKeyPath, { - encoding: 'utf-8', - }); - expect(retrievedKey).toBe(keyData); - - const sessionsPage = new SessionsPage(page); - await sessionsPage.waitForSessionToBeVisible(targetName); - } finally { - if (orgName) { + // Verify credentials await boundaryCli.authenticateBoundary( baseURL, adminAuthMethodId, @@ -139,9 +112,40 @@ test('Multiple Credential Stores (ENT) @ent @aws @docker', async ({ adminPassword, ); const orgId = await boundaryCli.getOrgIdFromName(orgName); - if (orgId) { - await boundaryCli.deleteScope(orgId); + const projectId = await boundaryCli.getProjectIdFromName( + orgId, + projectName, + ); + const targetId = await boundaryCli.getTargetIdFromName( + projectId, + targetName, + ); + const session = await boundaryCli.authorizeSessionByTargetId(targetId); + const retrievedUser = session.item.credentials[0].credential.username; + const retrievedKey = session.item.credentials[0].credential.private_key; + + expect(retrievedUser).toBe(sshUser); + + const keyData = await readFile(sshKeyPath, { + encoding: 'utf-8', + }); + expect(retrievedKey).toBe(keyData); + + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); + } finally { + if (orgName) { + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + const orgId = await boundaryCli.getOrgIdFromName(orgName); + if (orgId) { + await boundaryCli.deleteScope(orgId); + } } } - } -}); + }, +); diff --git a/e2e-tests/admin/tests/credential-store-static.spec.js b/e2e-tests/admin/tests/credential-store-static.spec.js index c3dbf33c5..da634d563 100644 --- a/e2e-tests/admin/tests/credential-store-static.spec.js +++ b/e2e-tests/admin/tests/credential-store-static.spec.js @@ -39,58 +39,31 @@ test.beforeEach(async ({ page, targetAddress, targetPort }) => { await credentialStoresPage.createStaticCredentialStore(); }); -test('Static Credential Store (User & Key Pair) @ce @aws @docker', async ({ - page, - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - sshUser, - sshKeyPath, -}) => { - try { - const credentialStoresPage = new CredentialStoresPage(page); - const credentialName = - await credentialStoresPage.createStaticCredentialKeyPair( - sshUser, - sshKeyPath, +test( + 'Static Credential Store (User & Key Pair)', + { tag: ['@ce', '@aws', '@docker'] }, + async ({ + page, + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + sshUser, + sshKeyPath, + }) => { + try { + const credentialStoresPage = new CredentialStoresPage(page); + const credentialName = + await credentialStoresPage.createStaticCredentialKeyPair( + sshUser, + sshKeyPath, + ); + const targetsPage = new TargetsPage(page); + await targetsPage.addBrokeredCredentialsToTarget( + targetName, + credentialName, ); - const targetsPage = new TargetsPage(page); - await targetsPage.addBrokeredCredentialsToTarget( - targetName, - credentialName, - ); - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - const orgId = await boundaryCli.getOrgIdFromName(orgName); - const projectId = await boundaryCli.getProjectIdFromName( - orgId, - projectName, - ); - const targetId = await boundaryCli.getTargetIdFromName( - projectId, - targetName, - ); - const session = await boundaryCli.authorizeSessionByTargetId(targetId); - const retrievedUser = session.item.credentials[0].credential.username; - const retrievedKey = session.item.credentials[0].credential.private_key; - - expect(retrievedUser).toBe(sshUser); - - const keyData = await readFile(sshKeyPath, { - encoding: 'utf-8', - }); - expect(retrievedKey).toBe(keyData); - - const sessionsPage = new SessionsPage(page); - await sessionsPage.waitForSessionToBeVisible(targetName); - } finally { - if (orgName) { await boundaryCli.authenticateBoundary( baseURL, adminAuthMethodId, @@ -98,61 +71,69 @@ test('Static Credential Store (User & Key Pair) @ce @aws @docker', async ({ adminPassword, ); const orgId = await boundaryCli.getOrgIdFromName(orgName); - if (orgId) { - await boundaryCli.deleteScope(orgId); + const projectId = await boundaryCli.getProjectIdFromName( + orgId, + projectName, + ); + const targetId = await boundaryCli.getTargetIdFromName( + projectId, + targetName, + ); + const session = await boundaryCli.authorizeSessionByTargetId(targetId); + const retrievedUser = session.item.credentials[0].credential.username; + const retrievedKey = session.item.credentials[0].credential.private_key; + + expect(retrievedUser).toBe(sshUser); + + const keyData = await readFile(sshKeyPath, { + encoding: 'utf-8', + }); + expect(retrievedKey).toBe(keyData); + + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); + } finally { + if (orgName) { + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + const orgId = await boundaryCli.getOrgIdFromName(orgName); + if (orgId) { + await boundaryCli.deleteScope(orgId); + } } } - } -}); + }, +); -test('Static Credential Store (Username & Password) @ce @aws @docker', async ({ - page, - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - sshUser, -}) => { - try { - const testPassword = 'password'; - const credentialStoresPage = new CredentialStoresPage(page); - const credentialName = - await credentialStoresPage.createStaticCredentialUsernamePassword( - sshUser, - testPassword, +test( + 'Static Credential Store (Username & Password)', + { tag: ['@ce', '@aws', '@docker'] }, + async ({ + page, + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + sshUser, + }) => { + try { + const testPassword = 'password'; + const credentialStoresPage = new CredentialStoresPage(page); + const credentialName = + await credentialStoresPage.createStaticCredentialUsernamePassword( + sshUser, + testPassword, + ); + const targetsPage = new TargetsPage(page); + await targetsPage.addBrokeredCredentialsToTarget( + targetName, + credentialName, ); - const targetsPage = new TargetsPage(page); - await targetsPage.addBrokeredCredentialsToTarget( - targetName, - credentialName, - ); - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - const orgId = await boundaryCli.getOrgIdFromName(orgName); - const projectId = await boundaryCli.getProjectIdFromName( - orgId, - projectName, - ); - const targetId = await boundaryCli.getTargetIdFromName( - projectId, - targetName, - ); - const session = await boundaryCli.authorizeSessionByTargetId(targetId); - const retrievedUser = session.item.credentials[0].credential.username; - const retrievedPassword = session.item.credentials[0].credential.password; - - expect(retrievedUser).toBe(sshUser); - expect(retrievedPassword).toBe(testPassword); - - const sessionsPage = new SessionsPage(page); - await sessionsPage.waitForSessionToBeVisible(targetName); - } finally { - if (orgName) { await boundaryCli.authenticateBoundary( baseURL, adminAuthMethodId, @@ -160,81 +141,88 @@ test('Static Credential Store (Username & Password) @ce @aws @docker', async ({ adminPassword, ); const orgId = await boundaryCli.getOrgIdFromName(orgName); - if (orgId) { - await boundaryCli.deleteScope(orgId); + const projectId = await boundaryCli.getProjectIdFromName( + orgId, + projectName, + ); + const targetId = await boundaryCli.getTargetIdFromName( + projectId, + targetName, + ); + const session = await boundaryCli.authorizeSessionByTargetId(targetId); + const retrievedUser = session.item.credentials[0].credential.username; + const retrievedPassword = session.item.credentials[0].credential.password; + + expect(retrievedUser).toBe(sshUser); + expect(retrievedPassword).toBe(testPassword); + + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); + } finally { + if (orgName) { + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + const orgId = await boundaryCli.getOrgIdFromName(orgName); + if (orgId) { + await boundaryCli.deleteScope(orgId); + } } } - } -}); + }, +); -test('Static Credential Store (JSON) @ce @aws @docker', async ({ - page, - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, -}) => { - try { - const credentialName = 'Credential ' + nanoid(); - await page.getByRole('link', { name: 'Credentials', exact: true }).click(); - await page.getByRole('link', { name: 'New', exact: true }).click(); - await page.getByLabel('Name (Optional)').fill(credentialName); - await page.getByLabel('Description').fill('This is an automated test'); - await page.getByRole('group', { name: 'Type' }).getByLabel('JSON').click(); - await page.getByText('{}').click(); - const testName = 'name-json'; - const testPassword = 'password-json'; - const testId = 'id-json'; - await page.keyboard.type( - `"username": "${testName}", +test( + 'Static Credential Store (JSON)', + { tag: ['@ce', '@aws', '@docker'] }, + async ({ + page, + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + }) => { + try { + const credentialName = 'Credential ' + nanoid(); + await page + .getByRole('link', { name: 'Credentials', exact: true }) + .click(); + await page.getByRole('link', { name: 'New', exact: true }).click(); + await page.getByLabel('Name (Optional)').fill(credentialName); + await page.getByLabel('Description').fill('This is an automated test'); + await page + .getByRole('group', { name: 'Type' }) + .getByLabel('JSON') + .click(); + await page.getByText('{}').click(); + const testName = 'name-json'; + const testPassword = 'password-json'; + const testId = 'id-json'; + await page.keyboard.type( + `"username": "${testName}", "password": "${testPassword}", "id": "${testId}"`, - ); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText(credentialName), - ).toBeVisible(); + ); + await page.getByRole('button', { name: 'Save' }).click(); + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); + await expect( + page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(credentialName), + ).toBeVisible(); - const targetsPage = new TargetsPage(page); - await targetsPage.addBrokeredCredentialsToTarget( - targetName, - credentialName, - ); + const targetsPage = new TargetsPage(page); + await targetsPage.addBrokeredCredentialsToTarget( + targetName, + credentialName, + ); - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - const orgId = await boundaryCli.getOrgIdFromName(orgName); - const projectId = await boundaryCli.getProjectIdFromName( - orgId, - projectName, - ); - const targetId = await boundaryCli.getTargetIdFromName( - projectId, - targetName, - ); - const session = await boundaryCli.authorizeSessionByTargetId(targetId); - const retrievedUser = session.item.credentials[0].credential.username; - const retrievedPassword = session.item.credentials[0].credential.password; - const retrievedId = session.item.credentials[0].credential.id; - - expect(retrievedUser).toBe(testName); - expect(retrievedPassword).toBe(testPassword); - expect(retrievedId).toBe(testId); - - const sessionsPage = new SessionsPage(page); - await sessionsPage.waitForSessionToBeVisible(targetName); - } finally { - if (orgName) { await boundaryCli.authenticateBoundary( baseURL, adminAuthMethodId, @@ -242,70 +230,103 @@ test('Static Credential Store (JSON) @ce @aws @docker', async ({ adminPassword, ); const orgId = await boundaryCli.getOrgIdFromName(orgName); - if (orgId) { - await boundaryCli.deleteScope(orgId); + const projectId = await boundaryCli.getProjectIdFromName( + orgId, + projectName, + ); + const targetId = await boundaryCli.getTargetIdFromName( + projectId, + targetName, + ); + const session = await boundaryCli.authorizeSessionByTargetId(targetId); + const retrievedUser = session.item.credentials[0].credential.username; + const retrievedPassword = session.item.credentials[0].credential.password; + const retrievedId = session.item.credentials[0].credential.id; + + expect(retrievedUser).toBe(testName); + expect(retrievedPassword).toBe(testPassword); + expect(retrievedId).toBe(testId); + + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); + } finally { + if (orgName) { + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + const orgId = await boundaryCli.getOrgIdFromName(orgName); + if (orgId) { + await boundaryCli.deleteScope(orgId); + } } } - } -}); + }, +); -test('Multiple Credential Stores (CE) @ce @aws @docker', async ({ - page, - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - sshUser, - sshKeyPath, -}) => { - try { - const credentialStoresPage = new CredentialStoresPage(page); - const credentialName = - await credentialStoresPage.createStaticCredentialKeyPair( - sshUser, - sshKeyPath, +test( + 'Multiple Credential Stores (CE)', + { tag: ['@ce', '@aws', '@docker'] }, + async ({ + page, + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + sshUser, + sshKeyPath, + }) => { + try { + const credentialStoresPage = new CredentialStoresPage(page); + const credentialName = + await credentialStoresPage.createStaticCredentialKeyPair( + sshUser, + sshKeyPath, + ); + const credentialName2 = + await credentialStoresPage.createStaticCredentialUsernamePassword( + sshUser, + 'testPassword', + ); + + const targetsPage = new TargetsPage(page); + await targetsPage.addBrokeredCredentialsToTarget( + targetName, + credentialName, ); - const credentialName2 = - await credentialStoresPage.createStaticCredentialUsernamePassword( - sshUser, - 'testPassword', + await targetsPage.addBrokeredCredentialsToTarget( + targetName, + credentialName2, ); - const targetsPage = new TargetsPage(page); - await targetsPage.addBrokeredCredentialsToTarget( - targetName, - credentialName, - ); - await targetsPage.addBrokeredCredentialsToTarget( - targetName, - credentialName2, - ); - - // Remove the host source from the target - await page - .getByRole('link', { name: credentialName2 }) - .locator('..') - .locator('..') - .getByRole('button', { name: 'Manage' }) - .click(); - await page.getByRole('button', { name: 'Remove' }).click(); - await page.getByRole('button', { name: 'OK', exact: true }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - } finally { - if (orgName) { - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - const orgId = await boundaryCli.getOrgIdFromName(orgName); - if (orgId) { - await boundaryCli.deleteScope(orgId); + // Remove the host source from the target + await page + .getByRole('link', { name: credentialName2 }) + .locator('..') + .locator('..') + .getByRole('button', { name: 'Manage' }) + .click(); + await page.getByRole('button', { name: 'Remove' }).click(); + await page.getByRole('button', { name: 'OK', exact: true }).click(); + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); + } finally { + if (orgName) { + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + const orgId = await boundaryCli.getOrgIdFromName(orgName); + if (orgId) { + await boundaryCli.deleteScope(orgId); + } } } - } -}); + }, +); diff --git a/e2e-tests/admin/tests/credential-store-vault-ent.spec.js b/e2e-tests/admin/tests/credential-store-vault-ent.spec.js index 94b537d7a..2a7a0225a 100644 --- a/e2e-tests/admin/tests/credential-store-vault-ent.spec.js +++ b/e2e-tests/admin/tests/credential-store-vault-ent.spec.js @@ -34,97 +34,101 @@ test.afterEach(() => { execSync(`vault secrets disable ${secretsPath}`); }); -test('Vault Credential Store (User & Key Pair) @ent @aws @docker', async ({ - page, - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - sshUser, - sshKeyPath, - targetAddress, - targetPort, - vaultAddr, -}) => { - let orgId; - let connect; - try { - execSync( - `vault policy write ${boundaryPolicyName} ./admin/tests/fixtures/boundary-controller-policy.hcl`, - ); - execSync(`vault secrets enable -path=${secretsPath} kv-v2`); - execSync( - `vault kv put -mount ${secretsPath} ${secretName} ` + - ` username=${sshUser}` + - ` private_key=@${sshKeyPath}`, - ); - execSync( - `vault policy write ${secretPolicyName} ./admin/tests/fixtures/kv-policy.hcl`, - ); - const vaultToken = JSON.parse( +test( + 'Vault Credential Store (User & Key Pair)', + { tag: ['@ent', '@aws', '@docker'] }, + async ({ + page, + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + sshUser, + sshKeyPath, + targetAddress, + targetPort, + vaultAddr, + }) => { + let orgId; + let connect; + try { execSync( - `vault token create` + - ` -no-default-policy=true` + - ` -policy=${boundaryPolicyName}` + - ` -policy=${secretPolicyName}` + - ` -orphan=true` + - ` -period=20m` + - ` -renewable=true` + - ` -format=json`, - ), - ); - const clientToken = vaultToken.auth.client_token; - - const orgsPage = new OrgsPage(page); - const orgName = await orgsPage.createOrg(); - const projectsPage = new ProjectsPage(page); - const projectName = await projectsPage.createProject(); - const targetsPage = new TargetsPage(page); - const targetName = await targetsPage.createSshTargetWithAddressEnt( - targetAddress, - targetPort, - ); - const credentialStoresPage = new CredentialStoresPage(page); - await credentialStoresPage.createVaultCredentialStore( - vaultAddr, - clientToken, - ); - const credentialLibraryName = - await credentialStoresPage.createVaultGenericCredentialLibraryEnt( - `${secretsPath}/data/${secretName}`, - 'SSH Private Key', + `vault policy write ${boundaryPolicyName} ./admin/tests/fixtures/boundary-controller-policy.hcl`, ); - await targetsPage.addInjectedCredentialsToTarget( - targetName, - credentialLibraryName, - ); + execSync(`vault secrets enable -path=${secretsPath} kv-v2`); + execSync( + `vault kv put -mount ${secretsPath} ${secretName} ` + + ` username=${sshUser}` + + ` private_key=@${sshKeyPath}`, + ); + execSync( + `vault policy write ${secretPolicyName} ./admin/tests/fixtures/kv-policy.hcl`, + ); + const vaultToken = JSON.parse( + execSync( + `vault token create` + + ` -no-default-policy=true` + + ` -policy=${boundaryPolicyName}` + + ` -policy=${secretPolicyName}` + + ` -orphan=true` + + ` -period=20m` + + ` -renewable=true` + + ` -format=json`, + ), + ); + const clientToken = vaultToken.auth.client_token; - // Verify that session can be established - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - orgId = await boundaryCli.getOrgIdFromName(orgName); - const projectId = await boundaryCli.getProjectIdFromName( - orgId, - projectName, - ); - const targetId = await boundaryCli.getTargetIdFromName( - projectId, - targetName, - ); - connect = await boundaryCli.connectSshToTarget(targetId); - const sessionsPage = new SessionsPage(page); - await sessionsPage.waitForSessionToBeVisible(targetName); - } finally { - if (connect) { - connect.kill('SIGTERM'); - } + const orgsPage = new OrgsPage(page); + const orgName = await orgsPage.createOrg(); + const projectsPage = new ProjectsPage(page); + const projectName = await projectsPage.createProject(); + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createSshTargetWithAddressEnt( + targetAddress, + targetPort, + ); + const credentialStoresPage = new CredentialStoresPage(page); + await credentialStoresPage.createVaultCredentialStore( + vaultAddr, + clientToken, + ); + const credentialLibraryName = + await credentialStoresPage.createVaultGenericCredentialLibraryEnt( + `${secretsPath}/data/${secretName}`, + 'SSH Private Key', + ); + await targetsPage.addInjectedCredentialsToTarget( + targetName, + credentialLibraryName, + ); - if (orgId) { - await boundaryCli.deleteScope(orgId); + // Verify that session can be established + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + orgId = await boundaryCli.getOrgIdFromName(orgName); + const projectId = await boundaryCli.getProjectIdFromName( + orgId, + projectName, + ); + const targetId = await boundaryCli.getTargetIdFromName( + projectId, + targetName, + ); + connect = await boundaryCli.connectSshToTarget(targetId); + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); + } finally { + if (connect) { + connect.kill('SIGTERM'); + } + + if (orgId) { + await boundaryCli.deleteScope(orgId); + } } - } -}); + }, +); diff --git a/e2e-tests/admin/tests/credential-store-vault.spec.js b/e2e-tests/admin/tests/credential-store-vault.spec.js index 49f07754a..74bffd7c6 100644 --- a/e2e-tests/admin/tests/credential-store-vault.spec.js +++ b/e2e-tests/admin/tests/credential-store-vault.spec.js @@ -35,119 +35,123 @@ test.afterEach(() => { execSync(`vault policy delete ${secretPolicyName}`); execSync(`vault policy delete ${boundaryPolicyName}`); execSync(`vault secrets disable ${secretsPath}`); -}) - -test('Vault Credential Store (User & Key Pair) @ce @aws @docker', async ({ - page, - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - sshUser, - sshKeyPath, - targetAddress, - targetPort, - vaultAddr, -}) => { - let orgId; - try { - execSync( - `vault policy write ${boundaryPolicyName} ./admin/tests/fixtures/boundary-controller-policy.hcl`, - ); - execSync(`vault secrets enable -path=${secretsPath} kv-v2`); - execSync( - `vault kv put -mount ${secretsPath} ${secretName} ` + - ` username=${sshUser}` + - ` private_key=@${sshKeyPath}`, - ); - execSync( - `vault policy write ${secretPolicyName} ./admin/tests/fixtures/kv-policy.hcl`, - ); - const vaultToken = JSON.parse( - execSync( - `vault token create` + - ` -no-default-policy=true` + - ` -policy=${boundaryPolicyName}` + - ` -policy=${secretPolicyName}` + - ` -orphan=true` + - ` -period=20m` + - ` -renewable=true` + - ` -format=json`, - ), - ); - const clientToken = vaultToken.auth.client_token; - - const orgsPage = new OrgsPage(page); - const orgName = await orgsPage.createOrg(); - const projectsPage = new ProjectsPage(page); - const projectName = await projectsPage.createProject(); - const targetsPage = new TargetsPage(page); - const targetName = await targetsPage.createTargetWithAddress( - targetAddress, - targetPort, - ); - const credentialStoresPage = new CredentialStoresPage(page); - await credentialStoresPage.createVaultCredentialStore( - vaultAddr, - clientToken, - ); - - const credentialLibraryName = 'Credential Library ' + nanoid(); - await page.getByRole('link', { name: 'Credential Libraries' }).click(); - await page.getByRole('link', { name: 'New', exact: true }).click(); - await page - .getByLabel('Name (Optional)', { exact: true }) - .fill(credentialLibraryName); - await page - .getByLabel('Description (Optional)') - .fill('This is an automated test'); - await page - .getByLabel('Vault Path') - .fill(`${secretsPath}/data/${secretName}`); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - - await targetsPage.addBrokeredCredentialsToTarget( - targetName, - credentialLibraryName, - ); - - // Verify correct credentials are returned after authorizing session - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - orgId = await boundaryCli.getOrgIdFromName(orgName); - const projectId = await boundaryCli.getProjectIdFromName( - orgId, - projectName, - ); - const targetId = await boundaryCli.getTargetIdFromName( - projectId, - targetName, - ); - const session = await boundaryCli.authorizeSessionByTargetId(targetId); - const retrievedUser = - session.item.credentials[0].secret.decoded.data.username; - const retrievedKey = - session.item.credentials[0].secret.decoded.data.private_key; - - expect(retrievedUser).toBe(sshUser); - const keyData = await readFile(sshKeyPath, { - encoding: 'utf-8', - }); - expect(retrievedKey).toBe(keyData); - - const sessionsPage = new SessionsPage(page); - await sessionsPage.waitForSessionToBeVisible(targetName); - } finally { - if (orgId) { - await boundaryCli.deleteScope(orgId); - } - } }); + +test( + 'Vault Credential Store (User & Key Pair)', + { tag: ['@ce', '@aws', '@docker'] }, + async ({ + page, + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + sshUser, + sshKeyPath, + targetAddress, + targetPort, + vaultAddr, + }) => { + let orgId; + try { + execSync( + `vault policy write ${boundaryPolicyName} ./admin/tests/fixtures/boundary-controller-policy.hcl`, + ); + execSync(`vault secrets enable -path=${secretsPath} kv-v2`); + execSync( + `vault kv put -mount ${secretsPath} ${secretName} ` + + ` username=${sshUser}` + + ` private_key=@${sshKeyPath}`, + ); + execSync( + `vault policy write ${secretPolicyName} ./admin/tests/fixtures/kv-policy.hcl`, + ); + const vaultToken = JSON.parse( + execSync( + `vault token create` + + ` -no-default-policy=true` + + ` -policy=${boundaryPolicyName}` + + ` -policy=${secretPolicyName}` + + ` -orphan=true` + + ` -period=20m` + + ` -renewable=true` + + ` -format=json`, + ), + ); + const clientToken = vaultToken.auth.client_token; + + const orgsPage = new OrgsPage(page); + const orgName = await orgsPage.createOrg(); + const projectsPage = new ProjectsPage(page); + const projectName = await projectsPage.createProject(); + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createTargetWithAddress( + targetAddress, + targetPort, + ); + const credentialStoresPage = new CredentialStoresPage(page); + await credentialStoresPage.createVaultCredentialStore( + vaultAddr, + clientToken, + ); + + const credentialLibraryName = 'Credential Library ' + nanoid(); + await page.getByRole('link', { name: 'Credential Libraries' }).click(); + await page.getByRole('link', { name: 'New', exact: true }).click(); + await page + .getByLabel('Name (Optional)', { exact: true }) + .fill(credentialLibraryName); + await page + .getByLabel('Description (Optional)') + .fill('This is an automated test'); + await page + .getByLabel('Vault Path') + .fill(`${secretsPath}/data/${secretName}`); + await page.getByRole('button', { name: 'Save' }).click(); + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); + + await targetsPage.addBrokeredCredentialsToTarget( + targetName, + credentialLibraryName, + ); + + // Verify correct credentials are returned after authorizing session + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + orgId = await boundaryCli.getOrgIdFromName(orgName); + const projectId = await boundaryCli.getProjectIdFromName( + orgId, + projectName, + ); + const targetId = await boundaryCli.getTargetIdFromName( + projectId, + targetName, + ); + const session = await boundaryCli.authorizeSessionByTargetId(targetId); + const retrievedUser = + session.item.credentials[0].secret.decoded.data.username; + const retrievedKey = + session.item.credentials[0].secret.decoded.data.private_key; + + expect(retrievedUser).toBe(sshUser); + const keyData = await readFile(sshKeyPath, { + encoding: 'utf-8', + }); + expect(retrievedKey).toBe(keyData); + + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); + } finally { + if (orgId) { + await boundaryCli.deleteScope(orgId); + } + } + }, +); diff --git a/e2e-tests/admin/tests/delete-resources-ent.spec.js b/e2e-tests/admin/tests/delete-resources-ent.spec.js index 50af728d8..31b10827b 100644 --- a/e2e-tests/admin/tests/delete-resources-ent.spec.js +++ b/e2e-tests/admin/tests/delete-resources-ent.spec.js @@ -39,126 +39,126 @@ test.beforeEach( test.afterEach(() => { execSync(`vault policy delete ${secretPolicyName}`); execSync(`vault policy delete ${boundaryPolicyName}`); -}) - -test('Verify resources can be deleted (enterprise) @ent @aws', async ({ - page, - awsRegion, - vaultAddr, -}) => { - let orgId; - let orgDeleted = false; - try { - orgId = await boundaryCli.createOrg(); - let projectId = await boundaryCli.createProject(orgId); - - // Create boundary resources using CLI - let workerId = await boundaryCli.createControllerLedWorker(); - let authMethodId = await boundaryCli.createPasswordAuthMethod(orgId); - await boundaryCli.makeAuthMethodPrimary(orgId, authMethodId); - let passwordAccountId = - await boundaryCli.createPasswordAccount(authMethodId); - let projectScopeRoleId = await boundaryCli.createRole(projectId); - let orgScopeRoleId = await boundaryCli.createRole(orgId); - let globalScopeRoleId = await boundaryCli.createRole('global'); - let groupId = await boundaryCli.createGroup(orgId); - let userId = await boundaryCli.createUser(orgId); - let staticHostCatalogId = - await boundaryCli.createStaticHostCatalog(projectId); - let dynamicAwsHostCatalogId = await boundaryCli.createDynamicAwsHostCatalog( - projectId, - awsRegion, - ); - let staticHostId = await boundaryCli.createStaticHost(staticHostCatalogId); - let staticHostSetId = await boundaryCli.createHostSet(staticHostCatalogId); - let staticCredentialStoreId = - await boundaryCli.createStaticCredentialStore(projectId); - const vaultToken = await vaultCli.getVaultToken( - boundaryPolicyName, - secretPolicyName, - ); - let vaultCredentialStoreId = await boundaryCli.createVaultCredentialStore( - projectId, - vaultAddr, - vaultToken, - ); - let usernamePasswordCredentialId = - await boundaryCli.createUsernamePasswordCredential( - staticCredentialStoreId, - ); - let tcpTargetId = await boundaryCli.createTcpTarget(projectId); - - // Create enterprise boundary resources using CLI - let sshTargetId = await boundaryCli.createSshTarget(projectId); - - // Delete resources - const baseResourcePage = new BaseResourcePage(page); - - await page.goto(`/scopes/${projectId}/targets/${tcpTargetId}`); - await baseResourcePage.deleteResource(page); - await page.goto( - `/scopes/${projectId}/credential-stores/${staticCredentialStoreId}/credentials/${usernamePasswordCredentialId}`, - ); - await baseResourcePage.deleteResource(page); - await page.goto( - `/scopes/${projectId}/credential-stores/${staticCredentialStoreId}`, - ); - await baseResourcePage.deleteResource(page); - await page.goto( - `/scopes/${projectId}/credential-stores/${vaultCredentialStoreId}`, - ); - await baseResourcePage.deleteResource(page); - await page.goto( - `/scopes/${projectId}/host-catalogs/${staticHostCatalogId}/host-sets/${staticHostSetId}`, - ); - await baseResourcePage.deleteResource(page); - await page.goto( - `/scopes/${projectId}/host-catalogs/${staticHostCatalogId}/hosts/${staticHostId}`, - ); - await baseResourcePage.deleteResource(page); - await page.goto( - `/scopes/${projectId}/host-catalogs/${staticHostCatalogId}`, - ); - await baseResourcePage.deleteResource(page); - await page.goto( - `/scopes/${projectId}/host-catalogs/${dynamicAwsHostCatalogId}`, - ); - await baseResourcePage.deleteResource(page); - await page.goto(`/scopes/${projectId}/users/${userId}`); - await baseResourcePage.deleteResource(page); - await page.goto(`/scopes/${projectId}/groups/${groupId}`); - await baseResourcePage.deleteResource(page); - await page.goto(`/scopes/global/roles/${globalScopeRoleId}`); - await baseResourcePage.deleteResource(page); - await page.goto(`/scopes/${projectId}/roles/${orgScopeRoleId}`); - await baseResourcePage.deleteResource(page); - await page.goto(`/scopes/${projectId}/roles/${projectScopeRoleId}`); - await baseResourcePage.deleteResource(page); - await page.goto( - `/scopes/${orgId}/auth-methods/${authMethodId}/accounts/${passwordAccountId}`, - ); - await baseResourcePage.deleteResource(page); - await page.goto(`/scopes/${orgId}/auth-methods/${authMethodId}`); - const authMethodsPage = new AuthMethodsPage(page); - await authMethodsPage.removeAuthMethodAsPrimary(); - await baseResourcePage.deleteResource(page); - await page.goto(`/scopes/global/workers/${workerId}`); - const workersPage = new WorkersPage(page); - await workersPage.deleteResource(page); - - // Delete enterprise resources - await page.goto(`/scopes/${projectId}/targets/${sshTargetId}`); - await baseResourcePage.deleteResource(page); - - // Delete project and org - await page.goto(`/scopes/${projectId}`); - await baseResourcePage.deleteResource(page); - await page.goto(`/scopes/${orgId}/edit`); - await baseResourcePage.deleteResource(page); - orgDeleted = true; - } finally { - if (orgId && orgDeleted == false) { - await boundaryCli.deleteScope(orgId); - } - } }); + +test( + 'Verify resources can be deleted (enterprise)', + { tag: ['@ent', '@aws'] }, + async ({ page, awsRegion, vaultAddr }) => { + let orgId; + let orgDeleted = false; + try { + orgId = await boundaryCli.createOrg(); + let projectId = await boundaryCli.createProject(orgId); + + // Create boundary resources using CLI + let workerId = await boundaryCli.createControllerLedWorker(); + let authMethodId = await boundaryCli.createPasswordAuthMethod(orgId); + await boundaryCli.makeAuthMethodPrimary(orgId, authMethodId); + let passwordAccountId = + await boundaryCli.createPasswordAccount(authMethodId); + let projectScopeRoleId = await boundaryCli.createRole(projectId); + let orgScopeRoleId = await boundaryCli.createRole(orgId); + let globalScopeRoleId = await boundaryCli.createRole('global'); + let groupId = await boundaryCli.createGroup(orgId); + let userId = await boundaryCli.createUser(orgId); + let staticHostCatalogId = + await boundaryCli.createStaticHostCatalog(projectId); + let dynamicAwsHostCatalogId = + await boundaryCli.createDynamicAwsHostCatalog(projectId, awsRegion); + let staticHostId = + await boundaryCli.createStaticHost(staticHostCatalogId); + let staticHostSetId = + await boundaryCli.createHostSet(staticHostCatalogId); + let staticCredentialStoreId = + await boundaryCli.createStaticCredentialStore(projectId); + const vaultToken = await vaultCli.getVaultToken( + boundaryPolicyName, + secretPolicyName, + ); + let vaultCredentialStoreId = await boundaryCli.createVaultCredentialStore( + projectId, + vaultAddr, + vaultToken, + ); + let usernamePasswordCredentialId = + await boundaryCli.createUsernamePasswordCredential( + staticCredentialStoreId, + ); + let tcpTargetId = await boundaryCli.createTcpTarget(projectId); + + // Create enterprise boundary resources using CLI + let sshTargetId = await boundaryCli.createSshTarget(projectId); + + // Delete resources + const baseResourcePage = new BaseResourcePage(page); + + await page.goto(`/scopes/${projectId}/targets/${tcpTargetId}`); + await baseResourcePage.deleteResource(page); + await page.goto( + `/scopes/${projectId}/credential-stores/${staticCredentialStoreId}/credentials/${usernamePasswordCredentialId}`, + ); + await baseResourcePage.deleteResource(page); + await page.goto( + `/scopes/${projectId}/credential-stores/${staticCredentialStoreId}`, + ); + await baseResourcePage.deleteResource(page); + await page.goto( + `/scopes/${projectId}/credential-stores/${vaultCredentialStoreId}`, + ); + await baseResourcePage.deleteResource(page); + await page.goto( + `/scopes/${projectId}/host-catalogs/${staticHostCatalogId}/host-sets/${staticHostSetId}`, + ); + await baseResourcePage.deleteResource(page); + await page.goto( + `/scopes/${projectId}/host-catalogs/${staticHostCatalogId}/hosts/${staticHostId}`, + ); + await baseResourcePage.deleteResource(page); + await page.goto( + `/scopes/${projectId}/host-catalogs/${staticHostCatalogId}`, + ); + await baseResourcePage.deleteResource(page); + await page.goto( + `/scopes/${projectId}/host-catalogs/${dynamicAwsHostCatalogId}`, + ); + await baseResourcePage.deleteResource(page); + await page.goto(`/scopes/${projectId}/users/${userId}`); + await baseResourcePage.deleteResource(page); + await page.goto(`/scopes/${projectId}/groups/${groupId}`); + await baseResourcePage.deleteResource(page); + await page.goto(`/scopes/global/roles/${globalScopeRoleId}`); + await baseResourcePage.deleteResource(page); + await page.goto(`/scopes/${projectId}/roles/${orgScopeRoleId}`); + await baseResourcePage.deleteResource(page); + await page.goto(`/scopes/${projectId}/roles/${projectScopeRoleId}`); + await baseResourcePage.deleteResource(page); + await page.goto( + `/scopes/${orgId}/auth-methods/${authMethodId}/accounts/${passwordAccountId}`, + ); + await baseResourcePage.deleteResource(page); + await page.goto(`/scopes/${orgId}/auth-methods/${authMethodId}`); + const authMethodsPage = new AuthMethodsPage(page); + await authMethodsPage.removeAuthMethodAsPrimary(); + await baseResourcePage.deleteResource(page); + await page.goto(`/scopes/global/workers/${workerId}`); + const workersPage = new WorkersPage(page); + await workersPage.deleteResource(page); + + // Delete enterprise resources + await page.goto(`/scopes/${projectId}/targets/${sshTargetId}`); + await baseResourcePage.deleteResource(page); + + // Delete project and org + await page.goto(`/scopes/${projectId}`); + await baseResourcePage.deleteResource(page); + await page.goto(`/scopes/${orgId}/edit`); + await baseResourcePage.deleteResource(page); + orgDeleted = true; + } finally { + if (orgId && orgDeleted == false) { + await boundaryCli.deleteScope(orgId); + } + } + }, +); diff --git a/e2e-tests/admin/tests/delete-resources.spec.js b/e2e-tests/admin/tests/delete-resources.spec.js index 7c9cd434c..5292afbc6 100644 --- a/e2e-tests/admin/tests/delete-resources.spec.js +++ b/e2e-tests/admin/tests/delete-resources.spec.js @@ -41,149 +41,149 @@ test.afterEach(() => { execSync(`vault policy delete ${boundaryPolicyName}`); }); -test('Verify resources can be deleted @ce @aws', async ({ - page, - awsRegion, - vaultAddr, -}) => { - let orgId; - let orgDeleted = false; - try { - // Create boundary resources using CLI - orgId = await boundaryCli.createOrg(); - let projectId = await boundaryCli.createProject(orgId); - let workerId = await boundaryCli.createControllerLedWorker(); - let authMethodId = await boundaryCli.createPasswordAuthMethod(orgId); - await boundaryCli.makeAuthMethodPrimary(orgId, authMethodId); - let passwordAccountId = - await boundaryCli.createPasswordAccount(authMethodId); - let projectScopeRoleId = await boundaryCli.createRole(projectId); - let orgScopeRoleId = await boundaryCli.createRole(orgId); - let globalScopeRoleId = await boundaryCli.createRole('global'); - let groupId = await boundaryCli.createGroup(orgId); - let userId = await boundaryCli.createUser(orgId); - let staticHostCatalogId = - await boundaryCli.createStaticHostCatalog(projectId); - let dynamicAwsHostCatalogId = await boundaryCli.createDynamicAwsHostCatalog( - projectId, - awsRegion, - ); - let staticHostId = await boundaryCli.createStaticHost(staticHostCatalogId); - let staticHostSetId = await boundaryCli.createHostSet(staticHostCatalogId); - let staticCredentialStoreId = - await boundaryCli.createStaticCredentialStore(projectId); - const vaultToken = await vaultCli.getVaultToken( - boundaryPolicyName, - secretPolicyName, - ); - let vaultCredentialStoreId = await boundaryCli.createVaultCredentialStore( - projectId, - vaultAddr, - vaultToken, - ); - let usernamePasswordCredentialId = - await boundaryCli.createUsernamePasswordCredential( - staticCredentialStoreId, +test( + 'Verify resources can be deleted', + { tag: ['@ce', '@aws'] }, + async ({ page, awsRegion, vaultAddr }) => { + let orgId; + let orgDeleted = false; + try { + // Create boundary resources using CLI + orgId = await boundaryCli.createOrg(); + let projectId = await boundaryCli.createProject(orgId); + let workerId = await boundaryCli.createControllerLedWorker(); + let authMethodId = await boundaryCli.createPasswordAuthMethod(orgId); + await boundaryCli.makeAuthMethodPrimary(orgId, authMethodId); + let passwordAccountId = + await boundaryCli.createPasswordAccount(authMethodId); + let projectScopeRoleId = await boundaryCli.createRole(projectId); + let orgScopeRoleId = await boundaryCli.createRole(orgId); + let globalScopeRoleId = await boundaryCli.createRole('global'); + let groupId = await boundaryCli.createGroup(orgId); + let userId = await boundaryCli.createUser(orgId); + let staticHostCatalogId = + await boundaryCli.createStaticHostCatalog(projectId); + let dynamicAwsHostCatalogId = + await boundaryCli.createDynamicAwsHostCatalog(projectId, awsRegion); + let staticHostId = + await boundaryCli.createStaticHost(staticHostCatalogId); + let staticHostSetId = + await boundaryCli.createHostSet(staticHostCatalogId); + let staticCredentialStoreId = + await boundaryCli.createStaticCredentialStore(projectId); + const vaultToken = await vaultCli.getVaultToken( + boundaryPolicyName, + secretPolicyName, ); - let tcpTargetId = await boundaryCli.createTcpTarget(projectId); + let vaultCredentialStoreId = await boundaryCli.createVaultCredentialStore( + projectId, + vaultAddr, + vaultToken, + ); + let usernamePasswordCredentialId = + await boundaryCli.createUsernamePasswordCredential( + staticCredentialStoreId, + ); + let tcpTargetId = await boundaryCli.createTcpTarget(projectId); - const baseResourcePage = new BaseResourcePage(page); + const baseResourcePage = new BaseResourcePage(page); - // Delete TCP target - await page.goto(`/scopes/${projectId}/targets/${tcpTargetId}`); - await baseResourcePage.deleteResource(page); + // Delete TCP target + await page.goto(`/scopes/${projectId}/targets/${tcpTargetId}`); + await baseResourcePage.deleteResource(page); - // Delete username-password credentials - await page.goto( - `/scopes/${projectId}/credential-stores/${staticCredentialStoreId}/credentials/${usernamePasswordCredentialId}`, - ); - await baseResourcePage.deleteResource(page); + // Delete username-password credentials + await page.goto( + `/scopes/${projectId}/credential-stores/${staticCredentialStoreId}/credentials/${usernamePasswordCredentialId}`, + ); + await baseResourcePage.deleteResource(page); - // Delete static credential store - await page.goto( - `/scopes/${projectId}/credential-stores/${staticCredentialStoreId}`, - ); - await baseResourcePage.deleteResource(page); + // Delete static credential store + await page.goto( + `/scopes/${projectId}/credential-stores/${staticCredentialStoreId}`, + ); + await baseResourcePage.deleteResource(page); - // Delete vault credential store - await page.goto( - `/scopes/${projectId}/credential-stores/${vaultCredentialStoreId}`, - ); - await baseResourcePage.deleteResource(page); + // Delete vault credential store + await page.goto( + `/scopes/${projectId}/credential-stores/${vaultCredentialStoreId}`, + ); + await baseResourcePage.deleteResource(page); - // Delete static host set - await page.goto( - `/scopes/${projectId}/host-catalogs/${staticHostCatalogId}/host-sets/${staticHostSetId}`, - ); - await baseResourcePage.deleteResource(page); + // Delete static host set + await page.goto( + `/scopes/${projectId}/host-catalogs/${staticHostCatalogId}/host-sets/${staticHostSetId}`, + ); + await baseResourcePage.deleteResource(page); - // Delete static host - await page.goto( - `/scopes/${projectId}/host-catalogs/${staticHostCatalogId}/hosts/${staticHostId}`, - ); - await baseResourcePage.deleteResource(page); + // Delete static host + await page.goto( + `/scopes/${projectId}/host-catalogs/${staticHostCatalogId}/hosts/${staticHostId}`, + ); + await baseResourcePage.deleteResource(page); - // Delete static host catalog - await page.goto( - `/scopes/${projectId}/host-catalogs/${staticHostCatalogId}`, - ); - await baseResourcePage.deleteResource(page); + // Delete static host catalog + await page.goto( + `/scopes/${projectId}/host-catalogs/${staticHostCatalogId}`, + ); + await baseResourcePage.deleteResource(page); - // Delete dynamic aws host catalog - await page.goto( - `/scopes/${projectId}/host-catalogs/${dynamicAwsHostCatalogId}`, - ); - await baseResourcePage.deleteResource(page); + // Delete dynamic aws host catalog + await page.goto( + `/scopes/${projectId}/host-catalogs/${dynamicAwsHostCatalogId}`, + ); + await baseResourcePage.deleteResource(page); - // Delete user - await page.goto(`/scopes/${projectId}/users/${userId}`); - await baseResourcePage.deleteResource(page); + // Delete user + await page.goto(`/scopes/${projectId}/users/${userId}`); + await baseResourcePage.deleteResource(page); - // Delete group - await page.goto(`/scopes/${projectId}/groups/${groupId}`); - await baseResourcePage.deleteResource(page); + // Delete group + await page.goto(`/scopes/${projectId}/groups/${groupId}`); + await baseResourcePage.deleteResource(page); - // Delete role under global scope - await page.goto(`/scopes/global/roles/${globalScopeRoleId}`); - await baseResourcePage.deleteResource(page); + // Delete role under global scope + await page.goto(`/scopes/global/roles/${globalScopeRoleId}`); + await baseResourcePage.deleteResource(page); - // Delete role under org scope - await page.goto(`/scopes/${projectId}/roles/${orgScopeRoleId}`); - await baseResourcePage.deleteResource(page); + // Delete role under org scope + await page.goto(`/scopes/${projectId}/roles/${orgScopeRoleId}`); + await baseResourcePage.deleteResource(page); - // Delete role under project scope - await page.goto(`/scopes/${projectId}/roles/${projectScopeRoleId}`); - await baseResourcePage.deleteResource(page); + // Delete role under project scope + await page.goto(`/scopes/${projectId}/roles/${projectScopeRoleId}`); + await baseResourcePage.deleteResource(page); - // Delete password account - await page.goto( - `/scopes/${orgId}/auth-methods/${authMethodId}/accounts/${passwordAccountId}`, - ); - await baseResourcePage.deleteResource(page); + // Delete password account + await page.goto( + `/scopes/${orgId}/auth-methods/${authMethodId}/accounts/${passwordAccountId}`, + ); + await baseResourcePage.deleteResource(page); - // Delete auth method - await page.goto(`/scopes/${orgId}/auth-methods/${authMethodId}`); - const authMethodsPage = new AuthMethodsPage(page); - await authMethodsPage.removeAuthMethodAsPrimary(); - await baseResourcePage.deleteResource(page); + // Delete auth method + await page.goto(`/scopes/${orgId}/auth-methods/${authMethodId}`); + const authMethodsPage = new AuthMethodsPage(page); + await authMethodsPage.removeAuthMethodAsPrimary(); + await baseResourcePage.deleteResource(page); - // Delete worker - await page.goto(`/scopes/global/workers/${workerId}`); - const workersPage = new WorkersPage(page); - await workersPage.deleteResource(page); + // Delete worker + await page.goto(`/scopes/global/workers/${workerId}`); + const workersPage = new WorkersPage(page); + await workersPage.deleteResource(page); - // Delete project - await page.goto(`/scopes/${projectId}`); - await baseResourcePage.deleteResource(page); + // Delete project + await page.goto(`/scopes/${projectId}`); + await baseResourcePage.deleteResource(page); - // Delete org - await page.goto(`/scopes/${orgId}/edit`); - await baseResourcePage.deleteResource(page); - orgDeleted = true; - } finally { - // Delete org in case the test failed before deleting the org using UI - if (orgId && orgDeleted === false) { - await boundaryCli.deleteScope(orgId); + // Delete org + await page.goto(`/scopes/${orgId}/edit`); + await baseResourcePage.deleteResource(page); + orgDeleted = true; + } finally { + // Delete org in case the test failed before deleting the org using UI + if (orgId && orgDeleted === false) { + await boundaryCli.deleteScope(orgId); + } } - } -}); + }, +); diff --git a/e2e-tests/admin/tests/dynamic-host-catalog-aws-ent.spec.js b/e2e-tests/admin/tests/dynamic-host-catalog-aws-ent.spec.js index fdf1bf121..6b6b661d6 100644 --- a/e2e-tests/admin/tests/dynamic-host-catalog-aws-ent.spec.js +++ b/e2e-tests/admin/tests/dynamic-host-catalog-aws-ent.spec.js @@ -20,217 +20,221 @@ test.beforeEach(async ({ page }) => { }); test.describe('AWS', () => { - test('Create an AWS Dynamic Host Catalog and set up Host Sets @ent @aws', async ({ - page, - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - awsAccessKeyId, - awsHostSetFilter, - awsHostSetIps, - awsRegion, - awsSecretAccessKey, - targetPort, - sshUser, - sshKeyPath, - }) => { - let orgName; - let connect; - try { - const orgsPage = new OrgsPage(page); - orgName = await orgsPage.createOrg(); - const projectsPage = new ProjectsPage(page); - const projectName = await projectsPage.createProject(); + test( + 'Create an AWS Dynamic Host Catalog and set up Host Sets', + { tag: ['@ent', '@aws'] }, + async ({ + page, + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + awsAccessKeyId, + awsHostSetFilter, + awsHostSetIps, + awsRegion, + awsSecretAccessKey, + targetPort, + sshUser, + sshKeyPath, + }) => { + let orgName; + let connect; + try { + const orgsPage = new OrgsPage(page); + orgName = await orgsPage.createOrg(); + const projectsPage = new ProjectsPage(page); + const projectName = await projectsPage.createProject(); - // Create host catalog - const hostCatalogName = 'Host Catalog ' + nanoid(); - await page - .getByRole('navigation', { name: 'Resources' }) - .getByRole('link', { name: 'Host Catalogs' }) - .click(); - await page.getByRole('link', { name: 'New', exact: true }).click(); - await page.getByLabel('Name').fill(hostCatalogName); - await page.getByLabel('Description').fill('This is an automated test'); - await page - .getByRole('group', { name: 'Type' }) - .getByLabel('Dynamic') - .click(); - await page - .getByRole('group', { name: 'Provider' }) - .getByLabel('AWS') - .click(); - await page.getByLabel('AWS Region').fill(awsRegion); - await page.getByLabel('Access Key ID').fill(awsAccessKeyId); - await page.getByLabel('Secret Access Key').fill(awsSecretAccessKey); - await page - .getByLabel('Disable credential rotation') - .click({ force: true }); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText(hostCatalogName), - ).toBeVisible(); - - // Create first host set - const hostSetName = 'Host Set ' + nanoid(); - await page.getByRole('link', { name: 'Host Sets' }).click(); - await page.getByRole('link', { name: 'New', exact: true }).click(); - await page.getByLabel('Name (Optional)').fill(hostSetName); - await page.getByLabel('Description').fill('This is an automated test'); - await page - .getByRole('group', { name: 'Filter' }) - .getByRole('textbox') - .fill(awsHostSetFilter); - await page - .getByRole('group', { name: 'Filter' }) - .getByRole('button', { name: 'Add' }) - .click(); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText(hostSetName), - ).toBeVisible(); - - await page.getByRole('link', { name: 'Hosts' }).click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText('Hosts'), - ).toBeVisible(); - - // Check number of hosts in host set - const expectedHosts = JSON.parse(awsHostSetIps); - await expect(async () => { - const rowCount = await page - .getByRole('table') - .getByRole('row') - .filter({ hasNot: page.getByRole('columnheader') }) - .count(); - - if (rowCount !== expectedHosts.length) { - await page.reload(); - await page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText(hostSetName) - .waitFor(); - } - expect(rowCount).toBe(expectedHosts.length); - }).toPass(); - - // Navigate to each host in the host set - for (const row of await page - .getByRole('table') - .getByRole('rowgroup') - .nth(1) - .getByRole('row') - .all()) { - const host = row.getByRole('link'); - let hostName = await host.innerText(); - await host.click(); + // Create host catalog + const hostCatalogName = 'Host Catalog ' + nanoid(); + await page + .getByRole('navigation', { name: 'Resources' }) + .getByRole('link', { name: 'Host Catalogs' }) + .click(); + await page.getByRole('link', { name: 'New', exact: true }).click(); + await page.getByLabel('Name').fill(hostCatalogName); + await page.getByLabel('Description').fill('This is an automated test'); + await page + .getByRole('group', { name: 'Type' }) + .getByLabel('Dynamic') + .click(); + await page + .getByRole('group', { name: 'Provider' }) + .getByLabel('AWS') + .click(); + await page.getByLabel('AWS Region').fill(awsRegion); + await page.getByLabel('Access Key ID').fill(awsAccessKeyId); + await page.getByLabel('Secret Access Key').fill(awsSecretAccessKey); + await page + .getByLabel('Disable credential rotation') + .click({ force: true }); + await page.getByRole('button', { name: 'Save' }).click(); + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); await expect( page .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText(hostName), + .getByText(hostCatalogName), ).toBeVisible(); + // Create first host set + const hostSetName = 'Host Set ' + nanoid(); + await page.getByRole('link', { name: 'Host Sets' }).click(); + await page.getByRole('link', { name: 'New', exact: true }).click(); + await page.getByLabel('Name (Optional)').fill(hostSetName); + await page.getByLabel('Description').fill('This is an automated test'); await page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText(hostSetName) + .getByRole('group', { name: 'Filter' }) + .getByRole('textbox') + .fill(awsHostSetFilter); + await page + .getByRole('group', { name: 'Filter' }) + .getByRole('button', { name: 'Add' }) .click(); + await page.getByRole('button', { name: 'Save' }).click(); + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); + await expect( + page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(hostSetName), + ).toBeVisible(); + await page.getByRole('link', { name: 'Hosts' }).click(); - } + await expect( + page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText('Hosts'), + ).toBeVisible(); - // Create a target and add DHC host set as a host source - const targetsPage = new TargetsPage(page); - const targetName = await targetsPage.createSshTargetEnt(targetPort); - await targetsPage.addHostSourceToTarget(hostSetName); + // Check number of hosts in host set + const expectedHosts = JSON.parse(awsHostSetIps); + await expect(async () => { + const rowCount = await page + .getByRole('table') + .getByRole('row') + .filter({ hasNot: page.getByRole('columnheader') }) + .count(); - // Add another host source - const hostCatalogsPage = new HostCatalogsPage(page); - await hostCatalogsPage.createHostCatalog(); - const newHostSetName = await hostCatalogsPage.createHostSet(); - await page - .getByRole('navigation', { name: 'Resources' }) - .getByRole('link', { name: 'Targets' }) - .click(); - await page.getByRole('link', { name: targetName }).click(); - await targetsPage.addHostSourceToTarget(newHostSetName); + if (rowCount !== expectedHosts.length) { + await page.reload(); + await page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(hostSetName) + .waitFor(); + } + expect(rowCount).toBe(expectedHosts.length); + }).toPass(); - // Remove the host source from the target - await page - .getByRole('link', { name: newHostSetName }) - .locator('..') - .locator('..') - .getByRole('button', { name: 'Manage' }) - .click(); - await page.getByRole('button', { name: 'Remove' }).click(); - await page.getByRole('button', { name: 'OK', exact: true }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); + // Navigate to each host in the host set + for (const row of await page + .getByRole('table') + .getByRole('rowgroup') + .nth(1) + .getByRole('row') + .all()) { + const host = row.getByRole('link'); + let hostName = await host.innerText(); + await host.click(); + await expect( + page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(hostName), + ).toBeVisible(); - // Set up target credentials - const credentialStoresPage = new CredentialStoresPage(page); - await credentialStoresPage.createStaticCredentialStore(); - const credentialName = - await credentialStoresPage.createStaticCredentialKeyPair( - sshUser, - sshKeyPath, + await page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(hostSetName) + .click(); + await page.getByRole('link', { name: 'Hosts' }).click(); + } + + // Create a target and add DHC host set as a host source + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createSshTargetEnt(targetPort); + await targetsPage.addHostSourceToTarget(hostSetName); + + // Add another host source + const hostCatalogsPage = new HostCatalogsPage(page); + await hostCatalogsPage.createHostCatalog(); + const newHostSetName = await hostCatalogsPage.createHostSet(); + await page + .getByRole('navigation', { name: 'Resources' }) + .getByRole('link', { name: 'Targets' }) + .click(); + await page.getByRole('link', { name: targetName }).click(); + await targetsPage.addHostSourceToTarget(newHostSetName); + + // Remove the host source from the target + await page + .getByRole('link', { name: newHostSetName }) + .locator('..') + .locator('..') + .getByRole('button', { name: 'Manage' }) + .click(); + await page.getByRole('button', { name: 'Remove' }).click(); + await page.getByRole('button', { name: 'OK', exact: true }).click(); + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); + + // Set up target credentials + const credentialStoresPage = new CredentialStoresPage(page); + await credentialStoresPage.createStaticCredentialStore(); + const credentialName = + await credentialStoresPage.createStaticCredentialKeyPair( + sshUser, + sshKeyPath, + ); + await targetsPage.addInjectedCredentialsToTarget( + targetName, + credentialName, ); - await targetsPage.addInjectedCredentialsToTarget( - targetName, - credentialName, - ); - // Connect to target - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - const orgId = await boundaryCli.getOrgIdFromName(orgName); - const projectId = await boundaryCli.getProjectIdFromName( - orgId, - projectName, - ); - const targetId = await boundaryCli.getTargetIdFromName( - projectId, - targetName, - ); - connect = await boundaryCli.connectSshToTarget(targetId); - const sessionsPage = new SessionsPage(page); - await sessionsPage.waitForSessionToBeVisible(targetName); - } finally { - if (connect) { - connect.kill('SIGTERM'); - } - - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - - if (orgName) { + // Connect to target + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); const orgId = await boundaryCli.getOrgIdFromName(orgName); - if (orgId) { - await boundaryCli.deleteScope(orgId); + const projectId = await boundaryCli.getProjectIdFromName( + orgId, + projectName, + ); + const targetId = await boundaryCli.getTargetIdFromName( + projectId, + targetName, + ); + connect = await boundaryCli.connectSshToTarget(targetId); + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); + } finally { + if (connect) { + connect.kill('SIGTERM'); + } + + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + + if (orgName) { + const orgId = await boundaryCli.getOrgIdFromName(orgName); + if (orgId) { + await boundaryCli.deleteScope(orgId); + } } } - } - }); + }, + ); }); diff --git a/e2e-tests/admin/tests/dynamic-host-catalog-aws.spec.js b/e2e-tests/admin/tests/dynamic-host-catalog-aws.spec.js index 92ecb50c0..ab0c8337c 100644 --- a/e2e-tests/admin/tests/dynamic-host-catalog-aws.spec.js +++ b/e2e-tests/admin/tests/dynamic-host-catalog-aws.spec.js @@ -20,217 +20,221 @@ test.beforeEach(async ({ page }) => { }); test.describe('AWS', () => { - test('Create an AWS Dynamic Host Catalog and set up Host Sets @ce @aws', async ({ - page, - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - awsAccessKeyId, - awsHostSetFilter, - awsHostSetIps, - awsRegion, - awsSecretAccessKey, - targetPort, - sshUser, - sshKeyPath, - }) => { - let orgName; - let connect; - try { - const orgsPage = new OrgsPage(page); - orgName = await orgsPage.createOrg(); - const projectsPage = new ProjectsPage(page); - const projectName = await projectsPage.createProject(); + test( + 'Create an AWS Dynamic Host Catalog and set up Host Sets', + { tag: ['@ce', '@aws'] }, + async ({ + page, + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + awsAccessKeyId, + awsHostSetFilter, + awsHostSetIps, + awsRegion, + awsSecretAccessKey, + targetPort, + sshUser, + sshKeyPath, + }) => { + let orgName; + let connect; + try { + const orgsPage = new OrgsPage(page); + orgName = await orgsPage.createOrg(); + const projectsPage = new ProjectsPage(page); + const projectName = await projectsPage.createProject(); - // Create host catalog - const hostCatalogName = 'Host Catalog ' + nanoid(); - await page - .getByRole('navigation', { name: 'Resources' }) - .getByRole('link', { name: 'Host Catalogs' }) - .click(); - await page.getByRole('link', { name: 'New', exact: true }).click(); - await page.getByLabel('Name').fill(hostCatalogName); - await page.getByLabel('Description').fill('This is an automated test'); - await page - .getByRole('group', { name: 'Type' }) - .getByLabel('Dynamic') - .click(); - await page - .getByRole('group', { name: 'Provider' }) - .getByLabel('AWS') - .click(); - await page.getByLabel('AWS Region').fill(awsRegion); - await page.getByLabel('Access Key ID').fill(awsAccessKeyId); - await page.getByLabel('Secret Access Key').fill(awsSecretAccessKey); - await page - .getByLabel('Disable credential rotation') - .click({ force: true }); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText(hostCatalogName), - ).toBeVisible(); - - // Create first host set - const hostSetName = 'Host Set ' + nanoid(); - await page.getByRole('link', { name: 'Host Sets' }).click(); - await page.getByRole('link', { name: 'New', exact: true }).click(); - await page.getByLabel('Name (Optional)').fill(hostSetName); - await page.getByLabel('Description').fill('This is an automated test'); - await page - .getByRole('group', { name: 'Filter' }) - .getByRole('textbox') - .fill(awsHostSetFilter); - await page - .getByRole('group', { name: 'Filter' }) - .getByRole('button', { name: 'Add' }) - .click(); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText(hostSetName), - ).toBeVisible(); - - await page.getByRole('link', { name: 'Hosts' }).click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText('Hosts'), - ).toBeVisible(); - - // Check number of hosts in host set - const expectedHosts = JSON.parse(awsHostSetIps); - await expect(async () => { - const rowCount = await page - .getByRole('table') - .getByRole('row') - .filter({ hasNot: page.getByRole('columnheader') }) - .count(); - - if (rowCount !== expectedHosts.length) { - await page.reload(); - await page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText(hostSetName) - .waitFor(); - } - expect(rowCount).toBe(expectedHosts.length); - }).toPass(); - - // Navigate to each host in the host set - for (const row of await page - .getByRole('table') - .getByRole('rowgroup') - .nth(1) - .getByRole('row') - .all()) { - const host = row.getByRole('link'); - let hostName = await host.innerText(); - await host.click(); + // Create host catalog + const hostCatalogName = 'Host Catalog ' + nanoid(); + await page + .getByRole('navigation', { name: 'Resources' }) + .getByRole('link', { name: 'Host Catalogs' }) + .click(); + await page.getByRole('link', { name: 'New', exact: true }).click(); + await page.getByLabel('Name').fill(hostCatalogName); + await page.getByLabel('Description').fill('This is an automated test'); + await page + .getByRole('group', { name: 'Type' }) + .getByLabel('Dynamic') + .click(); + await page + .getByRole('group', { name: 'Provider' }) + .getByLabel('AWS') + .click(); + await page.getByLabel('AWS Region').fill(awsRegion); + await page.getByLabel('Access Key ID').fill(awsAccessKeyId); + await page.getByLabel('Secret Access Key').fill(awsSecretAccessKey); + await page + .getByLabel('Disable credential rotation') + .click({ force: true }); + await page.getByRole('button', { name: 'Save' }).click(); + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); await expect( page .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText(hostName), + .getByText(hostCatalogName), ).toBeVisible(); + // Create first host set + const hostSetName = 'Host Set ' + nanoid(); + await page.getByRole('link', { name: 'Host Sets' }).click(); + await page.getByRole('link', { name: 'New', exact: true }).click(); + await page.getByLabel('Name (Optional)').fill(hostSetName); + await page.getByLabel('Description').fill('This is an automated test'); await page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText(hostSetName) + .getByRole('group', { name: 'Filter' }) + .getByRole('textbox') + .fill(awsHostSetFilter); + await page + .getByRole('group', { name: 'Filter' }) + .getByRole('button', { name: 'Add' }) .click(); + await page.getByRole('button', { name: 'Save' }).click(); + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); + await expect( + page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(hostSetName), + ).toBeVisible(); + await page.getByRole('link', { name: 'Hosts' }).click(); - } + await expect( + page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText('Hosts'), + ).toBeVisible(); - // Create a target and add DHC host set as a host source - const targetsPage = new TargetsPage(page); - const targetName = await targetsPage.createTarget(targetPort); - await targetsPage.addHostSourceToTarget(hostSetName); + // Check number of hosts in host set + const expectedHosts = JSON.parse(awsHostSetIps); + await expect(async () => { + const rowCount = await page + .getByRole('table') + .getByRole('row') + .filter({ hasNot: page.getByRole('columnheader') }) + .count(); - // Add another host source - const hostCatalogsPage = new HostCatalogsPage(page); - await hostCatalogsPage.createHostCatalog(); - const newHostSetName = await hostCatalogsPage.createHostSet(); - await page - .getByRole('navigation', { name: 'Resources' }) - .getByRole('link', { name: 'Targets' }) - .click(); - await page.getByRole('link', { name: targetName }).click(); - await targetsPage.addHostSourceToTarget(newHostSetName); + if (rowCount !== expectedHosts.length) { + await page.reload(); + await page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(hostSetName) + .waitFor(); + } + expect(rowCount).toBe(expectedHosts.length); + }).toPass(); - // Remove the host source from the target - await page - .getByRole('link', { name: newHostSetName }) - .locator('..') - .locator('..') - .getByRole('button', { name: 'Manage' }) - .click(); - await page.getByRole('button', { name: 'Remove' }).click(); - await page.getByRole('button', { name: 'OK', exact: true }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); + // Navigate to each host in the host set + for (const row of await page + .getByRole('table') + .getByRole('rowgroup') + .nth(1) + .getByRole('row') + .all()) { + const host = row.getByRole('link'); + let hostName = await host.innerText(); + await host.click(); + await expect( + page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(hostName), + ).toBeVisible(); - // Set up target credentials - const credentialStoresPage = new CredentialStoresPage(page); - await credentialStoresPage.createStaticCredentialStore(); - const credentialName = - await credentialStoresPage.createStaticCredentialKeyPair( - sshUser, - sshKeyPath, + await page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(hostSetName) + .click(); + await page.getByRole('link', { name: 'Hosts' }).click(); + } + + // Create a target and add DHC host set as a host source + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createTarget(targetPort); + await targetsPage.addHostSourceToTarget(hostSetName); + + // Add another host source + const hostCatalogsPage = new HostCatalogsPage(page); + await hostCatalogsPage.createHostCatalog(); + const newHostSetName = await hostCatalogsPage.createHostSet(); + await page + .getByRole('navigation', { name: 'Resources' }) + .getByRole('link', { name: 'Targets' }) + .click(); + await page.getByRole('link', { name: targetName }).click(); + await targetsPage.addHostSourceToTarget(newHostSetName); + + // Remove the host source from the target + await page + .getByRole('link', { name: newHostSetName }) + .locator('..') + .locator('..') + .getByRole('button', { name: 'Manage' }) + .click(); + await page.getByRole('button', { name: 'Remove' }).click(); + await page.getByRole('button', { name: 'OK', exact: true }).click(); + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); + + // Set up target credentials + const credentialStoresPage = new CredentialStoresPage(page); + await credentialStoresPage.createStaticCredentialStore(); + const credentialName = + await credentialStoresPage.createStaticCredentialKeyPair( + sshUser, + sshKeyPath, + ); + await targetsPage.addBrokeredCredentialsToTarget( + targetName, + credentialName, ); - await targetsPage.addBrokeredCredentialsToTarget( - targetName, - credentialName, - ); - // Connect to target - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - const orgId = await boundaryCli.getOrgIdFromName(orgName); - const projectId = await boundaryCli.getProjectIdFromName( - orgId, - projectName, - ); - const targetId = await boundaryCli.getTargetIdFromName( - projectId, - targetName, - ); - connect = await boundaryCli.connectSshToTarget(targetId); - const sessionsPage = new SessionsPage(page); - await sessionsPage.waitForSessionToBeVisible(targetName); - } finally { - if (connect) { - connect.kill('SIGTERM'); - } - - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - - if (orgName) { + // Connect to target + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); const orgId = await boundaryCli.getOrgIdFromName(orgName); - if (orgId) { - await boundaryCli.deleteScope(orgId); + const projectId = await boundaryCli.getProjectIdFromName( + orgId, + projectName, + ); + const targetId = await boundaryCli.getTargetIdFromName( + projectId, + targetName, + ); + connect = await boundaryCli.connectSshToTarget(targetId); + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); + } finally { + if (connect) { + connect.kill('SIGTERM'); + } + + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + + if (orgName) { + const orgId = await boundaryCli.getOrgIdFromName(orgName); + if (orgId) { + await boundaryCli.deleteScope(orgId); + } } } - } - }); + }, + ); }); diff --git a/e2e-tests/admin/tests/global-settings.spec.js b/e2e-tests/admin/tests/global-settings.spec.js index 670e3a1cc..4d884aed2 100644 --- a/e2e-tests/admin/tests/global-settings.spec.js +++ b/e2e-tests/admin/tests/global-settings.spec.js @@ -10,52 +10,56 @@ import * as boundaryCli from '../../helpers/boundary-cli'; import { OrgsPage } from '../pages/orgs.js'; import { StoragePoliciesPage } from '../pages/storage-policies.js'; -test('Global Settings @ent @aws @docker', async ({ - page, - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, -}) => { - await page.goto('/'); - let policyName; - try { - // Create a storage policy - const storagePoliciesPage = new StoragePoliciesPage(page); - policyName = await storagePoliciesPage.createStoragePolicy(); - const orgsPage = new OrgsPage(page); - await orgsPage.attachStoragePolicy(policyName); +test( + 'Global Settings', + { tag: ['@ent', '@aws', '@docker'] }, + async ({ + page, + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + }) => { + await page.goto('/'); + let policyName; + try { + // Create a storage policy + const storagePoliciesPage = new StoragePoliciesPage(page); + policyName = await storagePoliciesPage.createStoragePolicy(); + const orgsPage = new OrgsPage(page); + await orgsPage.attachStoragePolicy(policyName); - // Detach storage policy - await page.getByRole('button', { name: 'Manage', exact: true }).click(); - await page - .getByRole('button', { name: 'Detach Storage Policy', exact: true }) - .click(); - await page.getByRole('button', { name: 'OK', exact: true }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); + // Detach storage policy + await page.getByRole('button', { name: 'Manage', exact: true }).click(); + await page + .getByRole('button', { name: 'Detach Storage Policy', exact: true }) + .click(); + await page.getByRole('button', { name: 'OK', exact: true }).click(); + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); - // Navigate to orgs page when done - await page - .getByRole('navigation', { name: 'General' }) - .getByRole('link', { name: 'Orgs' }) - .click(); - await expect(page.getByRole('heading', { name: 'Orgs' })).toBeVisible(); - } finally { - if (policyName) { - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - const storagePolicyId = await boundaryCli.getPolicyIdFromName( - 'global', - policyName, - ); - await boundaryCli.deletePolicy(storagePolicyId); + // Navigate to orgs page when done + await page + .getByRole('navigation', { name: 'General' }) + .getByRole('link', { name: 'Orgs' }) + .click(); + await expect(page.getByRole('heading', { name: 'Orgs' })).toBeVisible(); + } finally { + if (policyName) { + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + const storagePolicyId = await boundaryCli.getPolicyIdFromName( + 'global', + policyName, + ); + await boundaryCli.deletePolicy(storagePolicyId); + } } - } -}); + }, +); diff --git a/e2e-tests/admin/tests/group-role.spec.js b/e2e-tests/admin/tests/group-role.spec.js index f135dcb64..9c919094c 100644 --- a/e2e-tests/admin/tests/group-role.spec.js +++ b/e2e-tests/admin/tests/group-role.spec.js @@ -14,35 +14,39 @@ test.beforeAll(async () => { await boundaryCli.checkBoundaryCli(); }); -test('Verify a new role can be created and associated with a group @ce @ent @aws @docker', async ({ - page, - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, -}) => { - await page.goto('/'); - let orgName; - try { - const orgsPage = new OrgsPage(page); - orgName = await orgsPage.createOrg(); - const groupsPage = new GroupsPage(page); - const groupName = await groupsPage.createGroup(); - await groupsPage.addMemberToGroup('admin'); - const rolesPage = new RolesPage(page); - await rolesPage.createRole(); - await rolesPage.addPrincipalToRole(groupName); - await rolesPage.addGrantsToRole('ids=*;type=*;actions=read,list'); - } finally { - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - const orgId = await boundaryCli.getOrgIdFromName(orgName); - if (orgId) { - await boundaryCli.deleteScope(orgId); +test( + 'Verify a new role can be created and associated with a group', + { tag: ['@ce', '@ent', '@aws', '@docker'] }, + async ({ + page, + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + }) => { + await page.goto('/'); + let orgName; + try { + const orgsPage = new OrgsPage(page); + orgName = await orgsPage.createOrg(); + const groupsPage = new GroupsPage(page); + const groupName = await groupsPage.createGroup(); + await groupsPage.addMemberToGroup('admin'); + const rolesPage = new RolesPage(page); + await rolesPage.createRole(); + await rolesPage.addPrincipalToRole(groupName); + await rolesPage.addGrantsToRole('ids=*;type=*;actions=read,list'); + } finally { + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + const orgId = await boundaryCli.getOrgIdFromName(orgName); + if (orgId) { + await boundaryCli.deleteScope(orgId); + } } - } -}); + }, +); diff --git a/e2e-tests/admin/tests/login.spec.js b/e2e-tests/admin/tests/login.spec.js index a5b551077..94967e7e7 100644 --- a/e2e-tests/admin/tests/login.spec.js +++ b/e2e-tests/admin/tests/login.spec.js @@ -11,68 +11,70 @@ import { LoginPage } from '../pages/login.js'; // Reset storage state for this file to avoid being authenticated test.use({ storageState: { cookies: [], origins: [] } }); -test('Log in, log out, and then log back in @ce @ent @aws @docker', async ({ - page, - adminLoginName, - adminPassword, -}) => { - await page.goto('/'); +test( + 'Log in, log out, and then log back in', + { tag: ['@ce', '@ent', '@aws', '@docker'] }, + async ({ page, adminLoginName, adminPassword }) => { + await page.goto('/'); - // Log in - const loginPage = new LoginPage(page); - await loginPage.login(adminLoginName, adminPassword); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText('Orgs'), - ).toBeVisible(); + // Log in + const loginPage = new LoginPage(page); + await loginPage.login(adminLoginName, adminPassword); + await expect( + page.getByRole('navigation', { name: 'breadcrumbs' }).getByText('Orgs'), + ).toBeVisible(); - // Log out - await loginPage.logout(adminLoginName); - await page.reload(); + // Log out + await loginPage.logout(adminLoginName); + await page.reload(); - // Log back in - await loginPage.login(adminLoginName, adminPassword); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText('Orgs'), - ).toBeVisible(); -}); + // Log back in + await loginPage.login(adminLoginName, adminPassword); + await expect( + page.getByRole('navigation', { name: 'breadcrumbs' }).getByText('Orgs'), + ).toBeVisible(); + }, +); test.describe('Failure Cases', () => { test.beforeEach(async ({ page }) => { await page.goto('/'); }); - test('Log in with invalid password @ce @ent @aws @docker', async ({ - page, - adminLoginName, - }) => { - await page.getByLabel('Login Name').fill(adminLoginName); - await page.getByLabel('Password', { exact: true }).fill('badpassword'); - await page.getByRole('button', { name: 'Sign In' }).click(); - await expect(page.getByRole('alert').getByText('Error')).toBeVisible(); - await expect(page.getByRole('button', { name: 'Sign In' })).toBeVisible(); - await expect(page.getByText(adminLoginName)).toBeHidden(); - }); + test( + 'Log in with invalid password', + { tag: ['@ce', '@ent', '@aws', '@docker'] }, + async ({ page, adminLoginName }) => { + await page.getByLabel('Login Name').fill(adminLoginName); + await page.getByLabel('Password', { exact: true }).fill('badpassword'); + await page.getByRole('button', { name: 'Sign In' }).click(); + await expect(page.getByRole('alert').getByText('Error')).toBeVisible(); + await expect(page.getByRole('button', { name: 'Sign In' })).toBeVisible(); + await expect(page.getByText(adminLoginName)).toBeHidden(); + }, + ); - test('Log in with only username @ce @ent @aws @docker', async ({ - page, - adminLoginName, - }) => { - await page.getByLabel('Login Name').fill('testuser'); - await page.getByRole('button', { name: 'Sign In' }).click(); - await expect(page.getByRole('alert').getByText('Error')).toBeVisible(); - await expect(page.getByRole('button', { name: 'Sign In' })).toBeVisible(); - await expect(page.getByText(adminLoginName)).toBeHidden(); - }); + test( + 'Log in with only username', + { tag: ['@ce', '@ent', '@aws', '@docker'] }, + async ({ page, adminLoginName }) => { + await page.getByLabel('Login Name').fill('testuser'); + await page.getByRole('button', { name: 'Sign In' }).click(); + await expect(page.getByRole('alert').getByText('Error')).toBeVisible(); + await expect(page.getByRole('button', { name: 'Sign In' })).toBeVisible(); + await expect(page.getByText(adminLoginName)).toBeHidden(); + }, + ); - test('Log in with only password @ce @ent @aws @docker', async ({ - page, - adminLoginName, - adminPassword, - }) => { - await page.getByLabel('Password', { exact: true }).fill(adminPassword); - await page.getByRole('button', { name: 'Sign In' }).click(); - await expect(page.getByRole('alert').getByText('Error')).toBeVisible(); - await expect(page.getByRole('button', { name: 'Sign In' })).toBeVisible(); - await expect(page.getByText(adminLoginName)).toBeHidden(); - }); + test( + 'Log in with only password', + { tag: ['@ce', '@ent', '@aws', '@docker'] }, + async ({ page, adminLoginName, adminPassword }) => { + await page.getByLabel('Password', { exact: true }).fill(adminPassword); + await page.getByRole('button', { name: 'Sign In' }).click(); + await expect(page.getByRole('alert').getByText('Error')).toBeVisible(); + await expect(page.getByRole('button', { name: 'Sign In' })).toBeVisible(); + await expect(page.getByText(adminLoginName)).toBeHidden(); + }, + ); }); diff --git a/e2e-tests/admin/tests/pagination.spec.js b/e2e-tests/admin/tests/pagination.spec.js index 4fa270f03..ab4d4cdbb 100644 --- a/e2e-tests/admin/tests/pagination.spec.js +++ b/e2e-tests/admin/tests/pagination.spec.js @@ -8,153 +8,156 @@ import { expect } from '@playwright/test'; import * as boundaryHttp from '../../helpers/boundary-http.js'; -test('Search and Pagination (Targets) @ce @ent @aws @docker', async ({ - page, - request, -}) => { - let org; - try { - org = await boundaryHttp.createOrg(request); - const project = await boundaryHttp.createProject(request, org.id); +test( + 'Search and Pagination (Targets)', + { tag: ['@ce', '@ent', '@aws', '@docker'] }, + async ({ page, request }) => { + let org; + try { + org = await boundaryHttp.createOrg(request); + const project = await boundaryHttp.createProject(request, org.id); - // Create targets - let targets = []; - const targetCount = 15; - for (let i = 0; i < targetCount; i++) { - const target = await boundaryHttp.createTarget(request, { - scopeId: project.id, - type: 'tcp', - port: 22, - }); - targets.push(target); + // Create targets + let targets = []; + const targetCount = 15; + for (let i = 0; i < targetCount; i++) { + const target = await boundaryHttp.createTarget(request, { + scopeId: project.id, + type: 'tcp', + port: 22, + }); + targets.push(target); + } + + // Navigate to targets page + await page.goto('/'); + await page.getByRole('link', { name: org.name }).click(); + await page.getByRole('link', { name: project.name }).click(); + await page + .getByRole('navigation', { name: 'Resources' }) + .getByRole('link', { name: 'Targets' }) + .click(); + await expect( + page.getByRole('heading', { name: 'Targets' }), + ).toBeVisible(); + + // Check pagination + // The most recent target should be visible. The oldest target should not be + // visible since there are 15 targets and the default page size is 10. + await expect( + page.getByRole('link', { name: targets[targets.length - 1].name }), + ).toBeVisible(); + await expect( + page.getByRole('link', { name: targets[0].name }), + ).toBeHidden(); + + // Navigate to the second page. The oldest target should now be visible. + await page + .getByRole('navigation', { name: 'pagination' }) + .getByRole('link', { name: 'page 2' }) + .click(); + await expect( + page.getByRole('link', { name: targets[0].name }), + ).toBeVisible(); + await expect( + page.getByRole('link', { name: targets[targets.length - 1].name }), + ).toBeHidden(); + + // Use the "previous page" button to navigate back to the first page. + await page + .getByRole('navigation', { name: 'pagination' }) + .getByRole('link', { name: 'Previous page' }) + .click(); + await expect( + page.getByRole('link', { name: targets[0].name }), + ).toBeHidden(); + await expect( + page.getByRole('link', { name: targets[targets.length - 1].name }), + ).toBeVisible(); + + // Use the "next page" button to navigate back to the second page. + await page + .getByRole('navigation', { name: 'pagination' }) + .getByRole('link', { name: 'Next page' }) + .click(); + await expect( + page.getByRole('link', { name: targets[0].name }), + ).toBeVisible(); + await expect( + page.getByRole('link', { name: targets[targets.length - 1].name }), + ).toBeHidden(); + + // Use the search box to find the most recent target. + await page + .getByRole('searchbox', { name: 'Search' }) + .fill(targets[targets.length - 1].name); + await expect( + page.getByRole('link', { name: targets[0].name }), + ).toBeHidden(); + await expect( + page.getByRole('link', { name: targets[targets.length - 1].name }), + ).toBeVisible(); + await expect( + page.getByRole('link', { name: targets[targets.length - 2].name }), + ).toBeHidden(); + + // Clear the search box + await page.getByRole('searchbox', { name: 'Search' }).clear(); + await expect( + page.getByRole('link', { name: targets[0].name }), + ).toBeHidden(); + await expect( + page.getByRole('link', { name: targets[targets.length - 2].name }), + ).toBeVisible(); + await expect( + page.getByRole('link', { name: targets[targets.length - 1].name }), + ).toBeVisible(); + + // Use the "Items per page" options to show 30 items per page. + await page + .getByRole('combobox', { name: 'Items per page' }) + .selectOption('30'); + await expect( + page.getByRole('link', { name: targets[0].name }), + ).toBeVisible(); + await expect( + page.getByRole('link', { name: targets[targets.length - 1].name }), + ).toBeVisible(); + await expect( + page.getByRole('link', { name: targets[targets.length - 2].name }), + ).toBeVisible(); + + // Use the "Items per page" options to show 50 items per page. + await page + .getByRole('combobox', { name: 'Items per page' }) + .selectOption('50'); + await expect( + page.getByRole('link', { name: targets[0].name }), + ).toBeVisible(); + await expect( + page.getByRole('link', { name: targets[targets.length - 1].name }), + ).toBeVisible(); + await expect( + page.getByRole('link', { name: targets[targets.length - 2].name }), + ).toBeVisible(); + + // Use the "Items per page" options to show 10 items per page. + await page + .getByRole('combobox', { name: 'Items per page' }) + .selectOption('10'); + await expect( + page.getByRole('link', { name: targets[0].name }), + ).toBeHidden(); + await expect( + page.getByRole('link', { name: targets[targets.length - 2].name }), + ).toBeVisible(); + await expect( + page.getByRole('link', { name: targets[targets.length - 1].name }), + ).toBeVisible(); + } finally { + if (org.id) { + org = await request.delete(`/v1/scopes/${org.id}`); + } } - - // Navigate to targets page - await page.goto('/'); - await page.getByRole('link', { name: org.name }).click(); - await page.getByRole('link', { name: project.name }).click(); - await page - .getByRole('navigation', { name: 'Resources' }) - .getByRole('link', { name: 'Targets' }) - .click(); - await expect(page.getByRole('heading', { name: 'Targets' })).toBeVisible(); - - // Check pagination - // The most recent target should be visible. The oldest target should not be - // visible since there are 15 targets and the default page size is 10. - await expect( - page.getByRole('link', { name: targets[targets.length - 1].name }), - ).toBeVisible(); - await expect( - page.getByRole('link', { name: targets[0].name }), - ).toBeHidden(); - - // Navigate to the second page. The oldest target should now be visible. - await page - .getByRole('navigation', { name: 'pagination' }) - .getByRole('link', { name: 'page 2' }) - .click(); - await expect( - page.getByRole('link', { name: targets[0].name }), - ).toBeVisible(); - await expect( - page.getByRole('link', { name: targets[targets.length - 1].name }), - ).toBeHidden(); - - // Use the "previous page" button to navigate back to the first page. - await page - .getByRole('navigation', { name: 'pagination' }) - .getByRole('link', { name: 'Previous page' }) - .click(); - await expect( - page.getByRole('link', { name: targets[0].name }), - ).toBeHidden(); - await expect( - page.getByRole('link', { name: targets[targets.length - 1].name }), - ).toBeVisible(); - - // Use the "next page" button to navigate back to the second page. - await page - .getByRole('navigation', { name: 'pagination' }) - .getByRole('link', { name: 'Next page' }) - .click(); - await expect( - page.getByRole('link', { name: targets[0].name }), - ).toBeVisible(); - await expect( - page.getByRole('link', { name: targets[targets.length - 1].name }), - ).toBeHidden(); - - // Use the search box to find the most recent target. - await page - .getByRole('searchbox', { name: 'Search' }) - .fill(targets[targets.length - 1].name); - await expect( - page.getByRole('link', { name: targets[0].name }), - ).toBeHidden(); - await expect( - page.getByRole('link', { name: targets[targets.length - 1].name }), - ).toBeVisible(); - await expect( - page.getByRole('link', { name: targets[targets.length - 2].name }), - ).toBeHidden(); - - // Clear the search box - await page.getByRole('searchbox', { name: 'Search' }).clear(); - await expect( - page.getByRole('link', { name: targets[0].name }), - ).toBeHidden(); - await expect( - page.getByRole('link', { name: targets[targets.length - 2].name }), - ).toBeVisible(); - await expect( - page.getByRole('link', { name: targets[targets.length - 1].name }), - ).toBeVisible(); - - // Use the "Items per page" options to show 30 items per page. - await page - .getByRole('combobox', { name: 'Items per page' }) - .selectOption('30'); - await expect( - page.getByRole('link', { name: targets[0].name }), - ).toBeVisible(); - await expect( - page.getByRole('link', { name: targets[targets.length - 1].name }), - ).toBeVisible(); - await expect( - page.getByRole('link', { name: targets[targets.length - 2].name }), - ).toBeVisible(); - - // Use the "Items per page" options to show 50 items per page. - await page - .getByRole('combobox', { name: 'Items per page' }) - .selectOption('50'); - await expect( - page.getByRole('link', { name: targets[0].name }), - ).toBeVisible(); - await expect( - page.getByRole('link', { name: targets[targets.length - 1].name }), - ).toBeVisible(); - await expect( - page.getByRole('link', { name: targets[targets.length - 2].name }), - ).toBeVisible(); - - // Use the "Items per page" options to show 10 items per page. - await page - .getByRole('combobox', { name: 'Items per page' }) - .selectOption('10'); - await expect( - page.getByRole('link', { name: targets[0].name }), - ).toBeHidden(); - await expect( - page.getByRole('link', { name: targets[targets.length - 2].name }), - ).toBeVisible(); - await expect( - page.getByRole('link', { name: targets[targets.length - 1].name }), - ).toBeVisible(); - } finally { - if (org.id) { - org = await request.delete(`/v1/scopes/${org.id}`); - } - } -}); + }, +); diff --git a/e2e-tests/admin/tests/session-recording-aws-ent.spec.js b/e2e-tests/admin/tests/session-recording-aws-ent.spec.js index 1119cc326..0d4113631 100644 --- a/e2e-tests/admin/tests/session-recording-aws-ent.spec.js +++ b/e2e-tests/admin/tests/session-recording-aws-ent.spec.js @@ -21,227 +21,237 @@ test.beforeAll(async () => { await boundaryCli.checkBoundaryCli(); }); -test('Session Recording Test (AWS) @ent @aws', async ({ - page, - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - awsAccessKeyId, - awsBucketName, - awsRegion, - awsSecretAccessKey, - sshUser, - sshKeyPath, - targetAddress, - targetPort, - workerTagEgress, -}) => { - await page.goto('/'); - let orgId; - let policyName; - let storageBucket; - let connect; - try { - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - - // Create org - const orgsPage = new OrgsPage(page); - const orgName = await orgsPage.createOrg(); - - // Create project - const projectsPage = new ProjectsPage(page); - const projectName = await projectsPage.createProject(); - - // Create storage bucket - await page.getByRole('link', { name: 'Orgs', exact: true }).click(); - const storageBucketsPage = new StorageBucketsPage(page); - const storageBucketName = await storageBucketsPage.createStorageBucketAws( - orgName, - awsBucketName, - awsRegion, - awsAccessKeyId, - awsSecretAccessKey, - `"${workerTagEgress}" in "/tags/type"`, - ); - const storageBuckets = JSON.parse( - execSync('boundary storage-buckets list --recursive -format json'), - ); - storageBucket = storageBuckets.items.filter( - (obj) => obj.name == storageBucketName, - )[0]; - - // Create target - await page.getByRole('link', { name: 'Orgs', exact: true }).click(); - await expect(page.getByRole('heading', { name: 'Orgs' })).toBeVisible(); - await page.getByRole('link', { name: orgName }).click(); - await page.getByRole('link', { name: projectName }).click(); - const targetsPage = new TargetsPage(page); - const targetName = await targetsPage.createSshTargetWithAddressEnt( - targetAddress, - targetPort, - ); - await targetsPage.addEgressWorkerFilterToTarget( - `"${workerTagEgress}" in "/tags/type"`, - ); - const credentialStoresPage = new CredentialStoresPage(page); - await credentialStoresPage.createStaticCredentialStore(); - const credentialName = - await credentialStoresPage.createStaticCredentialKeyPair( - sshUser, - sshKeyPath, +test( + 'Session Recording Test (AWS)', + { tag: ['@ent', '@aws'] }, + async ({ + page, + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + awsAccessKeyId, + awsBucketName, + awsRegion, + awsSecretAccessKey, + sshUser, + sshKeyPath, + targetAddress, + targetPort, + workerTagEgress, + }) => { + await page.goto('/'); + let orgId; + let policyName; + let storageBucket; + let connect; + try { + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, ); - await targetsPage.addInjectedCredentialsToTarget( - targetName, - credentialName, - ); - await page.getByRole('link', { name: targetName }).click(); - await targetsPage.enableSessionRecording(storageBucketName); - // Create storage policy in org scope: keep session recordings forever - await page.getByRole('link', { name: 'Orgs', exact: true }).click(); - await expect(page.getByRole('heading', { name: 'Orgs' })).toBeVisible(); - await page.getByRole('link', { name: orgName }).click(); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText(orgName), - ).toBeVisible(); - const storagePoliciesPage = new StoragePoliciesPage(page); - policyName = await storagePoliciesPage.createStoragePolicy(); - await orgsPage.attachStoragePolicy(policyName); + // Create org + const orgsPage = new OrgsPage(page); + const orgName = await orgsPage.createOrg(); - // Establish connection to target and cancel it - orgId = await boundaryCli.getOrgIdFromName(orgName); - const projectId = await boundaryCli.getProjectIdFromName( - orgId, - projectName, - ); - const targetId = await boundaryCli.getTargetIdFromName( - projectId, - targetName, - ); - connect = await boundaryCli.connectSshToTarget(targetId); - await page.getByRole('link', { name: 'Projects', exact: true }).click(); - await page.getByRole('link', { name: projectName }).click(); - const sessionsPage = new SessionsPage(page); - await sessionsPage.waitForSessionToBeVisible(targetName); - await page - .getByRole('cell', { name: targetName }) - .locator('..') - .getByRole('button', { name: 'Cancel' }) - .click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss', exact: true }).click(); - await boundaryCli.waitForSessionRecording(storageBucket.id); + // Create project + const projectsPage = new ProjectsPage(page); + const projectName = await projectsPage.createProject(); - // Play back session recording - await page.getByRole('link', { name: 'Orgs', exact: true }).click(); - await page - .getByRole('navigation', { name: 'General' }) - .getByRole('link', { name: 'Session Recordings', exact: true }) - .click(); - await page - .getByRole('row', { name: targetName }) - .getByRole('link', { name: 'View' }) - .click(); - await page - .getByRole('cell', { name: 'Channel 1' }) - .locator('..') - .getByRole('link', { name: 'Play' }) - .click(); - await page.locator('div.session-recording-player').hover(); - await page.locator('.ap-playback-button').click(); + // Create storage bucket + await page.getByRole('link', { name: 'Orgs', exact: true }).click(); + const storageBucketsPage = new StorageBucketsPage(page); + const storageBucketName = await storageBucketsPage.createStorageBucketAws( + orgName, + awsBucketName, + awsRegion, + awsAccessKeyId, + awsSecretAccessKey, + `"${workerTagEgress}" in "/tags/type"`, + ); + const storageBuckets = JSON.parse( + execSync('boundary storage-buckets list --recursive -format json'), + ); + storageBucket = storageBuckets.items.filter( + (obj) => obj.name == storageBucketName, + )[0]; - // Try to delete session recording: expect failure - await page.getByRole('link', { name: 'Orgs', exact: true }).click(); - await page - .getByRole('navigation', { name: 'General' }) - .getByRole('link', { name: 'Session Recordings', exact: true }) - .click(); - await page - .getByRole('row', { name: targetName }) - .getByRole('link', { name: 'View' }) - .click(); - await page.getByText('Manage').click(); - await page.getByRole('button', { name: 'Delete recording' }).click(); - await page.getByRole('button', { name: 'OK', exact: true }).click(); - await expect( - page.getByRole('alert').getByText('Error', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); + // Create target + await page.getByRole('link', { name: 'Orgs', exact: true }).click(); + await expect(page.getByRole('heading', { name: 'Orgs' })).toBeVisible(); + await page.getByRole('link', { name: orgName }).click(); + await page.getByRole('link', { name: projectName }).click(); + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createSshTargetWithAddressEnt( + targetAddress, + targetPort, + ); + await targetsPage.addEgressWorkerFilterToTarget( + `"${workerTagEgress}" in "/tags/type"`, + ); + const credentialStoresPage = new CredentialStoresPage(page); + await credentialStoresPage.createStaticCredentialStore(); + const credentialName = + await credentialStoresPage.createStaticCredentialKeyPair( + sshUser, + sshKeyPath, + ); + await targetsPage.addInjectedCredentialsToTarget( + targetName, + credentialName, + ); + await page.getByRole('link', { name: targetName }).click(); + await targetsPage.enableSessionRecording(storageBucketName); - // Edit storage policy: do not protect from deletion - await page.getByRole('link', { name: 'Orgs', exact: true }).click(); - await expect(page.getByRole('heading', { name: 'Orgs' })).toBeVisible(); - await page.getByRole('link', { name: orgName }).click(); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText(orgName), - ).toBeVisible(); - await page - .getByRole('link', { name: 'Storage Policies', exact: true }) - .click(); - await page.getByRole('link', { name: policyName }).click(); - await page.getByRole('button', { name: 'Edit form' }).click(); - await page - .getByLabel('Retention Policy') - .selectOption({ label: 'Do not protect, allow deletion any time' }); - await page.getByLabel('Deletion Policy').selectOption({ label: 'Custom' }); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); + // Create storage policy in org scope: keep session recordings forever + await page.getByRole('link', { name: 'Orgs', exact: true }).click(); + await expect(page.getByRole('heading', { name: 'Orgs' })).toBeVisible(); + await page.getByRole('link', { name: orgName }).click(); + await expect( + page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(orgName), + ).toBeVisible(); + const storagePoliciesPage = new StoragePoliciesPage(page); + policyName = await storagePoliciesPage.createStoragePolicy(); + await orgsPage.attachStoragePolicy(policyName); - // BUG? After the error trying to delete the session recording, subsequent - // actions result in an error. Reloading the page fixes the issue. This is a - // temporary workaround. - await page.reload(); - - // Re-apply storage policy to the session recording and delete - await page.getByRole('link', { name: 'Orgs', exact: true }).click(); - await page - .getByRole('link', { name: 'Session Recordings', exact: true }) - .click(); - await page - .getByRole('row', { name: targetName }) - .getByRole('link', { name: 'View' }) - .click(); - const sessionRecordingsPage = new SessionRecordingsPage(page); - await sessionRecordingsPage.reapplyStoragePolicy(); - await sessionRecordingsPage.deleteResource(); - - // Detach storage bucket from target - await page.getByRole('link', { name: 'Orgs', exact: true }).click(); - await expect(page.getByRole('heading', { name: 'Orgs' })).toBeVisible(); - await page.getByRole('link', { name: orgName }).click(); - await page.getByRole('link', { name: projectName }).click(); - await page.getByRole('link', { name: 'Targets', exact: true }).click(); - await page.getByRole('link', { name: targetName }).click(); - await targetsPage.detachStorageBucket(); - } finally { - // End `boundary connect` process - if (connect) { - connect.kill('SIGTERM'); - } - - if (policyName) { - const storagePolicyId = await boundaryCli.getPolicyIdFromName( + // Establish connection to target and cancel it + orgId = await boundaryCli.getOrgIdFromName(orgName); + const projectId = await boundaryCli.getProjectIdFromName( orgId, - policyName, + projectName, ); - await boundaryCli.deletePolicy(storagePolicyId); + const targetId = await boundaryCli.getTargetIdFromName( + projectId, + targetName, + ); + connect = await boundaryCli.connectSshToTarget(targetId); + await page.getByRole('link', { name: 'Projects', exact: true }).click(); + await page.getByRole('link', { name: projectName }).click(); + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); + await page + .getByRole('cell', { name: targetName }) + .locator('..') + .getByRole('button', { name: 'Cancel' }) + .click(); + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss', exact: true }).click(); + await boundaryCli.waitForSessionRecording(storageBucket.id); + + // Play back session recording + await page.getByRole('link', { name: 'Orgs', exact: true }).click(); + await page + .getByRole('navigation', { name: 'General' }) + .getByRole('link', { name: 'Session Recordings', exact: true }) + .click(); + await page + .getByRole('row', { name: targetName }) + .getByRole('link', { name: 'View' }) + .click(); + await page + .getByRole('cell', { name: 'Channel 1' }) + .locator('..') + .getByRole('link', { name: 'Play' }) + .click(); + await page.locator('div.session-recording-player').hover(); + await page.locator('.ap-playback-button').click(); + + // Try to delete session recording: expect failure + await page.getByRole('link', { name: 'Orgs', exact: true }).click(); + await page + .getByRole('navigation', { name: 'General' }) + .getByRole('link', { name: 'Session Recordings', exact: true }) + .click(); + await page + .getByRole('row', { name: targetName }) + .getByRole('link', { name: 'View' }) + .click(); + await page.getByText('Manage').click(); + await page.getByRole('button', { name: 'Delete recording' }).click(); + await page.getByRole('button', { name: 'OK', exact: true }).click(); + await expect( + page.getByRole('alert').getByText('Error', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); + + // Edit storage policy: do not protect from deletion + await page.getByRole('link', { name: 'Orgs', exact: true }).click(); + await expect(page.getByRole('heading', { name: 'Orgs' })).toBeVisible(); + await page.getByRole('link', { name: orgName }).click(); + await expect( + page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(orgName), + ).toBeVisible(); + await page + .getByRole('link', { name: 'Storage Policies', exact: true }) + .click(); + await page.getByRole('link', { name: policyName }).click(); + await page.getByRole('button', { name: 'Edit form' }).click(); + await page + .getByLabel('Retention Policy') + .selectOption({ label: 'Do not protect, allow deletion any time' }); + await page + .getByLabel('Deletion Policy') + .selectOption({ label: 'Custom' }); + await page.getByRole('button', { name: 'Save' }).click(); + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); + + // BUG? After the error trying to delete the session recording, subsequent + // actions result in an error. Reloading the page fixes the issue. This is a + // temporary workaround. + await page.reload(); + + // Re-apply storage policy to the session recording and delete + await page.getByRole('link', { name: 'Orgs', exact: true }).click(); + await page + .getByRole('link', { name: 'Session Recordings', exact: true }) + .click(); + await page + .getByRole('row', { name: targetName }) + .getByRole('link', { name: 'View' }) + .click(); + const sessionRecordingsPage = new SessionRecordingsPage(page); + await sessionRecordingsPage.reapplyStoragePolicy(); + await sessionRecordingsPage.deleteResource(); + + // Detach storage bucket from target + await page.getByRole('link', { name: 'Orgs', exact: true }).click(); + await expect(page.getByRole('heading', { name: 'Orgs' })).toBeVisible(); + await page.getByRole('link', { name: orgName }).click(); + await page.getByRole('link', { name: projectName }).click(); + await page.getByRole('link', { name: 'Targets', exact: true }).click(); + await page.getByRole('link', { name: targetName }).click(); + await targetsPage.detachStorageBucket(); + } finally { + // End `boundary connect` process + if (connect) { + connect.kill('SIGTERM'); + } + + if (policyName) { + const storagePolicyId = await boundaryCli.getPolicyIdFromName( + orgId, + policyName, + ); + await boundaryCli.deletePolicy(storagePolicyId); + } + if (storageBucket) { + await boundaryCli.deleteStorageBucket(storageBucket.id); + } + if (orgId) { + await boundaryCli.deleteScope(orgId); + } } - if (storageBucket) { - await boundaryCli.deleteStorageBucket(storageBucket.id); - } - if (orgId) { - await boundaryCli.deleteScope(orgId); - } - } -}); + }, +); diff --git a/e2e-tests/admin/tests/session-recording-minio-ent.spec.js b/e2e-tests/admin/tests/session-recording-minio-ent.spec.js index 4fc636113..2eaa88a27 100644 --- a/e2e-tests/admin/tests/session-recording-minio-ent.spec.js +++ b/e2e-tests/admin/tests/session-recording-minio-ent.spec.js @@ -21,234 +21,245 @@ test.beforeAll(async () => { await boundaryCli.checkBoundaryCli(); }); -test('Session Recording Test (MinIO) @ent @docker', async ({ - page, - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - bucketAccessKeyId, - bucketEndpointUrl, - bucketName, - bucketSecretAccessKey, - region, - sshUser, - sshKeyPath, - targetAddress, - targetPort, - workerTagEgress, -}) => { - await page.goto('/'); +test( + 'Session Recording Test (MinIO)', + { tag: ['@ent', '@docker'] }, + async ({ + page, + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + bucketAccessKeyId, + bucketEndpointUrl, + bucketName, + bucketSecretAccessKey, + region, + sshUser, + sshKeyPath, + targetAddress, + targetPort, + workerTagEgress, + }) => { + await page.goto('/'); - let orgId; - let policyName; - let storageBucket; - let connect; - try { - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - - // Create org - const orgsPage = new OrgsPage(page); - const orgName = await orgsPage.createOrg(); - - // Create project - const projectsPage = new ProjectsPage(page); - const projectName = await projectsPage.createProject(); - - // Create storage bucket - await page.getByRole('link', { name: 'Orgs', exact: true }).click(); - const storageBucketsPage = new StorageBucketsPage(page); - const storageBucketName = await storageBucketsPage.createStorageBucketMinio( - orgName, - bucketEndpointUrl, - bucketName, - region, - bucketAccessKeyId, - bucketSecretAccessKey, - `"${workerTagEgress}" in "/tags/type"`, - ); - const storageBuckets = JSON.parse( - execSync('boundary storage-buckets list --recursive -format json'), - ); - storageBucket = storageBuckets.items.filter( - (obj) => obj.name == storageBucketName, - )[0]; - - // Create target - await page.getByRole('link', { name: 'Orgs', exact: true }).click(); - await expect(page.getByRole('heading', { name: 'Orgs' })).toBeVisible(); - await page.getByRole('link', { name: orgName }).click(); - await page.getByRole('link', { name: projectName }).click(); - const targetsPage = new TargetsPage(page); - const targetName = await targetsPage.createSshTargetWithAddressEnt( - targetAddress, - targetPort, - ); - await targetsPage.addIngressWorkerFilterToTarget( - `"${workerTagEgress}" in "/tags/type"`, - ); - await targetsPage.addEgressWorkerFilterToTarget( - `"${workerTagEgress}" in "/tags/type"`, - ); - - const credentialStoresPage = new CredentialStoresPage(page); - await credentialStoresPage.createStaticCredentialStore(); - const credentialName = - await credentialStoresPage.createStaticCredentialKeyPair( - sshUser, - sshKeyPath, + let orgId; + let policyName; + let storageBucket; + let connect; + try { + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, ); - await targetsPage.addInjectedCredentialsToTarget( - targetName, - credentialName, - ); - await page.getByRole('link', { name: targetName }).click(); - await targetsPage.enableSessionRecording(storageBucketName); - // Create storage policy in org scope: keep session recordings forever - await page.getByRole('link', { name: 'Orgs', exact: true }).click(); - await expect(page.getByRole('heading', { name: 'Orgs' })).toBeVisible(); - await page.getByRole('link', { name: orgName }).click(); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText(orgName), - ).toBeVisible(); - const storagePoliciesPage = new StoragePoliciesPage(page); - policyName = await storagePoliciesPage.createStoragePolicy(); - await orgsPage.attachStoragePolicy(policyName); + // Create org + const orgsPage = new OrgsPage(page); + const orgName = await orgsPage.createOrg(); - // Establish connection to target and cancel it - orgId = await boundaryCli.getOrgIdFromName(orgName); - const projectId = await boundaryCli.getProjectIdFromName( - orgId, - projectName, - ); - const targetId = await boundaryCli.getTargetIdFromName( - projectId, - targetName, - ); - connect = await boundaryCli.connectSshToTarget(targetId); - await page.getByRole('link', { name: 'Projects', exact: true }).click(); - await page.getByRole('link', { name: projectName }).click(); - const sessionsPage = new SessionsPage(page); - await sessionsPage.waitForSessionToBeVisible(targetName); - await page - .getByRole('cell', { name: targetName }) - .locator('..') - .getByRole('button', { name: 'Cancel' }) - .click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss', exact: true }).click(); - await boundaryCli.waitForSessionRecording(storageBucket.id); + // Create project + const projectsPage = new ProjectsPage(page); + const projectName = await projectsPage.createProject(); - // Play back session recording - await page.getByRole('link', { name: 'Orgs', exact: true }).click(); - await page - .getByRole('navigation', { name: 'General' }) - .getByRole('link', { name: 'Session Recordings', exact: true }) - .click(); - await page - .getByRole('row', { name: targetName }) - .getByRole('link', { name: 'View' }) - .click(); - await page - .getByRole('cell', { name: 'Channel 1' }) - .locator('..') - .getByRole('link', { name: 'Play' }) - .click(); - await page.locator('div.session-recording-player').hover(); - await page.locator('.ap-playback-button').click(); + // Create storage bucket + await page.getByRole('link', { name: 'Orgs', exact: true }).click(); + const storageBucketsPage = new StorageBucketsPage(page); + const storageBucketName = + await storageBucketsPage.createStorageBucketMinio( + orgName, + bucketEndpointUrl, + bucketName, + region, + bucketAccessKeyId, + bucketSecretAccessKey, + `"${workerTagEgress}" in "/tags/type"`, + ); + const storageBuckets = JSON.parse( + execSync('boundary storage-buckets list --recursive -format json'), + ); + storageBucket = storageBuckets.items.filter( + (obj) => obj.name == storageBucketName, + )[0]; - // Try to delete session recording: expect failure - await page.getByRole('link', { name: 'Orgs', exact: true }).click(); - await page - .getByRole('navigation', { name: 'General' }) - .getByRole('link', { name: 'Session Recordings', exact: true }) - .click(); - await page - .getByRole('row', { name: targetName }) - .getByRole('link', { name: 'View' }) - .click(); - await page.getByText('Manage').click(); - await page.getByRole('button', { name: 'Delete recording' }).click(); - await page.getByRole('button', { name: 'OK', exact: true }).click(); - await expect( - page.getByRole('alert').getByText('Error', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); + // Create target + await page.getByRole('link', { name: 'Orgs', exact: true }).click(); + await expect(page.getByRole('heading', { name: 'Orgs' })).toBeVisible(); + await page.getByRole('link', { name: orgName }).click(); + await page.getByRole('link', { name: projectName }).click(); + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createSshTargetWithAddressEnt( + targetAddress, + targetPort, + ); + await targetsPage.addIngressWorkerFilterToTarget( + `"${workerTagEgress}" in "/tags/type"`, + ); + await targetsPage.addEgressWorkerFilterToTarget( + `"${workerTagEgress}" in "/tags/type"`, + ); - // Edit storage policy: do not protect from deletion - await page.getByRole('link', { name: 'Orgs', exact: true }).click(); - await expect(page.getByRole('heading', { name: 'Orgs' })).toBeVisible(); - await page.getByRole('link', { name: orgName }).click(); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText(orgName), - ).toBeVisible(); - await page - .getByRole('link', { name: 'Storage Policies', exact: true }) - .click(); - await page.getByRole('link', { name: policyName }).click(); - await page.getByRole('button', { name: 'Edit form' }).click(); - await page - .getByLabel('Retention Policy') - .selectOption({ label: 'Do not protect, allow deletion any time' }); - await page.getByLabel('Deletion Policy').selectOption({ label: 'Custom' }); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); + const credentialStoresPage = new CredentialStoresPage(page); + await credentialStoresPage.createStaticCredentialStore(); + const credentialName = + await credentialStoresPage.createStaticCredentialKeyPair( + sshUser, + sshKeyPath, + ); + await targetsPage.addInjectedCredentialsToTarget( + targetName, + credentialName, + ); + await page.getByRole('link', { name: targetName }).click(); + await targetsPage.enableSessionRecording(storageBucketName); - // BUG? After the error trying to delete the session recording, subsequent - // actions result in an error. Reloading the page fixes the issue. This is a - // temporary workaround. - await page.reload(); + // Create storage policy in org scope: keep session recordings forever + await page.getByRole('link', { name: 'Orgs', exact: true }).click(); + await expect(page.getByRole('heading', { name: 'Orgs' })).toBeVisible(); + await page.getByRole('link', { name: orgName }).click(); + await expect( + page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(orgName), + ).toBeVisible(); + const storagePoliciesPage = new StoragePoliciesPage(page); + policyName = await storagePoliciesPage.createStoragePolicy(); + await orgsPage.attachStoragePolicy(policyName); - // Re-apply storage policy to the session recording and delete - await page.getByRole('link', { name: 'Orgs', exact: true }).click(); - await page - .getByRole('link', { name: 'Session Recordings', exact: true }) - .click(); - await page - .getByRole('row', { name: targetName }) - .getByRole('link', { name: 'View' }) - .click(); - const sessionRecordingsPage = new SessionRecordingsPage(page); - await sessionRecordingsPage.reapplyStoragePolicy(); - await sessionRecordingsPage.deleteResource(); - - // Detach storage bucket from target - await page.getByRole('link', { name: 'Orgs', exact: true }).click(); - await expect(page.getByRole('heading', { name: 'Orgs' })).toBeVisible(); - await page.getByRole('link', { name: orgName }).click(); - await page.getByRole('link', { name: projectName }).click(); - await page.getByRole('link', { name: 'Targets', exact: true }).click(); - await page.getByRole('link', { name: targetName }).click(); - await targetsPage.detachStorageBucket(); - } finally { - // End `boundary connect` process - if (connect) { - connect.kill('SIGTERM'); - } - - if (policyName) { - const storagePolicyId = await boundaryCli.getPolicyIdFromName( + // Establish connection to target and cancel it + orgId = await boundaryCli.getOrgIdFromName(orgName); + const projectId = await boundaryCli.getProjectIdFromName( orgId, - policyName, + projectName, ); - await boundaryCli.deletePolicy(storagePolicyId); + const targetId = await boundaryCli.getTargetIdFromName( + projectId, + targetName, + ); + connect = await boundaryCli.connectSshToTarget(targetId); + await page.getByRole('link', { name: 'Projects', exact: true }).click(); + await page.getByRole('link', { name: projectName }).click(); + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); + await page + .getByRole('cell', { name: targetName }) + .locator('..') + .getByRole('button', { name: 'Cancel' }) + .click(); + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss', exact: true }).click(); + await boundaryCli.waitForSessionRecording(storageBucket.id); + + // Play back session recording + await page.getByRole('link', { name: 'Orgs', exact: true }).click(); + await page + .getByRole('navigation', { name: 'General' }) + .getByRole('link', { name: 'Session Recordings', exact: true }) + .click(); + await page + .getByRole('row', { name: targetName }) + .getByRole('link', { name: 'View' }) + .click(); + await page + .getByRole('cell', { name: 'Channel 1' }) + .locator('..') + .getByRole('link', { name: 'Play' }) + .click(); + await page.locator('div.session-recording-player').hover(); + await page.locator('.ap-playback-button').click(); + + // Try to delete session recording: expect failure + await page.getByRole('link', { name: 'Orgs', exact: true }).click(); + await page + .getByRole('navigation', { name: 'General' }) + .getByRole('link', { name: 'Session Recordings', exact: true }) + .click(); + await page + .getByRole('row', { name: targetName }) + .getByRole('link', { name: 'View' }) + .click(); + await page.getByText('Manage').click(); + await page.getByRole('button', { name: 'Delete recording' }).click(); + await page.getByRole('button', { name: 'OK', exact: true }).click(); + await expect( + page.getByRole('alert').getByText('Error', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); + + // Edit storage policy: do not protect from deletion + await page.getByRole('link', { name: 'Orgs', exact: true }).click(); + await expect(page.getByRole('heading', { name: 'Orgs' })).toBeVisible(); + await page.getByRole('link', { name: orgName }).click(); + await expect( + page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(orgName), + ).toBeVisible(); + await page + .getByRole('link', { name: 'Storage Policies', exact: true }) + .click(); + await page.getByRole('link', { name: policyName }).click(); + await page.getByRole('button', { name: 'Edit form' }).click(); + await page + .getByLabel('Retention Policy') + .selectOption({ label: 'Do not protect, allow deletion any time' }); + await page + .getByLabel('Deletion Policy') + .selectOption({ label: 'Custom' }); + await page.getByRole('button', { name: 'Save' }).click(); + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); + + // BUG? After the error trying to delete the session recording, subsequent + // actions result in an error. Reloading the page fixes the issue. This is a + // temporary workaround. + await page.reload(); + + // Re-apply storage policy to the session recording and delete + await page.getByRole('link', { name: 'Orgs', exact: true }).click(); + await page + .getByRole('link', { name: 'Session Recordings', exact: true }) + .click(); + await page + .getByRole('row', { name: targetName }) + .getByRole('link', { name: 'View' }) + .click(); + const sessionRecordingsPage = new SessionRecordingsPage(page); + await sessionRecordingsPage.reapplyStoragePolicy(); + await sessionRecordingsPage.deleteResource(); + + // Detach storage bucket from target + await page.getByRole('link', { name: 'Orgs', exact: true }).click(); + await expect(page.getByRole('heading', { name: 'Orgs' })).toBeVisible(); + await page.getByRole('link', { name: orgName }).click(); + await page.getByRole('link', { name: projectName }).click(); + await page.getByRole('link', { name: 'Targets', exact: true }).click(); + await page.getByRole('link', { name: targetName }).click(); + await targetsPage.detachStorageBucket(); + } finally { + // End `boundary connect` process + if (connect) { + connect.kill('SIGTERM'); + } + + if (policyName) { + const storagePolicyId = await boundaryCli.getPolicyIdFromName( + orgId, + policyName, + ); + await boundaryCli.deletePolicy(storagePolicyId); + } + if (storageBucket) { + await boundaryCli.deleteStorageBucket(storageBucket.id); + } + if (orgId) { + await boundaryCli.deleteScope(orgId); + } } - if (storageBucket) { - await boundaryCli.deleteStorageBucket(storageBucket.id); - } - if (orgId) { - await boundaryCli.deleteScope(orgId); - } - } -}); + }, +); diff --git a/e2e-tests/admin/tests/ssh-certificate-injection-vault-ent.spec.js b/e2e-tests/admin/tests/ssh-certificate-injection-vault-ent.spec.js index 8eb1d8101..947e4ab02 100644 --- a/e2e-tests/admin/tests/ssh-certificate-injection-vault-ent.spec.js +++ b/e2e-tests/admin/tests/ssh-certificate-injection-vault-ent.spec.js @@ -32,118 +32,122 @@ test.afterEach(async () => { execSync(`vault policy delete ${boundaryPolicyName}`); }); -test('SSH Certificate Injection @ent @docker', async ({ - page, - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - sshUser, - sshCaKey, - sshCaKeyPublic, - targetAddress, - targetPort, - vaultAddr, -}) => { - await page.goto('/'); - let orgId; - let connect; - try { - // Set up vault - execSync( - `vault policy write ${boundaryPolicyName} ./admin/tests/fixtures/boundary-controller-policy.hcl`, - ); - execSync(`vault secrets enable -path=${secretsPath} ssh`); - execSync( - `vault policy write ${sshPolicyName} ./admin/tests/fixtures/ssh-policy.hcl`, - ); - execSync( - `vault write ${secretsPath}/roles/${secretName} @./admin/tests/fixtures/ssh-certificate-injection-role.json`, - ); - - const private_key = Buffer.from(sshCaKey, 'base64'); - const public_key = Buffer.from(sshCaKeyPublic, 'base64'); - - execSync( - `vault write ${secretsPath}/config/ca` + - ` private_key="${private_key}"` + - ` public_key="${public_key}"` + - ` generate_signing_key=false`, - ` format=json`, - ); - - const vaultToken = JSON.parse( +test( + 'SSH Certificate Injection', + { tag: ['@ent', '@docker'] }, + async ({ + page, + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + sshUser, + sshCaKey, + sshCaKeyPublic, + targetAddress, + targetPort, + vaultAddr, + }) => { + await page.goto('/'); + let orgId; + let connect; + try { + // Set up vault execSync( - `vault token create` + - ` -no-default-policy=true` + - ` -policy=${boundaryPolicyName}` + - ` -policy=${sshPolicyName}` + - ` -orphan=true` + - ` -period=20m` + - ` -renewable=true` + - ` -format=json`, - ), - ); - const clientToken = vaultToken.auth.client_token; - - // Create org - const orgsPage = new OrgsPage(page); - const orgName = await orgsPage.createOrg(); - - // Create project - const projectsPage = new ProjectsPage(page); - const projectName = await projectsPage.createProject(); - - // Create target - const targetsPage = new TargetsPage(page); - const targetName = await targetsPage.createSshTargetWithAddressEnt( - targetAddress, - targetPort, - ); - - // Create credentials - const credentialStoresPage = new CredentialStoresPage(page); - await credentialStoresPage.createVaultCredentialStore( - vaultAddr, - clientToken, - ); - const credentialLibraryName = - await credentialStoresPage.createVaultSshCertificateCredentialLibraryEnt( - `${secretsPath}/issue/${secretName}`, - sshUser, + `vault policy write ${boundaryPolicyName} ./admin/tests/fixtures/boundary-controller-policy.hcl`, + ); + execSync(`vault secrets enable -path=${secretsPath} ssh`); + execSync( + `vault policy write ${sshPolicyName} ./admin/tests/fixtures/ssh-policy.hcl`, + ); + execSync( + `vault write ${secretsPath}/roles/${secretName} @./admin/tests/fixtures/ssh-certificate-injection-role.json`, ); - await targetsPage.addInjectedCredentialsToTarget( - targetName, - credentialLibraryName, - ); - // Connect to target - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - orgId = await boundaryCli.getOrgIdFromName(orgName); - const projectId = await boundaryCli.getProjectIdFromName( - orgId, - projectName, - ); - const targetId = await boundaryCli.getTargetIdFromName( - projectId, - targetName, - ); - connect = await boundaryCli.connectSshToTarget(targetId); - const sessionsPage = new SessionsPage(page); - await sessionsPage.waitForSessionToBeVisible(targetName); - } finally { - // End `boundary connect` process - if (connect) { - connect.kill('SIGTERM'); - } + const private_key = Buffer.from(sshCaKey, 'base64'); + const public_key = Buffer.from(sshCaKeyPublic, 'base64'); - if (orgId) { - await boundaryCli.deleteScope(orgId); + execSync( + `vault write ${secretsPath}/config/ca` + + ` private_key="${private_key}"` + + ` public_key="${public_key}"` + + ` generate_signing_key=false`, + ` format=json`, + ); + + const vaultToken = JSON.parse( + execSync( + `vault token create` + + ` -no-default-policy=true` + + ` -policy=${boundaryPolicyName}` + + ` -policy=${sshPolicyName}` + + ` -orphan=true` + + ` -period=20m` + + ` -renewable=true` + + ` -format=json`, + ), + ); + const clientToken = vaultToken.auth.client_token; + + // Create org + const orgsPage = new OrgsPage(page); + const orgName = await orgsPage.createOrg(); + + // Create project + const projectsPage = new ProjectsPage(page); + const projectName = await projectsPage.createProject(); + + // Create target + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createSshTargetWithAddressEnt( + targetAddress, + targetPort, + ); + + // Create credentials + const credentialStoresPage = new CredentialStoresPage(page); + await credentialStoresPage.createVaultCredentialStore( + vaultAddr, + clientToken, + ); + const credentialLibraryName = + await credentialStoresPage.createVaultSshCertificateCredentialLibraryEnt( + `${secretsPath}/issue/${secretName}`, + sshUser, + ); + await targetsPage.addInjectedCredentialsToTarget( + targetName, + credentialLibraryName, + ); + + // Connect to target + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + orgId = await boundaryCli.getOrgIdFromName(orgName); + const projectId = await boundaryCli.getProjectIdFromName( + orgId, + projectName, + ); + const targetId = await boundaryCli.getTargetIdFromName( + projectId, + targetName, + ); + connect = await boundaryCli.connectSshToTarget(targetId); + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); + } finally { + // End `boundary connect` process + if (connect) { + connect.kill('SIGTERM'); + } + + if (orgId) { + await boundaryCli.deleteScope(orgId); + } } - } -}); + }, +); diff --git a/e2e-tests/admin/tests/ssh-credential-injection-vault-ent.spec.js b/e2e-tests/admin/tests/ssh-credential-injection-vault-ent.spec.js index a66d86e56..07dd4590c 100644 --- a/e2e-tests/admin/tests/ssh-credential-injection-vault-ent.spec.js +++ b/e2e-tests/admin/tests/ssh-credential-injection-vault-ent.spec.js @@ -34,106 +34,110 @@ test.afterEach(() => { execSync(`vault secrets disable ${secretsPath}`); }); -test('SSH Credential Injection (Vault User & Key Pair) @ent @docker', async ({ - page, - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - sshUser, - sshKeyPath, - targetAddress, - targetPort, - vaultAddr, -}) => { - let orgId; - let connect; - try { - // Set up vault - execSync( - `vault policy write ${boundaryPolicyName} ./admin/tests/fixtures/boundary-controller-policy.hcl`, - ); - execSync(`vault secrets enable -path=${secretsPath} kv-v2`); - execSync( - `vault kv put -mount ${secretsPath} ${secretName} ` + - ` username=${sshUser}` + - ` private_key=@${sshKeyPath}`, - ); - execSync( - `vault policy write ${secretPolicyName} ./admin/tests/fixtures/kv-policy.hcl`, - ); - const vaultToken = JSON.parse( +test( + 'SSH Credential Injection (Vault User & Key Pair)', + { tag: ['@ent', '@docker'] }, + async ({ + page, + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + sshUser, + sshKeyPath, + targetAddress, + targetPort, + vaultAddr, + }) => { + let orgId; + let connect; + try { + // Set up vault execSync( - `vault token create` + - ` -no-default-policy=true` + - ` -policy=${boundaryPolicyName}` + - ` -policy=${secretPolicyName}` + - ` -orphan=true` + - ` -period=20m` + - ` -renewable=true` + - ` -format=json`, - ), - ); - const clientToken = vaultToken.auth.client_token; - - // Create org - const orgsPage = new OrgsPage(page); - const orgName = await orgsPage.createOrg(); - - // Create project - const projectsPage = new ProjectsPage(page); - const projectName = await projectsPage.createProject(); - - // Create target - const targetsPage = new TargetsPage(page); - const targetName = await targetsPage.createSshTargetWithAddressEnt( - targetAddress, - targetPort, - ); - - // Create credentials - const credentialStoresPage = new CredentialStoresPage(page); - await credentialStoresPage.createVaultCredentialStore( - vaultAddr, - clientToken, - ); - const credentialLibraryName = - await credentialStoresPage.createVaultGenericCredentialLibraryEnt( - `${secretsPath}/data/${secretName}`, - 'SSH Private Key', + `vault policy write ${boundaryPolicyName} ./admin/tests/fixtures/boundary-controller-policy.hcl`, ); - await targetsPage.addInjectedCredentialsToTarget( - targetName, - credentialLibraryName, - ); + execSync(`vault secrets enable -path=${secretsPath} kv-v2`); + execSync( + `vault kv put -mount ${secretsPath} ${secretName} ` + + ` username=${sshUser}` + + ` private_key=@${sshKeyPath}`, + ); + execSync( + `vault policy write ${secretPolicyName} ./admin/tests/fixtures/kv-policy.hcl`, + ); + const vaultToken = JSON.parse( + execSync( + `vault token create` + + ` -no-default-policy=true` + + ` -policy=${boundaryPolicyName}` + + ` -policy=${secretPolicyName}` + + ` -orphan=true` + + ` -period=20m` + + ` -renewable=true` + + ` -format=json`, + ), + ); + const clientToken = vaultToken.auth.client_token; - // Connect to target - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - orgId = await boundaryCli.getOrgIdFromName(orgName); - const projectId = await boundaryCli.getProjectIdFromName( - orgId, - projectName, - ); - const targetId = await boundaryCli.getTargetIdFromName( - projectId, - targetName, - ); - connect = await boundaryCli.connectSshToTarget(targetId); - const sessionsPage = new SessionsPage(page); - await sessionsPage.waitForSessionToBeVisible(targetName); - } finally { - // End `boundary connect` process - if (connect) { - connect.kill('SIGTERM'); - } + // Create org + const orgsPage = new OrgsPage(page); + const orgName = await orgsPage.createOrg(); - if (orgId) { - await boundaryCli.deleteScope(orgId); + // Create project + const projectsPage = new ProjectsPage(page); + const projectName = await projectsPage.createProject(); + + // Create target + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createSshTargetWithAddressEnt( + targetAddress, + targetPort, + ); + + // Create credentials + const credentialStoresPage = new CredentialStoresPage(page); + await credentialStoresPage.createVaultCredentialStore( + vaultAddr, + clientToken, + ); + const credentialLibraryName = + await credentialStoresPage.createVaultGenericCredentialLibraryEnt( + `${secretsPath}/data/${secretName}`, + 'SSH Private Key', + ); + await targetsPage.addInjectedCredentialsToTarget( + targetName, + credentialLibraryName, + ); + + // Connect to target + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + orgId = await boundaryCli.getOrgIdFromName(orgName); + const projectId = await boundaryCli.getProjectIdFromName( + orgId, + projectName, + ); + const targetId = await boundaryCli.getTargetIdFromName( + projectId, + targetName, + ); + connect = await boundaryCli.connectSshToTarget(targetId); + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); + } finally { + // End `boundary connect` process + if (connect) { + connect.kill('SIGTERM'); + } + + if (orgId) { + await boundaryCli.deleteScope(orgId); + } } - } -}); + }, +); diff --git a/e2e-tests/admin/tests/target-ent.spec.js b/e2e-tests/admin/tests/target-ent.spec.js index d9e6f177f..5b8f41b6a 100644 --- a/e2e-tests/admin/tests/target-ent.spec.js +++ b/e2e-tests/admin/tests/target-ent.spec.js @@ -18,233 +18,249 @@ test.beforeAll(async () => { await boundaryCli.checkBoundaryCli(); }); -test('Verify session created for TCP target @ent @aws @docker', async ({ - page, - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - sshUser, - sshKeyPath, - targetAddress, - targetPort, -}) => { - await page.goto('/'); - let orgId; - let connect; - try { - const orgsPage = new OrgsPage(page); - const orgName = await orgsPage.createOrg(); - const projectsPage = new ProjectsPage(page); - const projectName = await projectsPage.createProject(); - const targetsPage = new TargetsPage(page); - const targetName = await targetsPage.createTcpTargetWithAddressEnt( - targetAddress, - targetPort, - ); +test( + 'Verify session created for TCP target', + { tag: ['@ent', '@aws', '@docker'] }, + async ({ + page, + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + sshUser, + sshKeyPath, + targetAddress, + targetPort, + }) => { + await page.goto('/'); + let orgId; + let connect; + try { + const orgsPage = new OrgsPage(page); + const orgName = await orgsPage.createOrg(); + const projectsPage = new ProjectsPage(page); + const projectName = await projectsPage.createProject(); + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createTcpTargetWithAddressEnt( + targetAddress, + targetPort, + ); - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - orgId = await boundaryCli.getOrgIdFromName(orgName); - const projectId = await boundaryCli.getProjectIdFromName( - orgId, - projectName, - ); - const targetId = await boundaryCli.getTargetIdFromName( - projectId, - targetName, - ); - connect = await boundaryCli.connectToTarget(targetId, sshUser, sshKeyPath); - const sessionsPage = new SessionsPage(page); - await sessionsPage.waitForSessionToBeVisible(targetName); - await page - .getByRole('cell', { name: targetName }) - .locator('..') - .getByRole('button', { name: 'Cancel' }) - .click(); - } finally { - if (orgId) { - await boundaryCli.deleteScope(orgId); - } - // End `boundary connect` process - if (connect) { - connect.kill('SIGTERM'); - } - } -}); - -test('Verify session created for SSH target @ent @aws @docker', async ({ - page, - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - sshUser, - sshKeyPath, - targetAddress, - targetPort, -}) => { - await page.goto('/'); - let orgId; - let connect; - try { - const orgsPage = new OrgsPage(page); - const orgName = await orgsPage.createOrg(); - const projectsPage = new ProjectsPage(page); - const projectName = await projectsPage.createProject(); - const targetsPage = new TargetsPage(page); - const targetName = await targetsPage.createSshTargetWithAddressEnt( - targetAddress, - targetPort, - ); - const credentialStoresPage = new CredentialStoresPage(page); - await credentialStoresPage.createStaticCredentialStore(); - const credentialName = - await credentialStoresPage.createStaticCredentialKeyPair( + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + orgId = await boundaryCli.getOrgIdFromName(orgName); + const projectId = await boundaryCli.getProjectIdFromName( + orgId, + projectName, + ); + const targetId = await boundaryCli.getTargetIdFromName( + projectId, + targetName, + ); + connect = await boundaryCli.connectToTarget( + targetId, sshUser, sshKeyPath, ); - await targetsPage.addInjectedCredentialsToTarget( - targetName, - credentialName, - ); - - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - orgId = await boundaryCli.getOrgIdFromName(orgName); - const projectId = await boundaryCli.getProjectIdFromName( - orgId, - projectName, - ); - const targetId = await boundaryCli.getTargetIdFromName( - projectId, - targetName, - ); - connect = await boundaryCli.connectSshToTarget(targetId); - const sessionsPage = new SessionsPage(page); - await sessionsPage.waitForSessionToBeVisible(targetName); - await page - .getByRole('cell', { name: targetName }) - .locator('..') - .getByRole('button', { name: 'Cancel' }) - .click(); - } finally { - if (orgId) { - await boundaryCli.deleteScope(orgId); + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); + await page + .getByRole('cell', { name: targetName }) + .locator('..') + .getByRole('button', { name: 'Cancel' }) + .click(); + } finally { + if (orgId) { + await boundaryCli.deleteScope(orgId); + } + // End `boundary connect` process + if (connect) { + connect.kill('SIGTERM'); + } } - // End `boundary connect` process - if (connect) { - connect.kill('SIGTERM'); - } - } -}); + }, +); -test('SSH target with host sources @ent @aws @docker', async ({ - page, - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - sshUser, - sshKeyPath, - targetAddress, - targetPort, -}) => { - await page.goto('/'); - let orgId; - let connect; - try { - const orgsPage = new OrgsPage(page); - const orgName = await orgsPage.createOrg(); - const projectsPage = new ProjectsPage(page); - const projectName = await projectsPage.createProject(); - - // Create host set - const hostCatalogsPage = new HostCatalogsPage(page); - const hostCatalogName = await hostCatalogsPage.createHostCatalog(); - const hostSetName = await hostCatalogsPage.createHostSet(); - await hostCatalogsPage.createHostInHostSet(targetAddress); - - // Create another host set - await page - .getByRole('navigation', { name: 'Resources' }) - .getByRole('link', { name: 'Host Catalogs' }) - .click(); - await page.getByRole('link', { name: hostCatalogName }).click(); - const hostSetName2 = await hostCatalogsPage.createHostSet(); - - // Create target - const targetsPage = new TargetsPage(page); - const targetName = await targetsPage.createSshTargetEnt(targetPort); - await targetsPage.addHostSourceToTarget(hostSetName); - - // Add/Remove another host source - await targetsPage.addHostSourceToTarget(hostSetName2); - await page - .getByRole('link', { name: hostSetName2 }) - .locator('..') - .locator('..') - .getByRole('button', { name: 'Manage' }) - .click(); - await page.getByRole('button', { name: 'Remove' }).click(); - await page.getByRole('button', { name: 'OK', exact: true }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - - // Create credentials and attach to target - const credentialStoresPage = new CredentialStoresPage(page); - await credentialStoresPage.createStaticCredentialStore(); - const credentialName = - await credentialStoresPage.createStaticCredentialKeyPair( - sshUser, - sshKeyPath, +test( + 'Verify session created for SSH target', + { tag: ['@ent', '@aws', '@docker'] }, + async ({ + page, + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + sshUser, + sshKeyPath, + targetAddress, + targetPort, + }) => { + await page.goto('/'); + let orgId; + let connect; + try { + const orgsPage = new OrgsPage(page); + const orgName = await orgsPage.createOrg(); + const projectsPage = new ProjectsPage(page); + const projectName = await projectsPage.createProject(); + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createSshTargetWithAddressEnt( + targetAddress, + targetPort, + ); + const credentialStoresPage = new CredentialStoresPage(page); + await credentialStoresPage.createStaticCredentialStore(); + const credentialName = + await credentialStoresPage.createStaticCredentialKeyPair( + sshUser, + sshKeyPath, + ); + await targetsPage.addInjectedCredentialsToTarget( + targetName, + credentialName, ); - await targetsPage.addInjectedCredentialsToTarget( - targetName, - credentialName, - ); - // Connect to target - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - orgId = await boundaryCli.getOrgIdFromName(orgName); - const projectId = await boundaryCli.getProjectIdFromName( - orgId, - projectName, - ); - const targetId = await boundaryCli.getTargetIdFromName( - projectId, - targetName, - ); - connect = await boundaryCli.connectSshToTarget(targetId); - const sessionsPage = new SessionsPage(page); - await sessionsPage.waitForSessionToBeVisible(targetName); - await page - .getByRole('cell', { name: targetName }) - .locator('..') - .getByRole('button', { name: 'Cancel' }) - .click(); - } finally { - if (orgId) { - await boundaryCli.deleteScope(orgId); + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + orgId = await boundaryCli.getOrgIdFromName(orgName); + const projectId = await boundaryCli.getProjectIdFromName( + orgId, + projectName, + ); + const targetId = await boundaryCli.getTargetIdFromName( + projectId, + targetName, + ); + connect = await boundaryCli.connectSshToTarget(targetId); + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); + await page + .getByRole('cell', { name: targetName }) + .locator('..') + .getByRole('button', { name: 'Cancel' }) + .click(); + } finally { + if (orgId) { + await boundaryCli.deleteScope(orgId); + } + // End `boundary connect` process + if (connect) { + connect.kill('SIGTERM'); + } } - // End `boundary connect` process - if (connect) { - connect.kill('SIGTERM'); + }, +); + +test( + 'SSH target with host sources', + { tag: ['@ent', '@aws', '@docker'] }, + async ({ + page, + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + sshUser, + sshKeyPath, + targetAddress, + targetPort, + }) => { + await page.goto('/'); + let orgId; + let connect; + try { + const orgsPage = new OrgsPage(page); + const orgName = await orgsPage.createOrg(); + const projectsPage = new ProjectsPage(page); + const projectName = await projectsPage.createProject(); + + // Create host set + const hostCatalogsPage = new HostCatalogsPage(page); + const hostCatalogName = await hostCatalogsPage.createHostCatalog(); + const hostSetName = await hostCatalogsPage.createHostSet(); + await hostCatalogsPage.createHostInHostSet(targetAddress); + + // Create another host set + await page + .getByRole('navigation', { name: 'Resources' }) + .getByRole('link', { name: 'Host Catalogs' }) + .click(); + await page.getByRole('link', { name: hostCatalogName }).click(); + const hostSetName2 = await hostCatalogsPage.createHostSet(); + + // Create target + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createSshTargetEnt(targetPort); + await targetsPage.addHostSourceToTarget(hostSetName); + + // Add/Remove another host source + await targetsPage.addHostSourceToTarget(hostSetName2); + await page + .getByRole('link', { name: hostSetName2 }) + .locator('..') + .locator('..') + .getByRole('button', { name: 'Manage' }) + .click(); + await page.getByRole('button', { name: 'Remove' }).click(); + await page.getByRole('button', { name: 'OK', exact: true }).click(); + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); + + // Create credentials and attach to target + const credentialStoresPage = new CredentialStoresPage(page); + await credentialStoresPage.createStaticCredentialStore(); + const credentialName = + await credentialStoresPage.createStaticCredentialKeyPair( + sshUser, + sshKeyPath, + ); + await targetsPage.addInjectedCredentialsToTarget( + targetName, + credentialName, + ); + + // Connect to target + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + orgId = await boundaryCli.getOrgIdFromName(orgName); + const projectId = await boundaryCli.getProjectIdFromName( + orgId, + projectName, + ); + const targetId = await boundaryCli.getTargetIdFromName( + projectId, + targetName, + ); + connect = await boundaryCli.connectSshToTarget(targetId); + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); + await page + .getByRole('cell', { name: targetName }) + .locator('..') + .getByRole('button', { name: 'Cancel' }) + .click(); + } finally { + if (orgId) { + await boundaryCli.deleteScope(orgId); + } + // End `boundary connect` process + if (connect) { + connect.kill('SIGTERM'); + } } - } -}); + }, +); diff --git a/e2e-tests/admin/tests/target.spec.js b/e2e-tests/admin/tests/target.spec.js index 98ac5ecc6..3de29fa04 100644 --- a/e2e-tests/admin/tests/target.spec.js +++ b/e2e-tests/admin/tests/target.spec.js @@ -18,216 +18,236 @@ test.beforeAll(async () => { await boundaryCli.checkBoundaryCli(); }); -test('Verify session created to target with host, then cancel the session @ce @aws @docker', async ({ - page, - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - sshUser, - sshKeyPath, - targetAddress, - targetPort, -}) => { - await page.goto('/'); - let orgId; - let connect; - try { - const orgsPage = new OrgsPage(page); - const orgName = await orgsPage.createOrg(); - const projectsPage = new ProjectsPage(page); - const projectName = await projectsPage.createProject(); +test( + 'Verify session created to target with host, then cancel the session', + { tag: ['@ce', '@aws', '@docker'] }, + async ({ + page, + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + sshUser, + sshKeyPath, + targetAddress, + targetPort, + }) => { + await page.goto('/'); + let orgId; + let connect; + try { + const orgsPage = new OrgsPage(page); + const orgName = await orgsPage.createOrg(); + const projectsPage = new ProjectsPage(page); + const projectName = await projectsPage.createProject(); - // Create host set - const hostCatalogsPage = new HostCatalogsPage(page); - const hostCatalogName = await hostCatalogsPage.createHostCatalog(); - const hostSetName = await hostCatalogsPage.createHostSet(); - await hostCatalogsPage.createHostInHostSet(targetAddress); + // Create host set + const hostCatalogsPage = new HostCatalogsPage(page); + const hostCatalogName = await hostCatalogsPage.createHostCatalog(); + const hostSetName = await hostCatalogsPage.createHostSet(); + await hostCatalogsPage.createHostInHostSet(targetAddress); - // Create another host set - await page - .getByRole('navigation', { name: 'Resources' }) - .getByRole('link', { name: 'Host Catalogs' }) - .click(); - await page.getByRole('link', { name: hostCatalogName }).click(); - const hostSetName2 = await hostCatalogsPage.createHostSet(); + // Create another host set + await page + .getByRole('navigation', { name: 'Resources' }) + .getByRole('link', { name: 'Host Catalogs' }) + .click(); + await page.getByRole('link', { name: hostCatalogName }).click(); + const hostSetName2 = await hostCatalogsPage.createHostSet(); - // Create target - const targetsPage = new TargetsPage(page); - const targetName = await targetsPage.createTarget(targetPort); - await targetsPage.addHostSourceToTarget(hostSetName); + // Create target + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createTarget(targetPort); + await targetsPage.addHostSourceToTarget(hostSetName); - // Add/Remove another host source - await targetsPage.addHostSourceToTarget(hostSetName2); - await targetsPage.removeHostSourceFromTarget(hostSetName2); + // Add/Remove another host source + await targetsPage.addHostSourceToTarget(hostSetName2); + await targetsPage.removeHostSourceFromTarget(hostSetName2); - // Connect to target - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - orgId = await boundaryCli.getOrgIdFromName(orgName); - const projectId = await boundaryCli.getProjectIdFromName( - orgId, - projectName, - ); - const targetId = await boundaryCli.getTargetIdFromName( - projectId, - targetName, - ); - connect = await boundaryCli.connectToTarget(targetId, sshUser, sshKeyPath); - const sessionsPage = new SessionsPage(page); - await sessionsPage.waitForSessionToBeVisible(targetName); - await page - .getByRole('cell', { name: targetName }) - .locator('..') - .getByRole('button', { name: 'Cancel' }) - .click(); - } finally { - if (orgId) { - await boundaryCli.deleteScope(orgId); + // Connect to target + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + orgId = await boundaryCli.getOrgIdFromName(orgName); + const projectId = await boundaryCli.getProjectIdFromName( + orgId, + projectName, + ); + const targetId = await boundaryCli.getTargetIdFromName( + projectId, + targetName, + ); + connect = await boundaryCli.connectToTarget( + targetId, + sshUser, + sshKeyPath, + ); + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); + await page + .getByRole('cell', { name: targetName }) + .locator('..') + .getByRole('button', { name: 'Cancel' }) + .click(); + } finally { + if (orgId) { + await boundaryCli.deleteScope(orgId); + } + // End `boundary connect` process + if (connect) { + connect.kill('SIGTERM'); + } } - // End `boundary connect` process - if (connect) { - connect.kill('SIGTERM'); + }, +); + +test( + 'Verify session created to target with address, then cancel the session', + { tag: ['@ce', '@aws', '@docker'] }, + async ({ + page, + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + sshUser, + sshKeyPath, + targetAddress, + targetPort, + }) => { + await page.goto('/'); + let orgId; + let connect; + try { + const orgsPage = new OrgsPage(page); + const orgName = await orgsPage.createOrg(); + const projectsPage = new ProjectsPage(page); + const projectName = await projectsPage.createProject(); + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createTargetWithAddress( + targetAddress, + targetPort, + ); + + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + orgId = await boundaryCli.getOrgIdFromName(orgName); + const projectId = await boundaryCli.getProjectIdFromName( + orgId, + projectName, + ); + const targetId = await boundaryCli.getTargetIdFromName( + projectId, + targetName, + ); + connect = await boundaryCli.connectToTarget( + targetId, + sshUser, + sshKeyPath, + ); + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); + await page + .getByRole('cell', { name: targetName }) + .locator('..') + .getByRole('button', { name: 'Cancel' }) + .click(); + } finally { + if (orgId) { + await boundaryCli.deleteScope(orgId); + } + // End `boundary connect` process + if (connect) { + connect.kill('SIGTERM'); + } } - } -}); + }, +); -test('Verify session created to target with address, then cancel the session @ce @aws @docker', async ({ - page, - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - sshUser, - sshKeyPath, - targetAddress, - targetPort, -}) => { - await page.goto('/'); - let orgId; - let connect; - try { - const orgsPage = new OrgsPage(page); - const orgName = await orgsPage.createOrg(); - const projectsPage = new ProjectsPage(page); - const projectName = await projectsPage.createProject(); - const targetsPage = new TargetsPage(page); - const targetName = await targetsPage.createTargetWithAddress( - targetAddress, - targetPort, - ); +test( + 'Verify TCP target is updated', + { tag: ['@ce', '@aws', '@docker'] }, + async ({ + page, + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + targetAddress, + targetPort, + }) => { + await page.goto('/'); + let orgName; + try { + const orgsPage = new OrgsPage(page); + orgName = await orgsPage.createOrg(); + const projectsPage = new ProjectsPage(page); + await projectsPage.createProject(); + const targetsPage = new TargetsPage(page); + await targetsPage.createTargetWithAddress(targetAddress, targetPort); - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - orgId = await boundaryCli.getOrgIdFromName(orgName); - const projectId = await boundaryCli.getProjectIdFromName( - orgId, - projectName, - ); - const targetId = await boundaryCli.getTargetIdFromName( - projectId, - targetName, - ); - connect = await boundaryCli.connectToTarget(targetId, sshUser, sshKeyPath); - const sessionsPage = new SessionsPage(page); - await sessionsPage.waitForSessionToBeVisible(targetName); - await page - .getByRole('cell', { name: targetName }) - .locator('..') - .getByRole('button', { name: 'Cancel' }) - .click(); - } finally { - if (orgId) { - await boundaryCli.deleteScope(orgId); + // Update target + await page.getByRole('button', { name: 'Edit Form' }).click(); + await page.getByLabel('Name').fill('New target name'); + await page.getByLabel('Description').fill('New description'); + await page.getByLabel('Target Address').fill('127.0.0.1'); + await page.getByLabel('Default Port').fill('10'); + await page.getByLabel('Default Client Port').fill('10'); + await page.getByLabel('Maximum Duration').fill('1000'); + await page.getByLabel('Maximum Connections').fill('10'); + await page.getByRole('button', { name: 'Save' }).click(); + + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); + + await targetsPage.addEgressWorkerFilterToTarget('"dev" in "/tags/type"'); + + // Edit a worker filter + await page.getByRole('link', { name: 'Workers', exact: true }).click(); + await expect( + page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText('Workers'), + ).toBeVisible(); + await page + .getByText('Egress workers') + .locator('..') + .getByRole('link', { name: 'Edit worker filter' }) + .click(); + await expect( + page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText('Edit Egress Worker Filter'), + ).toBeVisible(); + await expect(page.getByText('"dev" in "/tags/type"')).toBeVisible(); + + await page.locator('textarea').click({ force: true }); + await page.keyboard.press('Meta+A'); + await page.keyboard.press('Backspace'); + await page.locator('textarea').fill('"prod" in "/tags/type"'); + await page.getByRole('button', { name: 'Save' }).click(); + const basePage = new BasePage(page); + await basePage.dismissSuccessAlert(); + } finally { + await boundaryCli.authenticateBoundary( + baseURL, + adminAuthMethodId, + adminLoginName, + adminPassword, + ); + const orgId = await boundaryCli.getOrgIdFromName(orgName); + if (orgId) { + await boundaryCli.deleteScope(orgId); + } } - // End `boundary connect` process - if (connect) { - connect.kill('SIGTERM'); - } - } -}); - -test('Verify TCP target is updated @ce @aws @docker', async ({ - page, - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - targetAddress, - targetPort, -}) => { - await page.goto('/'); - let orgName; - try { - const orgsPage = new OrgsPage(page); - orgName = await orgsPage.createOrg(); - const projectsPage = new ProjectsPage(page); - await projectsPage.createProject(); - const targetsPage = new TargetsPage(page); - await targetsPage.createTargetWithAddress(targetAddress, targetPort); - - // Update target - await page.getByRole('button', { name: 'Edit Form' }).click(); - await page.getByLabel('Name').fill('New target name'); - await page.getByLabel('Description').fill('New description'); - await page.getByLabel('Target Address').fill('127.0.0.1'); - await page.getByLabel('Default Port').fill('10'); - await page.getByLabel('Default Client Port').fill('10'); - await page.getByLabel('Maximum Duration').fill('1000'); - await page.getByLabel('Maximum Connections').fill('10'); - await page.getByRole('button', { name: 'Save' }).click(); - - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - - await targetsPage.addEgressWorkerFilterToTarget('"dev" in "/tags/type"'); - - // Edit a worker filter - await page.getByRole('link', { name: 'Workers', exact: true }).click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText('Workers'), - ).toBeVisible(); - await page - .getByText('Egress workers') - .locator('..') - .getByRole('link', { name: 'Edit worker filter' }) - .click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText('Edit Egress Worker Filter'), - ).toBeVisible(); - await expect(page.getByText('"dev" in "/tags/type"')).toBeVisible(); - - await page.locator('textarea').click({ force: true }); - await page.keyboard.press('Meta+A'); - await page.keyboard.press('Backspace'); - await page.locator('textarea').fill('"prod" in "/tags/type"'); - await page.getByRole('button', { name: 'Save' }).click(); - const basePage = new BasePage(page); - await basePage.dismissSuccessAlert(); - } finally { - await boundaryCli.authenticateBoundary( - baseURL, - adminAuthMethodId, - adminLoginName, - adminPassword, - ); - const orgId = await boundaryCli.getOrgIdFromName(orgName); - if (orgId) { - await boundaryCli.deleteScope(orgId); - } - } -}); + }, +); diff --git a/e2e-tests/admin/tests/worker-ent.spec.js b/e2e-tests/admin/tests/worker-ent.spec.js index 9faec160b..a1d75f185 100644 --- a/e2e-tests/admin/tests/worker-ent.spec.js +++ b/e2e-tests/admin/tests/worker-ent.spec.js @@ -6,85 +6,90 @@ import { test } from '../../global-setup.js'; import { expect } from '@playwright/test'; -test('Create a worker (enterprise) @ent @docker @aws', async ({ - page, - browserName, -}) => { - test.skip(browserName === 'webkit', 'Bug in worker form on Safari'); +test( + 'Create a worker (enterprise)', + { tag: ['@ent', '@aws', '@docker'] }, + async ({ page, browserName }) => { + test.skip(browserName === 'webkit', 'Bug in worker form on Safari'); - await page.goto('/'); - await page - .getByRole('navigation', { name: 'General' }) - .getByRole('link', { name: 'Workers' }) - .click(); - await page.getByRole('link', { name: 'New', exact: true }).click(); + await page.goto('/'); + await page + .getByRole('navigation', { name: 'General' }) + .getByRole('link', { name: 'Workers' }) + .click(); + await page.getByRole('link', { name: 'New', exact: true }).click(); - // Populate config fields - await page.getByLabel('Worker public address').fill('worker1.example.com'); - await page.getByLabel('Config file path').fill('/home/ubuntu/boundary'); - await page.getByLabel('Initial upstreams').fill('10.0.0.1, 10.0.0.2'); - await page - .getByRole('group', { name: 'Worker Tags' }) - .getByLabel('Key') - .fill('type'); - await page - .getByRole('group', { name: 'Worker Tags' }) - .getByLabel('Value') - .fill('downstream, pki'); - await page - .getByRole('group', { name: 'Worker Tags' }) - .getByRole('button', { name: 'Add' }) - .click(); - await page - .getByRole('group', { name: 'Worker Tags' }) - .getByLabel('Key') - .last() - .fill('test'); - await page - .getByRole('group', { name: 'Worker Tags' }) - .getByLabel('Value') - .last() - .fill('example'); - await page - .getByRole('group', { name: 'Worker Tags' }) - .getByRole('button', { name: 'Add' }) - .click(); + // Populate config fields + await page.getByLabel('Worker public address').fill('worker1.example.com'); + await page.getByLabel('Config file path').fill('/home/ubuntu/boundary'); + await page.getByLabel('Initial upstreams').fill('10.0.0.1, 10.0.0.2'); + await page + .getByRole('group', { name: 'Worker Tags' }) + .getByLabel('Key') + .fill('type'); + await page + .getByRole('group', { name: 'Worker Tags' }) + .getByLabel('Value') + .fill('downstream, pki'); + await page + .getByRole('group', { name: 'Worker Tags' }) + .getByRole('button', { name: 'Add' }) + .click(); + await page + .getByRole('group', { name: 'Worker Tags' }) + .getByLabel('Key') + .last() + .fill('test'); + await page + .getByRole('group', { name: 'Worker Tags' }) + .getByLabel('Value') + .last() + .fill('example'); + await page + .getByRole('group', { name: 'Worker Tags' }) + .getByRole('button', { name: 'Add' }) + .click(); - await page.getByRole('switch', { name: 'Enable Recording Storage' }).click(); - await page.getByLabel('Recording Storage Path').fill('/tmp/recordings'); + await page + .getByRole('switch', { name: 'Enable Recording Storage' }) + .click(); + await page.getByLabel('Recording Storage Path').fill('/tmp/recordings'); - // Check auto-populated config - const dirLocator = '[data-test-worker-directory]'; - await expect(page.locator(dirLocator)).toContainText( - 'mkdir /home/ubuntu/boundary/ ;', - ); - await expect(page.locator(dirLocator)).toContainText( - 'touch /home/ubuntu/boundary/pki-worker.hcl', - ); + // Check auto-populated config + const dirLocator = '[data-test-worker-directory]'; + await expect(page.locator(dirLocator)).toContainText( + 'mkdir /home/ubuntu/boundary/ ;', + ); + await expect(page.locator(dirLocator)).toContainText( + 'touch /home/ubuntu/boundary/pki-worker.hcl', + ); - const configLocator = '[data-test-worker-config]'; - await expect(page.locator(configLocator)).toContainText( - 'public_addr = "worker1.example.com"', - ); - await expect(page.locator(configLocator)).toContainText( - 'auth_storage_path = "/home/ubuntu/boundary/worker1"', - ); - await expect(page.locator(configLocator)).toContainText( - 'initial_upstreams = ["10.0.0.1", "10.0.0.2"]', - ); - await expect(page.locator(configLocator)).toContainText( - 'type = ["downstream", "pki"]', - ); - await expect(page.locator(configLocator)).toContainText('test = ["example"]'); - await expect(page.locator(configLocator)).toContainText( - 'recording_storage_path = "/tmp/recordings"', - ); + const configLocator = '[data-test-worker-config]'; + await expect(page.locator(configLocator)).toContainText( + 'public_addr = "worker1.example.com"', + ); + await expect(page.locator(configLocator)).toContainText( + 'auth_storage_path = "/home/ubuntu/boundary/worker1"', + ); + await expect(page.locator(configLocator)).toContainText( + 'initial_upstreams = ["10.0.0.1", "10.0.0.2"]', + ); + await expect(page.locator(configLocator)).toContainText( + 'type = ["downstream", "pki"]', + ); + await expect(page.locator(configLocator)).toContainText( + 'test = ["example"]', + ); + await expect(page.locator(configLocator)).toContainText( + 'recording_storage_path = "/tmp/recordings"', + ); - // Try using an invalid worker token. Expect an error - await page.getByLabel('Worker Auth Registration Request').fill('test'); - await page.getByRole('button', { name: 'Register' }).click(); - await expect( - page.getByRole('alert').getByText('Error', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); -}); + // Try using an invalid worker token. Expect an error + await page.getByLabel('Worker Auth Registration Request').fill('test'); + await page.getByRole('button', { name: 'Register' }).click(); + await expect( + page.getByRole('alert').getByText('Error', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); + }, +); diff --git a/e2e-tests/admin/tests/worker-tags.spec.js b/e2e-tests/admin/tests/worker-tags.spec.js index 47ac301c2..32687a545 100644 --- a/e2e-tests/admin/tests/worker-tags.spec.js +++ b/e2e-tests/admin/tests/worker-tags.spec.js @@ -9,177 +9,185 @@ import { customAlphabet } from 'nanoid'; import { WorkersPage } from '../pages/workers.js'; -test('Worker Tags @ce @ent @aws @docker', async ({ page }) => { - const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 10); +test( + 'Worker Tags', + { tag: ['@ce', '@ent', '@aws', '@docker'] }, + async ({ page }) => { + const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 10); - await page.goto('/'); - await page - .getByRole('navigation', { name: 'General' }) - .getByRole('link', { name: 'Workers' }) - .click(); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText('Workers'), - ).toBeVisible(); + await page.goto('/'); + await page + .getByRole('navigation', { name: 'General' }) + .getByRole('link', { name: 'Workers' }) + .click(); + await expect( + page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText('Workers'), + ).toBeVisible(); - // View tags from worker list page - const workerListHeadersCount = await page - .getByRole('table') - .getByRole('columnheader') - .count(); - let workerListWorkerIndex; - let workerListTagIndex; - for (let i = 0; i < workerListHeadersCount; i++) { - const header = await page + // View tags from worker list page + const workerListHeadersCount = await page .getByRole('table') .getByRole('columnheader') - .nth(i) - .innerText(); - if (header === 'Worker') { - workerListWorkerIndex = i; - } else if (header === 'Tags') { - workerListTagIndex = i; + .count(); + let workerListWorkerIndex; + let workerListTagIndex; + for (let i = 0; i < workerListHeadersCount; i++) { + const header = await page + .getByRole('table') + .getByRole('columnheader') + .nth(i) + .innerText(); + if (header === 'Worker') { + workerListWorkerIndex = i; + } else if (header === 'Tags') { + workerListTagIndex = i; + } } - } - await page - .getByRole('table') - .getByRole('row') - .nth(1) - .getByRole('cell') - .nth(workerListTagIndex) - .getByRole('button') - .click(); - - // Check that dialog shows a config tag - const dialogHeadersCount = await page - .getByRole('dialog') - .getByRole('table') - .getByRole('columnheader') - .count(); - let dialogTypeIndex; - for (let i = 0; i < dialogHeadersCount; i++) { - const header = await page - .getByRole('dialog') - .getByRole('table') - .getByRole('columnheader') - .nth(i) - .innerText(); - if (header === 'Type') { - dialogTypeIndex = i; - } - } - await expect( - page - .getByRole('dialog') + await page .getByRole('table') .getByRole('row') .nth(1) .getByRole('cell') - .nth(dialogTypeIndex), - ).toHaveText('config'); - await page - .getByRole('dialog') - .getByRole('button', { name: 'Dismiss' }) - .click(); + .nth(workerListTagIndex) + .getByRole('button') + .click(); - // Go to worker details and create a new tag - await page - .getByRole('table') - .getByRole('row') - .nth(1) - .getByRole('cell') - .nth(workerListWorkerIndex) - .getByRole('link') - .click(); - const workersPage = new WorkersPage(page); - const tagKey = nanoid(); - const tagValue = nanoid(); - await workersPage.createNewTag(tagKey, tagValue); - - // Check tag in worker dialog - await page - .getByRole('navigation', { name: 'General' }) - .getByRole('link', { name: 'Workers' }) - .click(); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText('Workers'), - ).toBeVisible(); - await page - .getByRole('table') - .getByRole('row') - .nth(1) - .getByRole('cell') - .nth(workerListTagIndex) - .getByRole('button') - .click(); - await expect( - page + // Check that dialog shows a config tag + const dialogHeadersCount = await page .getByRole('dialog') - .getByRole('table') - .getByRole('cell', { name: tagKey + ' = ' + tagValue }), - ).toBeVisible(); - await page - .getByRole('dialog') - .getByRole('button', { name: 'Dismiss' }) - .click(); - - // Go to worker details page - await page - .getByRole('table') - .getByRole('row') - .nth(1) - .getByRole('cell') - .nth(workerListWorkerIndex) - .getByRole('link') - .click(); - await page.getByRole('link', { name: 'Tags' }).click(); - const workerDetailsHeadersCount = await page - .getByRole('table') - .getByRole('columnheader') - .count(); - let workerDetailsTypeIndex; - for (let i = 0; i < workerDetailsHeadersCount; i++) { - const header = await page .getByRole('table') .getByRole('columnheader') - .nth(i) - .innerText(); - if (header === 'Type') { - workerDetailsTypeIndex = i; + .count(); + let dialogTypeIndex; + for (let i = 0; i < dialogHeadersCount; i++) { + const header = await page + .getByRole('dialog') + .getByRole('table') + .getByRole('columnheader') + .nth(i) + .innerText(); + if (header === 'Type') { + dialogTypeIndex = i; + } } - } + await expect( + page + .getByRole('dialog') + .getByRole('table') + .getByRole('row') + .nth(1) + .getByRole('cell') + .nth(dialogTypeIndex), + ).toHaveText('config'); + await page + .getByRole('dialog') + .getByRole('button', { name: 'Dismiss' }) + .click(); - // Check tags in the worker details page - await expect( - page + // Go to worker details and create a new tag + await page .getByRole('table') .getByRole('row') .nth(1) .getByRole('cell') - .nth(workerDetailsTypeIndex), - ).toHaveText('config'); - await expect( - page - .getByRole('table') - .getByRole('cell', { name: tagKey }) - .locator('..') - .getByRole('cell', { name: tagValue }), - ).toBeVisible(); + .nth(workerListWorkerIndex) + .getByRole('link') + .click(); + const workersPage = new WorkersPage(page); + const tagKey = nanoid(); + const tagValue = nanoid(); + await workersPage.createNewTag(tagKey, tagValue); - // Edit the tag - const newTagKey = nanoid(); - const newTagValue = nanoid(); - await workersPage.editTag(tagKey, newTagKey, newTagValue); - await expect( - page + // Check tag in worker dialog + await page + .getByRole('navigation', { name: 'General' }) + .getByRole('link', { name: 'Workers' }) + .click(); + await expect( + page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText('Workers'), + ).toBeVisible(); + await page .getByRole('table') - .getByRole('cell', { name: newTagKey }) - .locator('..') - .getByRole('cell', { name: newTagValue }), - ).toBeVisible(); + .getByRole('row') + .nth(1) + .getByRole('cell') + .nth(workerListTagIndex) + .getByRole('button') + .click(); + await expect( + page + .getByRole('dialog') + .getByRole('table') + .getByRole('cell', { name: tagKey + ' = ' + tagValue }), + ).toBeVisible(); + await page + .getByRole('dialog') + .getByRole('button', { name: 'Dismiss' }) + .click(); - // Delete the tag - await workersPage.removeTag(newTagKey); - await expect( - page.getByRole('table').getByRole('cell', { name: newTagKey }), - ).toBeHidden(); -}); + // Go to worker details page + await page + .getByRole('table') + .getByRole('row') + .nth(1) + .getByRole('cell') + .nth(workerListWorkerIndex) + .getByRole('link') + .click(); + await page.getByRole('link', { name: 'Tags' }).click(); + const workerDetailsHeadersCount = await page + .getByRole('table') + .getByRole('columnheader') + .count(); + let workerDetailsTypeIndex; + for (let i = 0; i < workerDetailsHeadersCount; i++) { + const header = await page + .getByRole('table') + .getByRole('columnheader') + .nth(i) + .innerText(); + if (header === 'Type') { + workerDetailsTypeIndex = i; + } + } + + // Check tags in the worker details page + await expect( + page + .getByRole('table') + .getByRole('row') + .nth(1) + .getByRole('cell') + .nth(workerDetailsTypeIndex), + ).toHaveText('config'); + await expect( + page + .getByRole('table') + .getByRole('cell', { name: tagKey }) + .locator('..') + .getByRole('cell', { name: tagValue }), + ).toBeVisible(); + + // Edit the tag + const newTagKey = nanoid(); + const newTagValue = nanoid(); + await workersPage.editTag(tagKey, newTagKey, newTagValue); + await expect( + page + .getByRole('table') + .getByRole('cell', { name: newTagKey }) + .locator('..') + .getByRole('cell', { name: newTagValue }), + ).toBeVisible(); + + // Delete the tag + await workersPage.removeTag(newTagKey); + await expect( + page.getByRole('table').getByRole('cell', { name: newTagKey }), + ).toBeHidden(); + }, +); diff --git a/e2e-tests/admin/tests/worker.spec.js b/e2e-tests/admin/tests/worker.spec.js index 05601cf62..a45985996 100644 --- a/e2e-tests/admin/tests/worker.spec.js +++ b/e2e-tests/admin/tests/worker.spec.js @@ -6,76 +6,82 @@ import { test } from '../../global-setup.js'; import { expect } from '@playwright/test'; -test('Create a worker @ce @docker @aws', async ({ page, browserName }) => { - test.skip(browserName === 'webkit', 'Bug in worker form on Safari'); +test( + 'Create a worker', + { tag: ['@ce', '@aws', '@docker'] }, + async ({ page, browserName }) => { + test.skip(browserName === 'webkit', 'Bug in worker form on Safari'); - await page.goto('/'); - await page - .getByRole('navigation', { name: 'General' }) - .getByRole('link', { name: 'Workers' }) - .click(); - await page.getByRole('link', { name: 'New', exact: true }).click(); + await page.goto('/'); + await page + .getByRole('navigation', { name: 'General' }) + .getByRole('link', { name: 'Workers' }) + .click(); + await page.getByRole('link', { name: 'New', exact: true }).click(); - // Populate config fields - await page.getByLabel('Worker public address').fill('worker1.example.com'); - await page.getByLabel('Config file path').fill('/home/ubuntu/boundary'); - await page.getByLabel('Initial upstreams').fill('10.0.0.1, 10.0.0.2'); - await page - .getByRole('group', { name: 'Worker Tags' }) - .getByLabel('Key') - .fill('type'); - await page - .getByRole('group', { name: 'Worker Tags' }) - .getByLabel('Value') - .fill('downstream, pki'); - await page - .getByRole('group', { name: 'Worker Tags' }) - .getByRole('button', { name: 'Add' }) - .click(); - await page - .getByRole('group', { name: 'Worker Tags' }) - .getByLabel('Key') - .last() - .fill('test'); - await page - .getByRole('group', { name: 'Worker Tags' }) - .getByLabel('Value') - .last() - .fill('example'); - await page - .getByRole('group', { name: 'Worker Tags' }) - .getByRole('button', { name: 'Add' }) - .click(); + // Populate config fields + await page.getByLabel('Worker public address').fill('worker1.example.com'); + await page.getByLabel('Config file path').fill('/home/ubuntu/boundary'); + await page.getByLabel('Initial upstreams').fill('10.0.0.1, 10.0.0.2'); + await page + .getByRole('group', { name: 'Worker Tags' }) + .getByLabel('Key') + .fill('type'); + await page + .getByRole('group', { name: 'Worker Tags' }) + .getByLabel('Value') + .fill('downstream, pki'); + await page + .getByRole('group', { name: 'Worker Tags' }) + .getByRole('button', { name: 'Add' }) + .click(); + await page + .getByRole('group', { name: 'Worker Tags' }) + .getByLabel('Key') + .last() + .fill('test'); + await page + .getByRole('group', { name: 'Worker Tags' }) + .getByLabel('Value') + .last() + .fill('example'); + await page + .getByRole('group', { name: 'Worker Tags' }) + .getByRole('button', { name: 'Add' }) + .click(); - // Check auto-populated configs - const dirLocator = '[data-test-worker-directory]'; - await expect(page.locator(dirLocator)).toContainText( - 'mkdir /home/ubuntu/boundary/ ;', - ); - await expect(page.locator(dirLocator)).toContainText( - 'touch /home/ubuntu/boundary/pki-worker.hcl', - ); + // Check auto-populated configs + const dirLocator = '[data-test-worker-directory]'; + await expect(page.locator(dirLocator)).toContainText( + 'mkdir /home/ubuntu/boundary/ ;', + ); + await expect(page.locator(dirLocator)).toContainText( + 'touch /home/ubuntu/boundary/pki-worker.hcl', + ); - const configLocator = '[data-test-worker-config]'; - await expect(page.locator(configLocator)).toContainText( - 'public_addr = "worker1.example.com"', - ); - await expect(page.locator(configLocator)).toContainText( - 'auth_storage_path = "/home/ubuntu/boundary/worker1"', - ); - await expect(page.locator(configLocator)).toContainText( - 'initial_upstreams = ["10.0.0.1", "10.0.0.2"]', - ); - await expect(page.locator(configLocator)).toContainText( - 'type = ["downstream", "pki"]', - ); - await expect(page.locator(configLocator)).toContainText('test = ["example"]'); + const configLocator = '[data-test-worker-config]'; + await expect(page.locator(configLocator)).toContainText( + 'public_addr = "worker1.example.com"', + ); + await expect(page.locator(configLocator)).toContainText( + 'auth_storage_path = "/home/ubuntu/boundary/worker1"', + ); + await expect(page.locator(configLocator)).toContainText( + 'initial_upstreams = ["10.0.0.1", "10.0.0.2"]', + ); + await expect(page.locator(configLocator)).toContainText( + 'type = ["downstream", "pki"]', + ); + await expect(page.locator(configLocator)).toContainText( + 'test = ["example"]', + ); - // Try using an invalid worker token. Expect an error - await page.getByLabel('Worker Auth Registration Request').fill('test'); - await page.getByRole('button', { name: 'Register' }).click(); - await expect( - page.getByRole('alert').getByText('Error', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); -}); + // Try using an invalid worker token. Expect an error + await page.getByLabel('Worker Auth Registration Request').fill('test'); + await page.getByRole('button', { name: 'Register' }).click(); + await expect( + page.getByRole('alert').getByText('Error', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); + }, +); diff --git a/yarn.lock b/yarn.lock index 2e279ee6d..e761e9695 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3071,13 +3071,6 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.17.8": - version "7.26.7" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.7.tgz#f4e7fe527cd710f8dc0618610b61b4b060c3c341" - integrity sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ== - dependencies: - regenerator-runtime "^0.14.0" - "@babel/runtime@^7.21.0": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.5.tgz#8564dd588182ce0047d55d7a75e93921107b57ec" @@ -3742,15 +3735,6 @@ mkdirp "^1.0.4" silent-error "^1.1.1" -"@ember/render-modifiers@^2.0.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@ember/render-modifiers/-/render-modifiers-2.1.0.tgz#f4fff95a8b5cfbe947ec46644732d511711c5bf9" - integrity sha512-LruhfoDv2itpk0fA0IC76Sxjcnq/7BC6txpQo40hOko8Dn6OxwQfxkPIbZGV0Cz7df+iX+VJrcYzNIvlc3w2EQ== - dependencies: - "@embroider/macros" "^1.0.0" - ember-cli-babel "^7.26.11" - ember-modifier-manager-polyfill "^1.2.0" - "@ember/render-modifiers@^2.0.2", "@ember/render-modifiers@^2.0.4": version "2.0.4" resolved "https://registry.yarnpkg.com/@ember/render-modifiers/-/render-modifiers-2.0.4.tgz#0ac7af647cb736076dbfcd54ca71e090cd329d71" @@ -3769,6 +3753,15 @@ ember-cli-babel "^7.26.11" ember-modifier-manager-polyfill "^1.2.0" +"@ember/render-modifiers@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@ember/render-modifiers/-/render-modifiers-2.1.0.tgz#f4fff95a8b5cfbe947ec46644732d511711c5bf9" + integrity sha512-LruhfoDv2itpk0fA0IC76Sxjcnq/7BC6txpQo40hOko8Dn6OxwQfxkPIbZGV0Cz7df+iX+VJrcYzNIvlc3w2EQ== + dependencies: + "@embroider/macros" "^1.0.0" + ember-cli-babel "^7.26.11" + ember-modifier-manager-polyfill "^1.2.0" + "@ember/string@^3.0.1", "@ember/string@^3.1.1": version "3.1.1" resolved "https://registry.yarnpkg.com/@ember/string/-/string-3.1.1.tgz#0a5ac0d1e4925259e41d5c8d55ef616117d47ff0" @@ -3844,7 +3837,7 @@ common-ancestor-path "^1.0.1" semver "^7.3.8" -"@embroider/addon-shim@^1.7.1": +"@embroider/addon-shim@^1.7.1", "@embroider/addon-shim@^1.8.9": version "1.9.0" resolved "https://registry.yarnpkg.com/@embroider/addon-shim/-/addon-shim-1.9.0.tgz#f729cfaf0f2d5f3c178808a546251743335c62f4" integrity sha512-fMzayl/licUL8VRAy4qXROKcYvHwUbV8aTh4m97L5/MRuVpxbcAy92DGGTqx5OBKCSQN3gMg+sUKeE6AviefpQ== @@ -3877,7 +3870,7 @@ resolve "^1.20.0" semver "^7.3.2" -"@embroider/macros@^1.2.0": +"@embroider/macros@^1.12.3": version "1.16.10" resolved "https://registry.yarnpkg.com/@embroider/macros/-/macros-1.16.10.tgz#4f00c959409ee7327a344c250fc91c4622bf1589" integrity sha512-G0vCsKgNCX0PMmuVNsTLG7IYXz8VkekQMK4Kcllzqpwb7ivFRDwVx2bD4QSvZ9LCTd4eWQ654RsCqVbW5aviww== @@ -5545,10 +5538,10 @@ ansi-escapes@^7.0.0: dependencies: environment "^1.0.0" -ansi-html@^0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" - integrity sha512-JoAxEa1DfP9m2xfB/y2r/aKcwXNlltr4+0QSBC4TrLfcxyvepX2Pv0t/xpgGV5bGsDzCYV8SzjWgyCW0T9yYbA== +ansi-html@^0.0.7, ansi-html@^0.0.9: + version "0.0.9" + resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.9.tgz#6512d02342ae2cc68131952644a129cb734cd3f0" + integrity sha512-ozbS3LuenHVxNRh/wdnN16QapUHzauqSomAl1jwwJRRsGwFwtj644lIhxfWu0Fy0acCij2+AEgHvjscq3dlVXg== ansi-regex@^2.0.0: version "2.1.1" @@ -5682,21 +5675,6 @@ aria-query@^5.3.0: dependencies: dequal "^2.0.3" -arr-diff@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" - integrity sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA== - -arr-flatten@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" - integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== - -arr-union@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" - integrity sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q== - array-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" @@ -5717,11 +5695,6 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -array-unique@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" - integrity sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ== - arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -5745,11 +5718,6 @@ assert-never@^1.2.1: resolved "https://registry.yarnpkg.com/assert-never/-/assert-never-1.2.1.tgz#11f0e363bf146205fb08193b5c7b90f4d1cf44fe" integrity sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw== -assign-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" - integrity sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw== - ast-types@0.13.3: version "0.13.3" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.3.tgz#50da3f28d17bdbc7969a3a2d83a0e4a72ae755a7" @@ -5794,28 +5762,18 @@ async-promise-queue@^1.0.3, async-promise-queue@^1.0.5: async "^2.4.1" debug "^2.6.8" -async@^2.4.1, async@^2.6.4: +async@^2.4.1, async@^2.6.4, async@~0.2.9: version "2.6.4" resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== dependencies: lodash "^4.17.14" -async@~0.2.9: - version "0.2.10" - resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" - integrity sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ== - at-least-node@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== -atob@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" - integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== - author-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/author-regex/-/author-regex-1.0.0.tgz#d08885be6b9bbf9439fe087c76287245f0a81450" @@ -6204,19 +6162,6 @@ base64id@2.0.0, base64id@~2.0.0: resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== -base@^0.11.1: - version "0.11.2" - resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" - integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== - dependencies: - cache-base "^1.0.1" - class-utils "^0.3.5" - component-emitter "^1.2.1" - define-property "^1.0.0" - isobject "^3.0.1" - mixin-deep "^1.2.0" - pascalcase "^0.1.1" - basic-auth@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" @@ -6293,7 +6238,7 @@ body@^5.1.0: raw-body "~1.1.0" safe-json-parse "~1.0.1" -boolbase@^1.0.0, boolbase@~1.0.0: +boolbase@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= @@ -6323,22 +6268,6 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@^2.3.1: - version "2.3.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" - integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== - dependencies: - arr-flatten "^1.1.0" - array-unique "^0.3.2" - extend-shallow "^2.0.1" - fill-range "^4.0.0" - isobject "^3.0.1" - repeat-element "^1.1.2" - snapdragon "^0.8.1" - snapdragon-node "^2.0.1" - split-string "^3.0.2" - to-regex "^3.0.1" - braces@^3.0.3, braces@~3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" @@ -6982,21 +6911,6 @@ cacache@^16.1.0: tar "^6.1.11" unique-filename "^2.0.0" -cache-base@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" - integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== - dependencies: - collection-visit "^1.0.0" - component-emitter "^1.2.1" - get-value "^2.0.6" - has-value "^1.0.0" - isobject "^3.0.1" - set-value "^2.0.0" - to-object-path "^0.3.0" - union-value "^1.0.0" - unset-value "^1.0.0" - cacheable-lookup@^5.0.3: version "5.0.4" resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" @@ -7240,16 +7154,6 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== -class-utils@^0.3.5: - version "0.3.6" - resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" - integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== - dependencies: - arr-union "^3.1.0" - define-property "^0.2.5" - isobject "^3.0.0" - static-extend "^0.1.1" - cldr-core@^45.0.0: version "45.0.0" resolved "https://registry.yarnpkg.com/cldr-core/-/cldr-core-45.0.0.tgz#bbb77f479a62e3913999cce1c2c94b8834feffa9" @@ -7425,14 +7329,6 @@ collect-v8-coverage@^1.0.0: resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== -collection-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" - integrity sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw== - dependencies: - map-visit "^1.0.0" - object-visit "^1.0.0" - color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -7562,11 +7458,6 @@ compare-version@^0.1.2: resolved "https://registry.yarnpkg.com/compare-version/-/compare-version-0.1.2.tgz#0162ec2d9351f5ddd59a9202cba935366a725080" integrity sha1-AWLsLZNR9d3VmpICy6k1NmpyUIA= -component-emitter@^1.2.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.1.tgz#ef1d5796f7d93f135ee6fb684340b26403c97d17" - integrity sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ== - compressible@~2.0.16: version "2.0.18" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" @@ -7707,11 +7598,6 @@ copy-dereference@^1.0.0: resolved "https://registry.yarnpkg.com/copy-dereference/-/copy-dereference-1.0.0.tgz#6b131865420fd81b413ba994b44d3655311152b6" integrity sha1-axMYZUIP2BtBO6mUtE02VTERUrY= -copy-descriptor@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" - integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== - core-js-compat@^3.14.0, core-js-compat@^3.18.0, core-js-compat@^3.19.1, core-js-compat@^3.21.0, core-js-compat@^3.22.1, core-js-compat@^3.37.1, core-js-compat@^3.38.0: version "3.38.1" resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.38.1.tgz#2bc7a298746ca5a7bcb9c164bcb120f2ebc09a09" @@ -7974,7 +7860,7 @@ date-fns@^2.30.0: dependencies: "@babel/runtime" "^7.21.0" -debug@2.6.9, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8: +debug@2.6.9, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0, debug@^2.6.8: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -8032,11 +7918,6 @@ decamelize@^5.0.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-5.0.1.tgz#db11a92e58c741ef339fb0a2868d8a06a9a7b1e9" integrity sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA== -decode-uri-component@^0.2.0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" - integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== - decompress-response@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" @@ -8108,28 +7989,6 @@ define-properties@^1.1.3: dependencies: object-keys "^1.0.12" -define-property@^0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" - integrity sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA== - dependencies: - is-descriptor "^0.1.0" - -define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" - integrity sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA== - dependencies: - is-descriptor "^1.0.0" - -define-property@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" - integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== - dependencies: - is-descriptor "^1.0.2" - isobject "^3.0.1" - delegate@^3.1.2: version "3.2.0" resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166" @@ -9481,14 +9340,13 @@ ember-resolver@^10.0.0: dependencies: ember-cli-babel "^7.26.11" -ember-resources@^5.0.1: - version "5.6.4" - resolved "https://registry.yarnpkg.com/ember-resources/-/ember-resources-5.6.4.tgz#1ae05bb5398ab0d8fab8c0925c5bf679ee86e327" - integrity sha512-ShdosnruPm37jPpzPOgPVelymEDJT/27Jz/j5AGPVAfCaUhRIocTxNMtPx13ox890A2babuPF5M3Ur8UFidqtw== +ember-resources@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/ember-resources/-/ember-resources-7.0.3.tgz#a1e47103aee89540e22d460379bc66a742fbda54" + integrity sha512-aebWpULjdWEUnxheoOz5e6XZ8kDV8b79CerPSMcUkdzOcKS7N4HC8FuyCPG6M6MiVuYYqgsrhzSxyW8H1164MA== dependencies: - "@babel/runtime" "^7.17.8" "@embroider/addon-shim" "^1.2.0" - "@embroider/macros" "^1.2.0" + "@embroider/macros" "^1.12.3" ember-rfc176-data@^0.3.13, ember-rfc176-data@^0.3.15, ember-rfc176-data@^0.3.17: version "0.3.17" @@ -9557,16 +9415,16 @@ ember-source@~4.12.0: semver "^7.3.8" silent-error "^1.1.1" -ember-stargate@^0.4.3: - version "0.4.3" - resolved "https://registry.yarnpkg.com/ember-stargate/-/ember-stargate-0.4.3.tgz#93e92e4928d489557401d70e52b242b38f36f9ab" - integrity sha512-GeT5n+TT3Lfl335f16fx9ms0Jap+v5LTs8otIaQEGtFbSP5Jj/hlT3JPB9Uo8IDLXdjejxJsKRpCEzRD43g5dg== +ember-stargate@^0.4.3, ember-stargate@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/ember-stargate/-/ember-stargate-0.6.0.tgz#2571ec489a75ffe4eae4e92b99871e51dea7f4fb" + integrity sha512-oNMZj+eS6a82aQ8rbxlCjQwg+mK0lAIOQEylSGqBXhivi9smXPYVVnpN0vrC5SALa9C0S1EOvA2qc/59a+0w9w== dependencies: - "@ember/render-modifiers" "^2.0.0" - "@embroider/addon-shim" "^1.0.0" + "@ember/render-modifiers" "^2.1.0" + "@embroider/addon-shim" "^1.8.9" "@glimmer/component" "^1.1.2" - ember-resources "^5.0.1" - tracked-maps-and-sets "^3.0.1" + ember-resources "^7.0.1" + tracked-maps-and-sets "^3.0.2" ember-style-modifier@^4.4.0: version "4.4.0" @@ -10435,19 +10293,6 @@ exit@^0.1.2: resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= -expand-brackets@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" - integrity sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA== - dependencies: - debug "^2.3.3" - define-property "^0.2.5" - extend-shallow "^2.0.1" - posix-character-classes "^0.1.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - expand-tilde@^2.0.0, expand-tilde@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" @@ -10515,21 +10360,6 @@ ext@^1.1.2: dependencies: type "^2.0.0" -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" - integrity sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug== - dependencies: - is-extendable "^0.1.0" - -extend-shallow@^3.0.0, extend-shallow@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" - integrity sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q== - dependencies: - assign-symbols "^1.0.0" - is-extendable "^1.0.1" - extend@^3.0.0, extend@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" @@ -10544,20 +10374,6 @@ external-editor@^3.0.3, external-editor@^3.1.0: iconv-lite "^0.4.24" tmp "^0.0.33" -extglob@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" - integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== - dependencies: - array-unique "^0.3.2" - define-property "^1.0.0" - expand-brackets "^2.1.4" - extend-shallow "^2.0.1" - fragment-cache "^0.2.1" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - extract-stack@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/extract-stack/-/extract-stack-2.0.0.tgz#11367bc865bfcd9bc0db3123e5edb57786f11f9b" @@ -10760,16 +10576,6 @@ filesize@^10.0.8: resolved "https://registry.yarnpkg.com/filesize/-/filesize-10.1.6.tgz#31194da825ac58689c0bce3948f33ce83aabd361" integrity sha512-sJslQKU2uM33qH5nqewAwVB2QgR6w1aMNsYUp3aN5rMRyXEwJGmZvaWzeJFNTOXWlHQyBFCWrdj3fV/fsTOX8w== -fill-range@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" - integrity sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ== - dependencies: - extend-shallow "^2.0.1" - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range "^2.1.0" - fill-range@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" @@ -11001,11 +10807,6 @@ follow-redirects@^1.0.0: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== -for-in@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" - integrity sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ== - foreach@^2.0.4: version "2.0.6" resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.6.tgz#87bcc8a1a0e74000ff2bf9802110708cfb02eb6e" @@ -11021,13 +10822,6 @@ forwarded@0.2.0: resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== -fragment-cache@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" - integrity sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA== - dependencies: - map-cache "^0.2.2" - fresh@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" @@ -11357,11 +11151,6 @@ get-tsconfig@^4.7.0: dependencies: resolve-pkg-maps "^1.0.0" -get-value@^2.0.3, get-value@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" - integrity sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA== - git-cz@^4.9.0: version "4.9.0" resolved "https://registry.yarnpkg.com/git-cz/-/git-cz-4.9.0.tgz#c0604ff85b37046d51fd85e265040032c9617bf2" @@ -11728,37 +11517,6 @@ has-unicode@^2.0.0, has-unicode@^2.0.1: resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== -has-value@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" - integrity sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q== - dependencies: - get-value "^2.0.3" - has-values "^0.1.4" - isobject "^2.0.0" - -has-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" - integrity sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw== - dependencies: - get-value "^2.0.6" - has-values "^1.0.0" - isobject "^3.0.0" - -has-values@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" - integrity sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ== - -has-values@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" - integrity sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ== - dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" - has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" @@ -12264,13 +12022,6 @@ ipaddr.js@1.9.1: resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== -is-accessor-descriptor@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.1.tgz#3223b10628354644b86260db29b3e693f5ceedd4" - integrity sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA== - dependencies: - hasown "^2.0.0" - is-alphabetical@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d" @@ -12309,11 +12060,6 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-buffer@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - is-buffer@^2.0.0: version "2.0.5" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" @@ -12359,13 +12105,6 @@ is-core-module@^2.9.0: dependencies: has "^1.0.3" -is-data-descriptor@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.1.tgz#2109164426166d32ea38c405c1e0945d9e6a4eeb" - integrity sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw== - dependencies: - hasown "^2.0.0" - is-date-object@^1.0.1: version "1.0.5" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" @@ -12378,39 +12117,11 @@ is-decimal@^1.0.0: resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== -is-descriptor@^0.1.0: - version "0.1.7" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.7.tgz#2727eb61fd789dcd5bdf0ed4569f551d2fe3be33" - integrity sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg== - dependencies: - is-accessor-descriptor "^1.0.1" - is-data-descriptor "^1.0.1" - -is-descriptor@^1.0.0, is-descriptor@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.3.tgz#92d27cb3cd311c4977a4db47df457234a13cb306" - integrity sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw== - dependencies: - is-accessor-descriptor "^1.0.1" - is-data-descriptor "^1.0.1" - is-docker@^2.0.0: version "2.2.1" resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== -is-extendable@^0.1.0, is-extendable@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== - -is-extendable@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" - integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== - dependencies: - is-plain-object "^2.0.4" - is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -12503,13 +12214,6 @@ is-number-object@^1.0.4: dependencies: has-tostringtag "^1.0.0" -is-number@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" - integrity sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg== - dependencies: - kind-of "^3.0.2" - is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -12535,13 +12239,6 @@ is-plain-obj@^1.1.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= -is-plain-object@^2.0.3, is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== - dependencies: - isobject "^3.0.1" - is-plain-object@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" @@ -12625,7 +12322,7 @@ is-weakref@^1.0.1: dependencies: call-bind "^1.0.2" -is-windows@^1.0.0, is-windows@^1.0.1, is-windows@^1.0.2: +is-windows@^1.0.0, is-windows@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== @@ -12674,11 +12371,6 @@ isobject@^2.0.0: dependencies: isarray "1.0.0" -isobject@^3.0.0, isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== - isstream@0.1.x: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -13320,20 +13012,6 @@ keyv@^4.0.0: dependencies: json-buffer "3.0.1" -kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - integrity sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ== - dependencies: - is-buffer "^1.1.5" - -kind-of@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" - integrity sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw== - dependencies: - is-buffer "^1.1.5" - kind-of@^6.0.2, kind-of@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" @@ -13814,11 +13492,6 @@ map-age-cleaner@^0.1.1, map-age-cleaner@^0.1.3: dependencies: p-defer "^1.0.0" -map-cache@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" - integrity sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg== - map-obj@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" @@ -13829,13 +13502,6 @@ map-obj@^4.1.0: resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a" integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== -map-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" - integrity sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w== - dependencies: - object-visit "^1.0.0" - markdown-it-terminal@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/markdown-it-terminal/-/markdown-it-terminal-0.4.0.tgz#d58466e3de0a638d6a5bd7e16ca51d8e55f3a3ac" @@ -14158,26 +13824,7 @@ micromark@^2.11.3, micromark@~2.11.0, micromark@~2.11.3: debug "^4.0.0" parse-entities "^2.0.0" -micromatch@^3.1.4: - version "3.1.10" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" - integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - braces "^2.3.1" - define-property "^2.0.2" - extend-shallow "^3.0.2" - extglob "^2.0.4" - fragment-cache "^0.2.1" - kind-of "^6.0.2" - nanomatch "^1.2.9" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.2" - -micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5, micromatch@~4.0.8: +micromatch@^3.1.4, micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5, micromatch@^4.0.8, micromatch@~4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== @@ -14390,14 +14037,6 @@ miragejs@^0.1.48: lodash "^4.0.0" pretender "^3.4.7" -mixin-deep@^1.2.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" - integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== - dependencies: - for-in "^1.0.2" - is-extendable "^1.0.1" - mkdirp@^0.3.5: version "0.3.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.5.tgz#de3e5f8961c88c787ee1368df849ac4413eca8d7" @@ -14486,23 +14125,6 @@ nanoid@^5.0.9: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-5.0.9.tgz#977dcbaac055430ce7b1e19cf0130cea91a20e50" integrity sha512-Aooyr6MXU6HpvvWXKoVoXwKMs/KyVakWwg7xQfv5/S/RIgJMy0Ifa45H9qqYy7pTCszrHzP21Uk4PZq2HpEM8Q== -nanomatch@^1.2.9: - version "1.2.13" - resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" - integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - define-property "^2.0.2" - extend-shallow "^3.0.2" - fragment-cache "^0.2.1" - is-windows "^1.0.2" - kind-of "^6.0.2" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -14778,14 +14400,7 @@ npmlog@^6.0.0: gauge "^4.0.3" set-blocking "^2.0.0" -nth-check@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" - integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== - dependencies: - boolbase "~1.0.0" - -nth-check@^2.0.0: +nth-check@^1.0.2, nth-check@^2.0.0, nth-check@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== @@ -14807,15 +14422,6 @@ object-assign@^2.0.0: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-2.1.1.tgz#43c36e5d569ff8e4816c4efa8be02d26967c18aa" integrity sha1-Q8NuXVaf+OSBbE76i+AtJpZ8GKo= -object-copy@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" - integrity sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ== - dependencies: - copy-descriptor "^0.1.0" - define-property "^0.2.5" - kind-of "^3.0.3" - object-hash@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.1.tgz#fde452098a951cb145f039bb7d455449ddc126df" @@ -14841,13 +14447,6 @@ object-keys@^1.0.12, object-keys@^1.1.1: resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object-visit@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" - integrity sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA== - dependencies: - isobject "^3.0.0" - object.assign@^4.1.0, object.assign@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" @@ -14867,13 +14466,6 @@ object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.0 define-properties "^1.1.3" es-abstract "^1.18.0-next.2" -object.pick@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" - integrity sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ== - dependencies: - isobject "^3.0.1" - object.values@^1.1.0: version "1.1.4" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.4.tgz#0d273762833e816b693a637d30073e7051535b30" @@ -15195,11 +14787,6 @@ parseurl@~1.3.3: resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== -pascalcase@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" - integrity sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw== - path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" @@ -15392,11 +14979,6 @@ portfinder@^1.0.25, portfinder@^1.0.32: debug "^3.2.7" mkdirp "^0.5.6" -posix-character-classes@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" - integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== - postcss-media-query-parser@^0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz#27b39c6f4d94f81b1a73b8f76351c609e5cef244" @@ -15999,11 +15581,6 @@ regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.4: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== -regenerator-runtime@^0.14.0: - version "0.14.1" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" - integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== - regenerator-transform@^0.14.2: version "0.14.5" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4" @@ -16025,14 +15602,6 @@ regenerator-transform@^0.15.2: dependencies: "@babel/runtime" "^7.8.4" -regex-not@^1.0.0, regex-not@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" - integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== - dependencies: - extend-shallow "^3.0.2" - safe-regex "^1.1.0" - regexp.prototype.flags@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz#7ef352ae8d159e758c0eadca6f8fcb4eef07be26" @@ -16168,12 +15737,7 @@ remove-types@^1.0.0: "@babel/plugin-transform-typescript" "^7.16.8" prettier "^2.5.1" -repeat-element@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" - integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== - -repeat-string@^1.0.0, repeat-string@^1.6.1: +repeat-string@^1.0.0: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= @@ -16303,11 +15867,6 @@ resolve-pkg-maps@^1.0.0: resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== -resolve-url@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" - integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== - resolve.exports@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" @@ -16379,11 +15938,6 @@ restore-cursor@^5.0.0: onetime "^7.0.0" signal-exit "^4.1.0" -ret@~0.1.10: - version "0.1.15" - resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" - integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== - retry@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" @@ -16529,13 +16083,6 @@ safe-json-parse@~1.0.1: resolved "https://registry.yarnpkg.com/safe-json-parse/-/safe-json-parse-1.0.1.tgz#3e76723e38dfdda13c9b1d29a1e07ffee4b30b57" integrity sha1-PnZyPjjf3aE8mx0poeB//uSzC1c= -safe-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" - integrity sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg== - dependencies: - ret "~0.1.10" - safe-stable-stringify@^2.4.3: version "2.5.0" resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz#4ca2f8e385f2831c432a719b108a3bf7af42a1dd" @@ -16716,16 +16263,6 @@ set-function-length@^1.2.1: gopd "^1.0.1" has-property-descriptors "^1.0.2" -set-value@^2.0.0, set-value@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" - integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.3" - split-string "^3.0.1" - setprototypeof@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" @@ -16917,36 +16454,6 @@ snake-case@^3.0.3: dot-case "^3.0.4" tslib "^2.0.3" -snapdragon-node@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" - integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== - dependencies: - define-property "^1.0.0" - isobject "^3.0.0" - snapdragon-util "^3.0.1" - -snapdragon-util@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" - integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== - dependencies: - kind-of "^3.2.0" - -snapdragon@^0.8.1: - version "0.8.2" - resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" - integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== - dependencies: - base "^0.11.1" - debug "^2.2.0" - define-property "^0.2.5" - extend-shallow "^2.0.1" - map-cache "^0.2.2" - source-map "^0.5.6" - source-map-resolve "^0.5.0" - use "^3.1.0" - socket.io-adapter@~2.5.2: version "2.5.5" resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz#c7a1f9c703d7756844751b6ff9abfc1780664082" @@ -17023,17 +16530,6 @@ sort-package-json@^1.57.0: resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== -source-map-resolve@^0.5.0: - version "0.5.3" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" - integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== - dependencies: - atob "^2.1.2" - decode-uri-component "^0.2.0" - resolve-url "^0.2.1" - source-map-url "^0.4.0" - urix "^0.1.0" - source-map-support@0.5.13: version "0.5.13" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" @@ -17075,7 +16571,7 @@ source-map@^0.4.2: dependencies: amdefine ">=0.0.4" -source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6: +source-map@^0.5.0, source-map@^0.5.3: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= @@ -17149,13 +16645,6 @@ spdx-satisfies@^4.0.0: spdx-expression-parse "^3.0.0" spdx-ranges "^2.0.0" -split-string@^3.0.1, split-string@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" - integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== - dependencies: - extend-shallow "^3.0.0" - sprintf-js@^1.0.3, sprintf-js@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673" @@ -17207,14 +16696,6 @@ stagehand@^1.0.0: dependencies: debug "^4.1.0" -static-extend@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" - integrity sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g== - dependencies: - define-property "^0.2.5" - object-copy "^0.1.0" - statuses@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" @@ -17912,21 +17393,6 @@ to-fast-properties@^2.0.0: resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= -to-object-path@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" - integrity sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg== - dependencies: - kind-of "^3.0.2" - -to-regex-range@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" - integrity sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg== - dependencies: - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -17934,16 +17400,6 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -to-regex@^3.0.1, to-regex@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" - integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== - dependencies: - define-property "^2.0.2" - extend-shallow "^3.0.2" - regex-not "^1.0.2" - safe-regex "^1.1.0" - toidentifier@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" @@ -17962,7 +17418,7 @@ tracked-built-ins@^3.1.1: "@embroider/addon-shim" "^1.8.3" ember-tracked-storage-polyfill "^1.0.0" -tracked-maps-and-sets@^3.0.1: +tracked-maps-and-sets@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/tracked-maps-and-sets/-/tracked-maps-and-sets-3.0.2.tgz#6ea1b9f2a367d24f2e9905b74b24437fbce76ea6" integrity sha512-UIRcWsX1kDOcC/Q2R58weYWlw01EnmWWBwUv3okWS+zMBvsgIfYoO6veHhuNE3hgzWCEImNp46QS5CyKnw5QUA== @@ -18173,16 +17629,11 @@ underscore.string@^3.2.2, underscore.string@~3.3.4: sprintf-js "^1.0.3" util-deprecate "^1.0.2" -underscore@>=1.8.3, underscore@^1.13.2: +underscore@>=1.8.3, underscore@^1.12.1, underscore@^1.13.2, underscore@~1.6.0: version "1.13.7" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.7.tgz#970e33963af9a7dda228f17ebe8399e5fbe63a10" integrity sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g== -underscore@~1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8" - integrity sha512-z4o1fvKUojIWh9XuaVLUDdf86RQiq13AC1dmHbTpoyuu+bquHms76v16CjycCbec87J7z0k//SiQVk0sMdFmpQ== - unicode-canonical-property-names-ecmascript@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" @@ -18246,16 +17697,6 @@ unified@^9.2.2: trough "^1.0.0" vfile "^4.0.0" -union-value@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" - integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== - dependencies: - arr-union "^3.1.0" - get-value "^2.0.6" - is-extendable "^0.1.1" - set-value "^2.0.1" - unique-filename@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-2.0.1.tgz#e785f8675a9a7589e0ac77e0b5c34d2eaeac6da2" @@ -18317,14 +17758,6 @@ unquote@~1.1.1: resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ= -unset-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" - integrity sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ== - dependencies: - has-value "^0.3.1" - isobject "^3.0.0" - upath@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" @@ -18350,16 +17783,6 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -urix@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" - integrity sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg== - -use@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" - integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== - username-sync@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/username-sync/-/username-sync-1.0.3.tgz#ae41c5c8a4c8c2ecc1443a7d0742742bd7e36732"