mirror of
https://github.com/HeyPuter/puter.git
synced 2026-09-11 07:45:50 +00:00
fix: make handle lookups case-insensitive and pin the reset flag
Review follow-ups on the team columns. NOCASE moves onto the `handle` column itself, not just the index. Index-only NOCASE makes uniqueness case-insensitive while leaving `WHERE handle = ?` case-sensitive, so the same lookup would match on mysql (utf8mb4_unicode_ci) and miss on sqlite. Postgres still needs lower(handle) at the call site. `requires_password_change` becomes NOT NULL DEFAULT 0 on all three dialects, matching the three sibling `requires_*` flags. Left nullable, any query written as `= 0` would silently exclude every pre-existing user.
This commit is contained in:
@@ -134,8 +134,7 @@ describe('SqliteDatabaseClient — boot and migrations', { timeout: DISK_MIGRATI
|
||||
'UPDATE `group` SET `handle` = ? WHERE `id` = (SELECT MIN(`id`) FROM `group`)',
|
||||
['design-team'],
|
||||
);
|
||||
// Without COLLATE NOCASE this differs per engine: MySQL rejects it via
|
||||
// utf8mb4_unicode_ci while sqlite and postgres would let it through.
|
||||
// Without NOCASE this differs per engine: mysql rejects, the others accept.
|
||||
await expect(
|
||||
client.write(
|
||||
'UPDATE `group` SET `handle` = ? WHERE `id` = (SELECT MAX(`id`) FROM `group`)',
|
||||
@@ -144,6 +143,20 @@ describe('SqliteDatabaseClient — boot and migrations', { timeout: DISK_MIGRATI
|
||||
).rejects.toThrow(/UNIQUE/iu);
|
||||
});
|
||||
|
||||
it('matches a handle case-insensitively on lookup, not just on insert', async () => {
|
||||
// Index-only NOCASE would leave `WHERE handle = ?` case-sensitive here while
|
||||
// mysql's utf8mb4_unicode_ci column matched -- one query, two behaviours.
|
||||
await client.write(
|
||||
'UPDATE `group` SET `handle` = ? WHERE `id` = (SELECT MIN(`id`) FROM `group`)',
|
||||
['design-team'],
|
||||
);
|
||||
await expect(
|
||||
client.read('SELECT `handle` FROM `group` WHERE `handle` = ?', [
|
||||
'Design-Team',
|
||||
]),
|
||||
).resolves.toEqual([{ handle: 'design-team' }]);
|
||||
});
|
||||
|
||||
it('runs the javascript migrations, not just the .sql ones', async () => {
|
||||
// The `system` user only exists because 0025 (a .dbmig.js file) ran
|
||||
// inside the migration VM.
|
||||
|
||||
@@ -24,7 +24,7 @@ CALL _puter_add_col('group', 'name', '`name` varchar(255) DEFAULT NULL');
|
||||
CALL _puter_add_col('group', 'handle', '`handle` varchar(64) DEFAULT NULL');
|
||||
CALL _puter_add_col('group', 'deleted_at', '`deleted_at` timestamp NULL DEFAULT NULL');
|
||||
CALL _puter_add_col('jct_user_group', 'org_owned', '`org_owned` tinyint(1) DEFAULT NULL');
|
||||
CALL _puter_add_col('user', 'requires_password_change', '`requires_password_change` tinyint(1) DEFAULT NULL');
|
||||
CALL _puter_add_col('user', 'requires_password_change', '`requires_password_change` tinyint(1) NOT NULL DEFAULT ''0''');
|
||||
|
||||
DROP PROCEDURE IF EXISTS _puter_add_team_indexes;
|
||||
DELIMITER //
|
||||
|
||||
@@ -35,4 +35,4 @@ ALTER TABLE jct_user_group ADD COLUMN IF NOT EXISTS org_owned smallint;
|
||||
CREATE INDEX IF NOT EXISTS idx_jct_user_group_group
|
||||
ON jct_user_group (group_id, user_id);
|
||||
|
||||
ALTER TABLE "user" ADD COLUMN IF NOT EXISTS requires_password_change smallint;
|
||||
ALTER TABLE "user" ADD COLUMN IF NOT EXISTS requires_password_change smallint NOT NULL DEFAULT 0;
|
||||
|
||||
@@ -15,16 +15,15 @@
|
||||
-- You should have received a copy of the GNU Affero General Public License
|
||||
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
-- Teams: a `group` row with `kind = 'team'` owns accounts and pays for them.
|
||||
-- Seeded system groups keep `kind` NULL, so a team query never returns them.
|
||||
-- A `group` row with `kind = 'team'` owns accounts and pays for them; seeded
|
||||
-- system groups keep `kind` NULL so a team query never returns them.
|
||||
|
||||
ALTER TABLE `group` ADD COLUMN `kind` TEXT DEFAULT NULL;
|
||||
ALTER TABLE `group` ADD COLUMN `name` TEXT DEFAULT NULL;
|
||||
ALTER TABLE `group` ADD COLUMN `handle` TEXT DEFAULT NULL;
|
||||
ALTER TABLE `group` ADD COLUMN `handle` TEXT COLLATE NOCASE DEFAULT NULL;
|
||||
ALTER TABLE `group` ADD COLUMN `deleted_at` TIMESTAMP DEFAULT NULL;
|
||||
|
||||
-- NOCASE so a handle is unique the way a username is (see 0055). MySQL gets this
|
||||
-- from utf8mb4_unicode_ci; postgres needs a lower() index. NULLs stay distinct.
|
||||
-- NOCASE on both column and index, so lookups match mysql's utf8mb4_unicode_ci.
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS `idx_group_handle`
|
||||
ON `group` (`handle` COLLATE NOCASE);
|
||||
|
||||
@@ -38,5 +37,5 @@ ALTER TABLE `jct_user_group` ADD COLUMN `org_owned` INTEGER DEFAULT NULL;
|
||||
CREATE INDEX IF NOT EXISTS `idx_jct_user_group_group`
|
||||
ON `jct_user_group` (`group_id`, `user_id`);
|
||||
|
||||
-- Fourth `requires_*` flag, enforced by assertVerifiedAccount. Set on admin reset.
|
||||
ALTER TABLE `user` ADD COLUMN `requires_password_change` INTEGER DEFAULT NULL;
|
||||
-- Fourth `requires_*` flag; assertVerifiedAccount will enforce it in phase 5.
|
||||
ALTER TABLE `user` ADD COLUMN `requires_password_change` INTEGER NOT NULL DEFAULT 0;
|
||||
|
||||
Reference in New Issue
Block a user