diff --git a/dbschema/migrations/00004.edgeql b/dbschema/migrations/00004.edgeql new file mode 100644 index 0000000..a149eba --- /dev/null +++ b/dbschema/migrations/00004.edgeql @@ -0,0 +1,8 @@ +CREATE MIGRATION m1mq62xqifdg62gf6oxshidxgnxruewugit5pgsxw6wydiojyytpoq + ONTO m1xz5pf3vdfhz2jg7irvfbvlu3lxnw4yrv2i4ycj3ygrkvgq3mcq6a +{ + ALTER TYPE default::ContributorLicenseAgreement { + CREATE PROPERTY normalized_username := (std::str_lower(.username)); + CREATE INDEX ON (.normalized_username); + }; +}; diff --git a/dbschema/structure.esdl b/dbschema/structure.esdl index ac472d8..f1cffc5 100644 --- a/dbschema/structure.esdl +++ b/dbschema/structure.esdl @@ -93,6 +93,9 @@ module default { property username -> str; + property normalized_username := str_lower(.username); + index on (.normalized_username); + required property creation_time -> datetime { default := datetime_current(); } diff --git a/service/data/edgedb/cla.ts b/service/data/edgedb/cla.ts index 76e5732..bf1e2cb 100644 --- a/service/data/edgedb/cla.ts +++ b/service/data/edgedb/cla.ts @@ -18,8 +18,11 @@ export class EdgeDBClaRepository async getClaByEmailAddress( email: string ): Promise { - const ghPseudoEmail = /^([0-9]+)\+([^@]+)@users\.noreply\.github\.com$/; - const ghEmailMatches = email.match(ghPseudoEmail); + const ghPseudoEmail = /^(?:[0-9]+)\+([^@]+)@users\.noreply\.github\.com$/; + const ghPseudoEmailOld = /^([^@+]+)@users\.noreply\.github\.com$/; + const ghEmailMatches = ( + email.match(ghPseudoEmail) || email.match(ghPseudoEmailOld) + ); let signed_cla = null; if (ghEmailMatches) { @@ -31,10 +34,10 @@ export class EdgeDBClaRepository creation_time, versionId := .agreement_version.id } - FILTER .username = $0 + FILTER .normalized_username = str_lower($0) ORDER BY .email LIMIT 1;`, - [ghEmailMatches[2]] + [ghEmailMatches[1]] ); }); } diff --git a/service/handlers/sign-cla.ts b/service/handlers/sign-cla.ts index 4e669d3..7abd50f 100644 --- a/service/handlers/sign-cla.ts +++ b/service/handlers/sign-cla.ts @@ -56,7 +56,7 @@ class SignClaHandler { private getAllAuthors(data: ClaCheckInput): string[] { if (data.authors) { - return data.authors; + return data.authors.map(email => email.toLowerCase()); } throw new Error("Missing authors information in state."); @@ -130,10 +130,9 @@ class SignClaHandler { if (!matchingEmails.length) { // The user who signed in is not among those who contributed to the PR - // NB: this can also happen if the user has a private email address, - // and wants to preserve email privacy when committing. - // As of today, it is unclear how this scenario should be handled. - // + // NB: private email address *are* passed in `getUserEmailAddresses` + // as well. Same for the pseudo-emails Github provides for each user + // in the form 12345678+USERNAME@users.noreply.github.com. throw new SafeError( `Thank you for authorizing our application, but the CLA must be signed ` + `by the users who contributed to the PR. ` +