Browse Source
-----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEPFu8Fz2BGGz/3nKpWMgKKqbHZeEFAmqmtpQACgkQWMgKKqbH ZeGSUA//a/6dX3Q2dbe8dWk46RZEBGuuzRlfo8JroLmIGEYhInuYg0z3fhIzxL27 UNRtq3w/y0t+NsqZxXjY+JKckrsuLPasmrftPzzxISjiptYHOz1/kS6ub0iTiJB8 woDtd9ApFrCuj0koTavVy7VMoH5LDaqgGw8hYdKF4yfGbp1NiJ2tzkri6ryBrEEw zk5P69hoZmJddHh0Ym4236piUc0YscZaWSsUR5NC1Mrtv1AGzyA/RKAZOrl/vPjD SpTwWvMyVNLliDWfBM598rYZx+Af0/k2HAZAk8jnMSWFx+DNQTqluEFaAHN0ukAr vlwrF08HyxTs3B/zXgFdk/zwvRPipi0wNTV0LLcptVc7GnvbyA80sZxnaSJajfJC iaJbUH35LoqE5QB8bBMIfoP6v+pu6IkGV8cz9lxhVIppv0EpGHdiOlsuPZepXMif kkKevQAiehGyTrmZm39kco3OQm11XTNmF0tTaubJc76tQEHVS97u0T8xwC0e7KT0 JgsczlMt5Wf/SzOPjrXLzRkGblt3WU+p0TakW19CTcELaZCM0cp+3SPvYbqJkii1 rKRsSwn+DdhWl1GniN8pIwTSwvwU0lkFxVvoGd/qx5Ctyg+6MBukKdmPvrr8F9jn tGkeCyUN6cPv1Wl/vTRiZqfZn8l+ZYX3hvGdgE0ZSxMBflnGisc= =SDWv -----END PGP SIGNATURE----- Merge tag '1.37.3' into public-api-member-group-crud Resolve the log_event conflict with upstream #7656, which made log_event take an EventType: keep the typed parameter and pass the acting user and device as Some(..), now that log_event_impl takes them as optional for the Public API events. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>pull/7569/head
98 changed files with 15536 additions and 23116 deletions
File diff suppressed because it is too large
@ -1,15 +1,31 @@ |
|||
-- Dynamically create DROP FOREIGN KEY |
|||
-- Some versions of MySQL or MariaDB might fail if the key doesn't exists |
|||
-- This checks if the key exists, and if so, will drop it. |
|||
SET @drop_sso_fk = IF((SELECT true FROM information_schema.TABLE_CONSTRAINTS WHERE |
|||
CONSTRAINT_SCHEMA = DATABASE() AND |
|||
TABLE_NAME = 'sso_users' AND |
|||
CONSTRAINT_NAME = 'sso_users_ibfk_1' AND |
|||
CONSTRAINT_TYPE = 'FOREIGN KEY') = true, |
|||
'ALTER TABLE sso_users DROP FOREIGN KEY sso_users_ibfk_1', |
|||
'SELECT 1'); |
|||
PREPARE stmt FROM @drop_sso_fk; |
|||
EXECUTE stmt; |
|||
DEALLOCATE PREPARE stmt; |
|||
SELECT if ( |
|||
EXISTS( |
|||
SELECT CONSTRAINT_NAME FROM information_schema.table_constraints |
|||
WHERE TABLE_SCHEMA = DATABASE() |
|||
AND TABLE_NAME = 'sso_users' |
|||
AND CONSTRAINT_TYPE = 'FOREIGN KEY' |
|||
AND CONSTRAINT_NAME = 'sso_users_ibfk_1' |
|||
) |
|||
,'ALTER TABLE sso_users DROP FOREIGN KEY `sso_users_ibfk_1`' |
|||
,'SELECT "info: FK sso_users_ibfk_1 does not exist."' |
|||
) INTO @drop_stmt; |
|||
PREPARE drop_stmt FROM @drop_stmt; |
|||
EXECUTE drop_stmt; |
|||
|
|||
SELECT if ( |
|||
EXISTS( |
|||
SELECT CONSTRAINT_NAME FROM information_schema.table_constraints |
|||
WHERE TABLE_SCHEMA = DATABASE() |
|||
AND TABLE_NAME = 'sso_users' |
|||
AND CONSTRAINT_TYPE = 'FOREIGN KEY' |
|||
AND CONSTRAINT_NAME = '1' |
|||
) |
|||
,'ALTER TABLE sso_users DROP FOREIGN KEY `1`' |
|||
,'SELECT "info: FK sso_users 1 does not exist."' |
|||
) INTO @drop_stmt; |
|||
PREPARE drop_stmt FROM @drop_stmt; |
|||
EXECUTE drop_stmt; |
|||
|
|||
DEALLOCATE PREPARE drop_stmt; |
|||
|
|||
ALTER TABLE sso_users ADD FOREIGN KEY(user_uuid) REFERENCES users(uuid) ON UPDATE CASCADE ON DELETE CASCADE; |
|||
|
|||
File diff suppressed because it is too large
@ -0,0 +1,56 @@ |
|||
import { test, expect, type Page, type TestInfo } from '@playwright/test'; |
|||
import * as OTPAuth from "otpauth"; |
|||
|
|||
import * as utils from "../global-utils"; |
|||
import { createAccount, logUser } from './setups/user'; |
|||
import { activateTOTP, disableTOTP } from './setups/2fa'; |
|||
|
|||
let users = utils.loadEnv(); |
|||
let totp; |
|||
|
|||
test.beforeAll('Setup', async ({ browser }, testInfo: TestInfo) => { |
|||
await utils.startVault(browser, testInfo, {}); |
|||
}); |
|||
|
|||
test.afterAll('Teardown', async ({}) => { |
|||
utils.stopVault(); |
|||
}); |
|||
|
|||
test('Change Key settings', async ({ page }) => { |
|||
await createAccount(test, page, users.user1); |
|||
|
|||
await test.step('Change SHA-256 Iterations', async () => { |
|||
await page.getByRole('button', { name: 'Toggle collapse Settings' }).click(); |
|||
await page.getByRole('link', { name: 'Security' }).click(); |
|||
await page.getByRole('link', { name: 'Keys' }).click(); |
|||
|
|||
await page.getByRole('spinbutton', { name: 'KDF iterations * (required)'}).fill('700000'); |
|||
|
|||
await page.getByRole('button', { name: 'Update encryption settings' }).click(); |
|||
await page.getByRole('textbox', { name: 'Master password * (required)' }).fill(users.user1.password); |
|||
await page.getByRole('button', { name: 'Update settings' }).click(); |
|||
await page.getByRole('heading', { name: 'Log in' }).click(); |
|||
}); |
|||
|
|||
await logUser(test, page, users.user1); |
|||
|
|||
await test.step('Switch to Argon2', async () => { |
|||
await page.getByRole('button', { name: 'Toggle collapse Settings' }).click(); |
|||
await page.getByRole('link', { name: 'Security' }).click(); |
|||
await page.getByRole('link', { name: 'Keys' }).click(); |
|||
|
|||
await page.locator('.ng-arrow-wrapper').click(); |
|||
await page.getByText('Argon2id').click(); |
|||
|
|||
await page.getByRole('spinbutton', { name: 'KDF memory (MB) * (required)'}).fill('16'); |
|||
await page.getByRole('spinbutton', { name: 'KDF iterations * (required)'}).fill('2'); |
|||
await page.getByRole('spinbutton', { name: 'KDF parallelism * (required)'}).fill('1'); |
|||
|
|||
await page.getByRole('button', { name: 'Update encryption settings' }).click(); |
|||
await page.getByRole('textbox', { name: 'Master password * (required)' }).fill(users.user1.password); |
|||
await page.getByRole('button', { name: 'Update settings' }).click(); |
|||
await page.getByRole('heading', { name: 'Log in' }).click(); |
|||
}); |
|||
|
|||
await logUser(test, page, users.user1); |
|||
}); |
|||
@ -0,0 +1,110 @@ |
|||
import { test, expect, type Page, type TestInfo } from '@playwright/test'; |
|||
import * as OTPAuth from "otpauth"; |
|||
|
|||
import * as utils from "../global-utils"; |
|||
import { createAccount, logUser } from './setups/user'; |
|||
|
|||
let users = utils.loadEnv(); |
|||
let totp; |
|||
|
|||
test.beforeAll('Setup', async ({ browser }, testInfo: TestInfo) => { |
|||
await utils.startVault(browser, testInfo, {}); |
|||
|
|||
const context = await browser.newContext(); |
|||
const page = await context.newPage(); |
|||
await createAccount(test, page, users.user1); |
|||
await context.close(); |
|||
}); |
|||
|
|||
test.afterAll('Teardown', async ({}) => { |
|||
utils.stopVault(); |
|||
}); |
|||
|
|||
test('Password', async ({ context, page }, testInfo: TestInfo) => { |
|||
const label = 'Test Password'; |
|||
|
|||
await logUser(test, page, users.user1); |
|||
|
|||
await test.step('Create password entry', async () => { |
|||
await page.getByRole('button', { name: 'New item' }).click(); |
|||
await page.getByRole('textbox', { name: 'Item name * (required)' }).fill(label); |
|||
await page.getByRole('textbox', { name: 'Username' }).fill(users.user1.name); |
|||
await page.getByRole('textbox', { name: 'Password' }).fill(users.user1.password); |
|||
await page.getByRole('button', { name: 'Save' }).click(); |
|||
await utils.checkNotification(page, 'Item added'); |
|||
await page.getByRole('button', { name: 'Close' }).click(); |
|||
}); |
|||
|
|||
// Log again
|
|||
await logUser(test, page, users.user1); |
|||
|
|||
await test.step('Check', async () => { |
|||
await page.getByRole('row').filter({ hasText: label }).getByRole('button', { name: label }).click(); |
|||
await page.getByTestId('copy-username').click(); |
|||
await utils.checkNotification(page, 'Username copied'); |
|||
expect(await page.evaluate(() => navigator.clipboard.readText())).toBe(users.user1.name) |
|||
await page.getByTestId('copy-password').click(); |
|||
await utils.checkNotification(page, 'Password copied'); |
|||
expect(await page.evaluate(() => navigator.clipboard.readText())).toBe(users.user1.password) |
|||
await page.getByRole('button', { name: 'Close' }).click(); |
|||
}); |
|||
|
|||
await test.step('Delete', async () => { |
|||
await page.getByRole('row').filter({ hasText: label }).getByLabel('Options').click(); |
|||
await page.getByRole('menuitem', { name: 'Delete' }).click(); |
|||
await page.getByRole('button', { name: 'Yes' }).click(); |
|||
await utils.checkNotification(page, 'Item sent to bin'); |
|||
}); |
|||
|
|||
// Log again
|
|||
await logUser(test, page, users.user1); |
|||
|
|||
await test.step('Deleted', async () => { |
|||
await expect(page.getByRole('row').filter({ hasText: label })).toHaveCount(0) |
|||
}); |
|||
}); |
|||
|
|||
|
|||
test('SSH Key', async ({ context, page }, testInfo: TestInfo) => { |
|||
const label = 'Test SSH key'; |
|||
|
|||
await logUser(test, page, users.user1); |
|||
|
|||
const privateKey = await test.step('Create key entry', async () => { |
|||
await page.getByRole('button', { name: 'New', exact: true }).click(); |
|||
await page.getByRole('menuitem', { name: 'SSH key' }).click(); |
|||
await page.getByRole('textbox', { name: 'Item name * (required)' }).fill('Test SSH key'); |
|||
await page.getByRole('button', { name: 'Save' }).click(); |
|||
await utils.checkNotification(page, 'Item added'); |
|||
|
|||
await page.getByRole('button', { name: 'Copy private key' }).click(); |
|||
await utils.checkNotification(page, 'Private key copied'); |
|||
return await page.evaluate(() => navigator.clipboard.readText()); |
|||
}); |
|||
|
|||
// Log again
|
|||
await logUser(test, page, users.user1); |
|||
|
|||
await test.step('Check', async () => { |
|||
await page.getByRole('row').filter({ hasText: label }).getByRole('button', { name: label }).click(); |
|||
|
|||
await page.getByRole('button', { name: 'Copy private key' }).click(); |
|||
await utils.checkNotification(page, 'Private key copied'); |
|||
expect(await page.evaluate(() => navigator.clipboard.readText())).toBe(privateKey) |
|||
await page.getByRole('button', { name: 'Close' }).click(); |
|||
}); |
|||
|
|||
await test.step('Delete', async () => { |
|||
await page.getByRole('row').filter({ hasText: label }).getByLabel('Options').click(); |
|||
await page.getByRole('menuitem', { name: 'Delete' }).click(); |
|||
await page.getByRole('button', { name: 'Yes' }).click(); |
|||
await utils.checkNotification(page, 'Item sent to bin'); |
|||
}); |
|||
|
|||
// Log again
|
|||
await logUser(test, page, users.user1); |
|||
|
|||
await test.step('Deleted', async () => { |
|||
await expect(page.getByRole('row').filter({ hasText: label })).toHaveCount(0) |
|||
}) |
|||
}); |
|||
@ -0,0 +1,21 @@ |
|||
import { expect, type Browser, Page } from '@playwright/test'; |
|||
import * as utils from '../../global-utils'; |
|||
|
|||
utils.loadEnv(); |
|||
|
|||
export async function login(test, page: Page) { |
|||
await test.step(`Admin login`, async () => { |
|||
await page.goto('/admin'); |
|||
await page.getByRole('textbox', { name: 'Enter admin token' }).fill(process.env.ADMIN_TOKEN); |
|||
await page.getByRole('button', { name: 'Enter' }).click(); |
|||
}); |
|||
} |
|||
|
|||
export async function invite(test, page: Page, email: string) { |
|||
await test.step(`Invite user with ${email}`, async () => { |
|||
await page.getByRole('link', { name: 'Users' }).click(); |
|||
await page.getByRole('textbox', { name: 'Enter email' }).fill(email); |
|||
await page.getByRole('button', { name: 'Invite' }).click(); |
|||
await expect(page.getByRole('row', { name: email })).toHaveText(/Invited/); |
|||
}); |
|||
} |
|||
@ -1,4 +1,4 @@ |
|||
[toolchain] |
|||
channel = "1.97.1" |
|||
channel = "1.98.1" |
|||
components = [ "rustfmt", "clippy" ] |
|||
profile = "minimal" |
|||
|
|||
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,12 @@ |
|||
Admin account recovery from {{org_name}} organization |
|||
<!----------------> |
|||
{{#if reset_password}} |
|||
The master password for {{user_name}} has been changed. |
|||
{{/if}} |
|||
{{#if reset_2fa}} |
|||
Your two-step verification providers have been reset.{{#if fallback_2fa_email}} Email two factor has been activated as a fallback.{{/if}} |
|||
{{/if}} |
|||
|
|||
If you did not initiate this request, please reach out to your administrator immediately. |
|||
|
|||
{{> email/email_footer_text }} |
|||
@ -1,10 +1,17 @@ |
|||
Master Password Has Been Changed |
|||
Admin account recovery from {{org_name}} organization |
|||
<!----------------> |
|||
{{> email/email_header }} |
|||
<table width="100%" cellpadding="0" cellspacing="0" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;"> |
|||
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;"> |
|||
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none;" valign="top"> |
|||
The master password for <b style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">{{user_name}}</b> has been changed by an administrator in your <b style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">{{org_name}}</b> organization. If you did not initiate this request, please reach out to your administrator immediately. |
|||
{{#if reset_password}} |
|||
The master password for <b style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">{{user_name}}</b> has been changed. |
|||
{{/if}} |
|||
{{#if reset_2fa}} |
|||
Your two-step verification providers have been reset.{{#if fallback_2fa_email}} Email two factor has been activated as a fallback.{{/if}} |
|||
{{/if}} |
|||
<br> |
|||
If you did not initiate this request, please reach out to your administrator immediately. |
|||
</td> |
|||
</tr> |
|||
</table> |
|||
@ -1,4 +0,0 @@ |
|||
Master Password Has Been Changed |
|||
<!----------------> |
|||
The master password for {{user_name}} has been changed by an administrator in your {{org_name}} organization. If you did not initiate this request, please reach out to your administrator immediately. |
|||
{{> email/email_footer_text }} |
|||
Loading…
Reference in new issue