Просмотр исходного кода

Merge pull request #11817 from overleaf/jk-password-too-similar-metric-refinement

[web] Refine metrics on password-too-similar validation

GitOrigin-RevId: f644e50e4815b34ad9af5215ebc3c9a082572681
Jakob Ackermann 3 лет назад
Родитель
Сommit
b6d5b97326

+ 14 - 1
services/web/app/src/Features/Authentication/AuthenticationManager.js

@@ -171,6 +171,8 @@ const AuthenticationManager = {
       })
     }
 
+    Metrics.inc('try-validate-password')
+
     let allowAnyChars, min, max
     if (Settings.passwordStrengthOptions) {
       allowAnyChars = Settings.passwordStrengthOptions.allowAnyChars === true
@@ -380,18 +382,29 @@ const AuthenticationManager = {
     password = password.toLowerCase()
     email = email.toLowerCase()
     const stringsToCheck = [email].concat(email.split(/\W+/))
+    let largestSimilarity = 0
+    let err = null
     for (const emailPart of stringsToCheck) {
       if (!_exceedsMaximumLengthRatio(password, MAX_SIMILARITY, emailPart)) {
         const similarity = DiffHelper.stringSimilarity(password, emailPart)
+        const similarityOneDecimalPlace = Math.floor(similarity * 10) / 10
+        largestSimilarity = Math.max(
+          largestSimilarity,
+          similarityOneDecimalPlace
+        )
         if (similarity > MAX_SIMILARITY) {
           logger.warn(
             { email, emailPart, similarity, maxSimilarity: MAX_SIMILARITY },
             'Password too similar to email'
           )
-          return new Error('password is too similar to email')
+          err = new Error('password is too similar to email')
         }
       }
     }
+    Metrics.inc('password-validation-similarity', 1, {
+      similarity: largestSimilarity,
+    })
+    return err
   },
 }
 

+ 26 - 0
services/web/test/unit/src/Authentication/AuthenticationManagerTests.js

@@ -484,6 +484,17 @@ describe('AuthenticationManager', function () {
 
     describe('password length', function () {
       describe('with the default password length options', function () {
+        beforeEach(function () {
+          this.metrics.inc.reset()
+        })
+
+        it('should send a metric', function () {
+          this.AuthenticationManager.validatePassword('foo')
+          expect(this.metrics.inc.calledWith('try-validate-password')).to.equal(
+            true
+          )
+        })
+
         it('should reject passwords that are too short', function () {
           const result1 = this.AuthenticationManager.validatePassword('')
           expect(result1).to.be.an.instanceOf(
@@ -697,6 +708,21 @@ describe('AuthenticationManager', function () {
       expect(error).to.exist
     })
 
+    it('should send a metric with a rounded similarity score when password is too similar to email', function () {
+      const password = 'su2oe1em3re'
+      const email = 'someuser@example.com'
+      const error = this.AuthenticationManager._validatePasswordNotTooSimilar(
+        password,
+        email
+      )
+      expect(
+        this.metrics.inc.calledWith('password-validation-similarity', 1, {
+          similarity: 0.7,
+        })
+      ).to.equal(true)
+      expect(error).to.exist
+    })
+
     it('should return nothing when the password different from email', function () {
       const password = '58WyLvr'
       const email = 'someuser@example.com'