From 992aa567fa80979d952cc7cad5e8c07caef8c069 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senart?= Date: Wed, 17 Jul 2019 14:30:03 +0200 Subject: [PATCH] Allow creating and updating (user|org) names with dots (#4925) In #4690, we made it possible to create usernames from SSO signups that include dots. But that would have failed, as I found out working on this, due to a database constraint that needed to be updated. Additionally, it was still not possible to change or manually create an account with a username that includes a dot in the UI. This PR fixes that too. For consistency, we allow dots in org names as well. Fixes #4909, #4920 --- CHANGELOG.md | 2 +- cmd/frontend/db/schema.md | 4 +- cmd/frontend/db/users_test.go | 9 +++- ...28395581_allows_dots_in_usernames.down.sql | 13 +++++ ...1528395581_allows_dots_in_usernames.up.sql | 13 +++++ migrations/bindata.go | 48 +++++++++++++++++++ web/src/org/new/NewOrganizationPage.tsx | 4 +- .../site-admin/SiteAdminCreateUserPage.tsx | 4 ++ web/src/user/index.ts | 2 +- .../profile/UserSettingsProfilePage.tsx | 6 +-- 10 files changed, 95 insertions(+), 10 deletions(-) create mode 100644 migrations/1528395581_allows_dots_in_usernames.down.sql create mode 100644 migrations/1528395581_allows_dots_in_usernames.up.sql diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f3532bdd797e..dd1cf46f1270b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,7 +46,7 @@ All notable changes to Sourcegraph are documented in this file. ### Changed -- Usernames can now contain the `.` character (#4690). +- Usernames and org names can now contain the `.` character (#4674). ### Added diff --git a/cmd/frontend/db/schema.md b/cmd/frontend/db/schema.md index dbce0987d4e19..d380ae02a2168 100644 --- a/cmd/frontend/db/schema.md +++ b/cmd/frontend/db/schema.md @@ -256,7 +256,7 @@ Indexes: Check constraints: "orgs_display_name_max_length" CHECK (char_length(display_name) <= 255) "orgs_name_max_length" CHECK (char_length(name::text) <= 255) - "orgs_name_valid_chars" CHECK (name ~ '^[a-zA-Z0-9](?:[a-zA-Z0-9]|-(?=[a-zA-Z0-9]))*$'::citext) + "orgs_name_valid_chars" CHECK (name ~ '^[a-zA-Z0-9](?:[a-zA-Z0-9]|[-.](?=[a-zA-Z0-9]))*$'::citext) Referenced by: TABLE "names" CONSTRAINT "names_org_id_fkey" FOREIGN KEY (org_id) REFERENCES orgs(id) ON UPDATE CASCADE ON DELETE CASCADE TABLE "org_invitations" CONSTRAINT "org_invitations_org_id_fkey" FOREIGN KEY (org_id) REFERENCES orgs(id) @@ -612,7 +612,7 @@ Indexes: Check constraints: "users_display_name_max_length" CHECK (char_length(display_name) <= 255) "users_username_max_length" CHECK (char_length(username::text) <= 255) - "users_username_valid_chars" CHECK (username ~ '^[a-zA-Z0-9](?:[a-zA-Z0-9]|-(?=[a-zA-Z0-9]))*$'::citext) + "users_username_valid_chars" CHECK (username ~ '^[a-zA-Z0-9](?:[a-zA-Z0-9]|[-.](?=[a-zA-Z0-9]))*$'::citext) Referenced by: TABLE "access_tokens" CONSTRAINT "access_tokens_creator_user_id_fkey" FOREIGN KEY (creator_user_id) REFERENCES users(id) TABLE "access_tokens" CONSTRAINT "access_tokens_subject_user_id_fkey" FOREIGN KEY (subject_user_id) REFERENCES users(id) diff --git a/cmd/frontend/db/users_test.go b/cmd/frontend/db/users_test.go index 2e55d31a932b8..572c57ba1ff4a 100644 --- a/cmd/frontend/db/users_test.go +++ b/cmd/frontend/db/users_test.go @@ -28,14 +28,21 @@ var usernamesForTests = []struct { {"deadmau5", true}, {"deadmau-5", true}, {"3blindmice", true}, + {"nick.com", true}, + {"nick.com.uk", true}, + {"nick.com-put-er", true}, {"777", true}, {"7-7", true}, {"long-butnotquitelongenoughtoreachlimit", true}, + {".nick", false}, + {"-nick", false}, {"nick-", false}, + {"nick.", false}, {"nick--s", false}, {"nick--sny", false}, - {"nick.com", false}, + {"nick..sny", false}, + {"nick.-sny", false}, {"nick_s", false}, {"_", false}, {"_nick", false}, diff --git a/migrations/1528395581_allows_dots_in_usernames.down.sql b/migrations/1528395581_allows_dots_in_usernames.down.sql new file mode 100644 index 0000000000000..93e88a585eed9 --- /dev/null +++ b/migrations/1528395581_allows_dots_in_usernames.down.sql @@ -0,0 +1,13 @@ +BEGIN; + +ALTER TABLE orgs +DROP CONSTRAINT orgs_name_valid_chars, +ADD CONSTRAINT orgs_name_valid_chars +CHECK (name ~ '^[a-zA-Z0-9](?:[a-zA-Z0-9]|-(?=[a-zA-Z0-9]))*$'); + +ALTER TABLE users +DROP CONSTRAINT users_username_valid_chars, +ADD CONSTRAINT users_username_valid_chars +CHECK (username ~ '^[a-zA-Z0-9](?:[a-zA-Z0-9]|-(?=[a-zA-Z0-9]))*$'); + +COMMIT; diff --git a/migrations/1528395581_allows_dots_in_usernames.up.sql b/migrations/1528395581_allows_dots_in_usernames.up.sql new file mode 100644 index 0000000000000..a55f1812ca5ab --- /dev/null +++ b/migrations/1528395581_allows_dots_in_usernames.up.sql @@ -0,0 +1,13 @@ +BEGIN; + +ALTER TABLE orgs +DROP CONSTRAINT orgs_name_valid_chars, +ADD CONSTRAINT orgs_name_valid_chars +CHECK (name ~ '^[a-zA-Z0-9](?:[a-zA-Z0-9]|[-.](?=[a-zA-Z0-9]))*$'); + +ALTER TABLE users +DROP CONSTRAINT users_username_valid_chars, +ADD CONSTRAINT users_username_valid_chars +CHECK (username ~ '^[a-zA-Z0-9](?:[a-zA-Z0-9]|[-.](?=[a-zA-Z0-9]))*$'); + +COMMIT; diff --git a/migrations/bindata.go b/migrations/bindata.go index 256762fde4604..5e05d38dd4bc8 100644 --- a/migrations/bindata.go +++ b/migrations/bindata.go @@ -223,6 +223,8 @@ // 1528395579_.up.sql (175B) // 1528395580_create_user_permissions_table.down.sql (56B) // 1528395580_create_user_permissions_table.up.sql (331B) +// 1528395581_allows_dots_in_usernames.down.sql (349B) +// 1528395581_allows_dots_in_usernames.up.sql (355B) package migrations @@ -4751,6 +4753,46 @@ func _1528395580_create_user_permissions_tableUpSql() (*asset, error) { return a, nil } +var __1528395581_allows_dots_in_usernamesDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x72\x75\xf7\xf4\xb3\xe6\xe2\x72\xf4\x09\x71\x0d\x52\x08\x71\x74\xf2\x71\x55\xc8\x2f\x4a\x2f\xe6\x72\x09\xf2\x0f\x50\x70\xf6\xf7\x0b\x0e\x09\x72\xf4\xf4\x0b\x01\x0b\xc6\xe7\x25\xe6\xa6\xc6\x97\x25\xe6\x64\xa6\xc4\x27\x67\x24\x16\x15\xeb\x70\x39\xba\xb8\x10\x54\xc5\xe5\xec\xe1\xea\xec\xad\xa0\x01\x12\x57\xa8\x53\x50\x8f\x8b\x4e\xd4\xad\x72\xd4\x8d\x32\xd0\xb5\x8c\xd5\xb0\xb7\x42\xe2\xd5\xe8\x6a\xd8\xdb\x22\xf1\x35\x35\xb5\x54\xd4\x35\xd1\x9c\x57\x5a\x9c\x5a\x84\xe9\x3e\xb0\x68\x3c\x88\x24\xe8\x48\xdc\x4a\x61\x2e\x85\xc9\x91\xe9\x5a\x67\x7f\x5f\x5f\xcf\x10\x6b\x2e\x40\x00\x00\x00\xff\xff\x72\x9e\x45\x86\x5d\x01\x00\x00") + +func _1528395581_allows_dots_in_usernamesDownSqlBytes() ([]byte, error) { + return bindataRead( + __1528395581_allows_dots_in_usernamesDownSql, + "1528395581_allows_dots_in_usernames.down.sql", + ) +} + +func _1528395581_allows_dots_in_usernamesDownSql() (*asset, error) { + bytes, err := _1528395581_allows_dots_in_usernamesDownSqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "1528395581_allows_dots_in_usernames.down.sql", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1e, 0x65, 0x10, 0xd8, 0x95, 0x0, 0x5, 0xf8, 0x4f, 0xfe, 0xa7, 0x33, 0xc9, 0x19, 0x9c, 0x8e, 0x11, 0x5f, 0x9d, 0x79, 0x65, 0x6a, 0xf9, 0x72, 0xe6, 0xf2, 0xb7, 0x2a, 0x7f, 0x42, 0x60, 0xf7}} + return a, nil +} + +var __1528395581_allows_dots_in_usernamesUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x72\x75\xf7\xf4\xb3\xe6\xe2\x72\xf4\x09\x71\x0d\x52\x08\x71\x74\xf2\x71\x55\xc8\x2f\x4a\x2f\xe6\x72\x09\xf2\x0f\x50\x70\xf6\xf7\x0b\x0e\x09\x72\xf4\xf4\x0b\x01\x0b\xc6\xe7\x25\xe6\xa6\xc6\x97\x25\xe6\x64\xa6\xc4\x27\x67\x24\x16\x15\xeb\x70\x39\xba\xb8\x10\x54\xc5\xe5\xec\xe1\xea\xec\xad\xa0\x01\x12\x57\xa8\x53\x50\x8f\x8b\x4e\xd4\xad\x72\xd4\x8d\x32\xd0\xb5\x8c\xd5\xb0\xb7\x42\xe2\xd5\x44\xeb\xea\xc5\x6a\xd8\xdb\x22\x09\x69\x6a\x6a\xa9\xa8\x6b\xa2\xb9\xb0\xb4\x38\xb5\x08\xd3\x89\x60\xd1\x78\x10\x49\xd0\x9d\xb8\x95\xc2\x1c\x0b\x93\x23\xdf\xc1\xce\xfe\xbe\xbe\x9e\x21\xd6\x5c\x80\x00\x00\x00\xff\xff\xa8\x98\x32\xb2\x63\x01\x00\x00") + +func _1528395581_allows_dots_in_usernamesUpSqlBytes() ([]byte, error) { + return bindataRead( + __1528395581_allows_dots_in_usernamesUpSql, + "1528395581_allows_dots_in_usernames.up.sql", + ) +} + +func _1528395581_allows_dots_in_usernamesUpSql() (*asset, error) { + bytes, err := _1528395581_allows_dots_in_usernamesUpSqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "1528395581_allows_dots_in_usernames.up.sql", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x76, 0x56, 0x31, 0x9d, 0xe6, 0x87, 0xfe, 0x57, 0x99, 0xf, 0x68, 0x62, 0xc2, 0x1b, 0xca, 0x33, 0x44, 0xb8, 0x65, 0x8c, 0x8f, 0xc4, 0xf, 0x44, 0x7f, 0x28, 0x6b, 0x4b, 0x35, 0x84, 0x3b, 0x8c}} + return a, nil +} + // Asset loads and returns the asset for the given name. // It returns an error if the asset could not be found or // could not be loaded. @@ -5287,6 +5329,10 @@ var _bindata = map[string]func() (*asset, error){ "1528395580_create_user_permissions_table.down.sql": _1528395580_create_user_permissions_tableDownSql, "1528395580_create_user_permissions_table.up.sql": _1528395580_create_user_permissions_tableUpSql, + + "1528395581_allows_dots_in_usernames.down.sql": _1528395581_allows_dots_in_usernamesDownSql, + + "1528395581_allows_dots_in_usernames.up.sql": _1528395581_allows_dots_in_usernamesUpSql, } // AssetDir returns the file names below a certain @@ -5553,6 +5599,8 @@ var _bintree = &bintree{nil, map[string]*bintree{ "1528395579_.up.sql": {_1528395579_UpSql, map[string]*bintree{}}, "1528395580_create_user_permissions_table.down.sql": {_1528395580_create_user_permissions_tableDownSql, map[string]*bintree{}}, "1528395580_create_user_permissions_table.up.sql": {_1528395580_create_user_permissions_tableUpSql, map[string]*bintree{}}, + "1528395581_allows_dots_in_usernames.down.sql": {_1528395581_allows_dots_in_usernamesDownSql, map[string]*bintree{}}, + "1528395581_allows_dots_in_usernames.up.sql": {_1528395581_allows_dots_in_usernamesUpSql, map[string]*bintree{}}, }} // RestoreAsset restores an asset under the given directory. diff --git a/web/src/org/new/NewOrganizationPage.tsx b/web/src/org/new/NewOrganizationPage.tsx index ea7b81631d47b..c1fd5d33d35ac 100644 --- a/web/src/org/new/NewOrganizationPage.tsx +++ b/web/src/org/new/NewOrganizationPage.tsx @@ -109,8 +109,8 @@ export class NewOrganizationPage extends React.Component { aria-describedby="new-org-page__form-name-help" /> - An organization name consists of letters, numbers, hyphens (-) and may not begin or end with - a hyphen + An organization name consists of letters, numbers, hyphens (-), dots (.) and may not begin + or end with a hyphen nor a dot. diff --git a/web/src/site-admin/SiteAdminCreateUserPage.tsx b/web/src/site-admin/SiteAdminCreateUserPage.tsx index b6065ea1d3f5d..f23a5555dff21 100644 --- a/web/src/site-admin/SiteAdminCreateUserPage.tsx +++ b/web/src/site-admin/SiteAdminCreateUserPage.tsx @@ -127,6 +127,10 @@ export class SiteAdminCreateUserPage extends React.Component { disabled={this.state.loading} autoFocus={true} /> + + A username consists of letters, numbers, hyphens (-), dots (.) and may not begin or end + with a hyphen nor a dot. +
diff --git a/web/src/user/index.ts b/web/src/user/index.ts index a50ee94605a3e..a2415db74c1c6 100644 --- a/web/src/user/index.ts +++ b/web/src/user/index.ts @@ -1,7 +1,7 @@ /** * Regular expression to identify valid username. */ -export const VALID_USERNAME_REGEXP = /^[a-zA-Z0-9](?:[a-zA-Z0-9]|-(?=[a-zA-Z0-9]))*$/.source +export const VALID_USERNAME_REGEXP = /^[a-zA-Z0-9](?:[a-zA-Z0-9]|[-.](?=[a-zA-Z0-9]))*$/.source /** Maximum allowed length for a username. */ export const USERNAME_MAX_LENGTH = 255 diff --git a/web/src/user/settings/profile/UserSettingsProfilePage.tsx b/web/src/user/settings/profile/UserSettingsProfilePage.tsx index 9f6fbe4d5ef07..736e439bbf7d7 100644 --- a/web/src/user/settings/profile/UserSettingsProfilePage.tsx +++ b/web/src/user/settings/profile/UserSettingsProfilePage.tsx @@ -200,9 +200,9 @@ export class UserSettingsProfilePage extends React.Component { } aria-describedby="user-settings-profile-page__form-username-help" /> - - A username consists of letters, numbers, hyphens (-) and may not begin or end with a - hyphen + + A username consists of letters, numbers, hyphens (-), dots (.) and may not begin or end + with a hyphen nor a dot.