Skip to content

Commit 5f53db7

Browse files
authored
fix resetting of invalid logins: (#2002)
* Fixes issue in FailedLogin model: - fix data-model to remove nested 'attempted.attempted' - migrate existing data to remove nested field * Also, avoid setting dt_now() in model as that results in fixed date for all objects: - update FailedLogin to update 'attempted' date on every attempt - also update PageNote object to set date in constructor * Update text for too many logins to make it clear it is set only if its a valid email * fixes #2001
1 parent 41d43ae commit 5f53db7

File tree

7 files changed

+54
-14
lines changed

7 files changed

+54
-14
lines changed

backend/btrixcloud/db.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from .migrations import BaseMigration
1818

1919

20-
CURR_DB_VERSION = "0034"
20+
CURR_DB_VERSION = "0035"
2121

2222

2323
# ============================================================================
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"""
2+
Migration 0035 -- fix model for failed logins
3+
"""
4+
5+
from btrixcloud.migrations import BaseMigration
6+
7+
8+
MIGRATION_VERSION = "0035"
9+
10+
11+
class Migration(BaseMigration):
12+
"""Migration class."""
13+
14+
# pylint: disable=unused-argument
15+
def __init__(self, mdb, **kwargs):
16+
super().__init__(mdb, migration_version=MIGRATION_VERSION)
17+
18+
async def migrate_up(self):
19+
"""Perform migration up.
20+
21+
Set created from attempted.attempted
22+
"""
23+
failed_logins = self.mdb["logins"]
24+
25+
try:
26+
res = await failed_logins.update_many(
27+
{"attempted.attempted": {"$exists": 1}},
28+
[{"$set": {"attempted": "$attempted.attempted"}}],
29+
)
30+
updated = res.modified_count
31+
print(f"{updated} failed logins fixed", flush=True)
32+
# pylint: disable=broad-exception-caught
33+
except Exception as err:
34+
print(
35+
f"Error fixing failed logins: {err}",
36+
flush=True,
37+
)

backend/btrixcloud/models.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
# from fastapi_users import models as fastapi_users_models
2525

2626
from .db import BaseMongoModel
27-
from .utils import dt_now
2827

2928
# crawl scale for constraint
3029
MAX_CRAWL_SCALE = int(os.environ.get("MAX_CRAWL_SCALE", 3))
@@ -162,7 +161,7 @@ class FailedLogin(BaseMongoModel):
162161
Failed login model
163162
"""
164163

165-
attempted: datetime = dt_now()
164+
attempted: datetime
166165
email: str
167166

168167
# Consecutive failed logins, reset to 0 on successful login or after
@@ -1996,7 +1995,7 @@ class PageNote(BaseModel):
19961995

19971996
id: UUID
19981997
text: str
1999-
created: datetime = dt_now()
1998+
created: datetime
20001999
userid: UUID
20012000
userName: str
20022001

backend/btrixcloud/pages.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,9 @@ async def add_page_note(
329329
crawl_id: str,
330330
) -> Dict[str, Union[bool, PageNote]]:
331331
"""Add note to page"""
332-
note = PageNote(id=uuid4(), text=text, userid=user.id, userName=user.name)
332+
note = PageNote(
333+
id=uuid4(), text=text, userid=user.id, userName=user.name, created=dt_now()
334+
)
333335

334336
modified = dt_now()
335337

@@ -371,7 +373,11 @@ async def update_page_note(
371373
raise HTTPException(status_code=404, detail="page_note_not_found")
372374

373375
new_note = PageNote(
374-
id=note_in.id, text=note_in.text, userid=user.id, userName=user.name
376+
id=note_in.id,
377+
text=note_in.text,
378+
userid=user.id,
379+
userName=user.name,
380+
created=dt_now(),
375381
)
376382
page_notes[matching_index] = new_note.dict()
377383

backend/btrixcloud/users.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
SuccessResponse,
3838
)
3939
from .pagination import DEFAULT_PAGE_SIZE, paginated_format
40-
from .utils import is_bool
40+
from .utils import is_bool, dt_now
4141

4242
from .auth import (
4343
init_jwt_auth,
@@ -525,13 +525,13 @@ async def inc_failed_logins(self, email: str) -> None:
525525
526526
If a FailedLogin object doesn't already exist, create it
527527
"""
528-
failed_login = FailedLogin(id=uuid4(), email=email)
528+
failed_login = FailedLogin(id=uuid4(), email=email, attempted=dt_now())
529529

530530
await self.failed_logins.find_one_and_update(
531531
{"email": email},
532532
{
533533
"$setOnInsert": failed_login.to_dict(exclude={"count", "attempted"}),
534-
"$set": {"attempted": failed_login.dict(include={"attempted"})},
534+
"$set": {"attempted": failed_login.attempted},
535535
"$inc": {"count": 1},
536536
},
537537
upsert=True,

backend/test/test_users.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -424,16 +424,14 @@ def test_user_change_role(admin_auth_headers, default_org_id):
424424

425425
def test_forgot_password():
426426
r = requests.post(
427-
f"{API_PREFIX}/auth/forgot-password",
428-
json={"email": "[email protected]"}
427+
f"{API_PREFIX}/auth/forgot-password", json={"email": "[email protected]"}
429428
)
430429
# always return success for security reasons even if user doesn't exist
431430
assert r.status_code == 202
432431
detail = r.json()["success"] == True
433432

434433
r = requests.post(
435-
f"{API_PREFIX}/auth/forgot-password",
436-
json={"email": VALID_USER_EMAIL}
434+
f"{API_PREFIX}/auth/forgot-password", json={"email": VALID_USER_EMAIL}
437435
)
438436
assert r.status_code == 202
439437
detail = r.json()["success"] == True

frontend/src/pages/log-in.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ export class LogInPage extends LiteElement {
375375
let message = msg("Sorry, invalid username or password");
376376
if (e.statusCode === 429) {
377377
message = msg(
378-
"Sorry, too many failed login attempts. A reset password link has been sent to your email.",
378+
"Sorry, too many failed login attempts. If this is a valid email, a reset password link has been sent to your email.",
379379
);
380380
}
381381
this.formStateService.send({

0 commit comments

Comments
 (0)