diff --git a/frontend/src/lib/workspaceCreation.test.ts b/frontend/src/lib/workspaceCreation.test.ts index 55230538e7..13d63d1fd1 100644 --- a/frontend/src/lib/workspaceCreation.test.ts +++ b/frontend/src/lib/workspaceCreation.test.ts @@ -52,4 +52,11 @@ describe('usernameFromName', () => { expect(usernameFromName('++')).toBeUndefined() expect(usernameFromName('')).toBeUndefined() }) + + it('answers undefined rather than a value the column cannot hold', () => { + // `usr.username` is VARCHAR(50) while the name and email it is derived from run to 255, + // and `create_workspace` inserts it untruncated. + expect(usernameFromName('a'.repeat(50))).toBe('a'.repeat(50)) + expect(usernameFromName('a'.repeat(51))).toBeUndefined() + }) }) diff --git a/frontend/src/lib/workspaceCreation.ts b/frontend/src/lib/workspaceCreation.ts index 3a39613870..da67afba6f 100644 --- a/frontend/src/lib/workspaceCreation.ts +++ b/frontend/src/lib/workspaceCreation.ts @@ -45,15 +45,22 @@ export interface UsernamePolicy { suggested?: string } +/** What `usr.username` holds, and neither the provider name nor the email is bounded by it. */ +const USERNAME_MAX_LENGTH = 50 + /** - * A username the `proper_username` constraint accepts: `^[\w-]+$`, so word characters and - * hyphens and nothing else. Everything else is dropped rather than substituted — `O'Connor` - * is `oconnor`, not `o-connor` — and a name with nothing left of it answers undefined, which - * is the caller's cue to ask for one instead of posting a value the database refuses. + * A username the whole `usr.username` contract accepts: the `proper_username` constraint + * (`^[\w-]+$`, so word characters and hyphens and nothing else) and the column's own 50 + * characters. Anything outside the class is dropped rather than substituted — `O'Connor` is + * `oconnor`, not `o-connor`. + * + * Undefined where nothing usable is left or where what is left is too long, which is the + * caller's cue to ask for one: `create_workspace` inserts this value with no truncation, so a + * name the column refuses would fail on insert with nothing on screen naming the field. */ export function usernameFromName(name: string): string | undefined { const cleaned = name.toLowerCase().replace(/[^\w-]/g, '') - return cleaned === '' ? undefined : cleaned + return cleaned === '' || cleaned.length > USERNAME_MAX_LENGTH ? undefined : cleaned } /**