|
|
@ -1,10 +1,11 @@ |
|
|
|
|
|
|
|
import hashlib |
|
|
|
from datetime import datetime |
|
|
|
from datetime import datetime |
|
|
|
from collections import namedtuple |
|
|
|
from collections import namedtuple |
|
|
|
from urllib.parse import urlparse |
|
|
|
from urllib.parse import urlparse |
|
|
|
|
|
|
|
|
|
|
|
from sqlalchemy.orm.exc import NoResultFound |
|
|
|
import requests |
|
|
|
|
|
|
|
|
|
|
|
from . import models |
|
|
|
from . import models, security |
|
|
|
|
|
|
|
|
|
|
|
CONSUMABLE_STATI = { |
|
|
|
CONSUMABLE_STATI = { |
|
|
|
models.OrderStatus.ORDERED, |
|
|
|
models.OrderStatus.ORDERED, |
|
|
@ -43,7 +44,7 @@ def create_log_entry(order, status, user): |
|
|
|
def verify_credentials(repo, pass_ctx, username, password): |
|
|
|
def verify_credentials(repo, pass_ctx, username, password): |
|
|
|
try: |
|
|
|
try: |
|
|
|
user = repo.get_user_by_username(username) |
|
|
|
user = repo.get_user_by_username(username) |
|
|
|
except (NoResultFound, StopIteration): |
|
|
|
except StopIteration: |
|
|
|
# user was not found |
|
|
|
# user was not found |
|
|
|
return None |
|
|
|
return None |
|
|
|
valid, new_hash = pass_ctx.verify_and_update(password, user.password) |
|
|
|
valid, new_hash = pass_ctx.verify_and_update(password, user.password) |
|
|
@ -87,3 +88,55 @@ def check_vendor_name(repo, to_check): |
|
|
|
return CheckVendorResult(canonical_name, False) |
|
|
|
return CheckVendorResult(canonical_name, False) |
|
|
|
else: |
|
|
|
else: |
|
|
|
return CheckVendorResult(vendor, True) |
|
|
|
return CheckVendorResult(vendor, True) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def set_new_password(user, password): |
|
|
|
|
|
|
|
crypt_context = security.get_passlib_context() |
|
|
|
|
|
|
|
user.password = crypt_context.hash(password) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
messages = [] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pwd_length = len(password) |
|
|
|
|
|
|
|
if pwd_length < 16: |
|
|
|
|
|
|
|
messages.append( |
|
|
|
|
|
|
|
models.FlashMessage( |
|
|
|
|
|
|
|
f"Your password is quite short.", |
|
|
|
|
|
|
|
( |
|
|
|
|
|
|
|
f"Your Password has only {pwd_length} characters. " |
|
|
|
|
|
|
|
'Have a look at <a href="https://www.xkcd.com/936/">' |
|
|
|
|
|
|
|
"this comic strip</a> why longer passwords are better." |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sha1_hash = hashlib.sha1(password.encode()).hexdigest() |
|
|
|
|
|
|
|
if have_i_been_pwned(sha1_hash): |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
messages.append( |
|
|
|
|
|
|
|
models.FlashMessage( |
|
|
|
|
|
|
|
"This password appears in a breach or has been compromised.", |
|
|
|
|
|
|
|
( |
|
|
|
|
|
|
|
"Please consider changing your password. " |
|
|
|
|
|
|
|
'<a href="/breached">Read this page</a> on why you ' |
|
|
|
|
|
|
|
"see this message." |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return messages |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def have_i_been_pwned(password_hash): |
|
|
|
|
|
|
|
head, tails = password_hash[:5], password_hash[5:].lower() |
|
|
|
|
|
|
|
url = f"https://api.pwnedpasswords.com/range/{head}" |
|
|
|
|
|
|
|
headers = {"Add-Padding": "true"} |
|
|
|
|
|
|
|
try: |
|
|
|
|
|
|
|
response = requests.get(url, headers=headers, timeout=2) |
|
|
|
|
|
|
|
response.raise_for_status() |
|
|
|
|
|
|
|
except requests.RequestException: |
|
|
|
|
|
|
|
return False |
|
|
|
|
|
|
|
lower_case_lines = (line.lower() for line in response.text.splitlines()) |
|
|
|
|
|
|
|
for line in lower_case_lines: |
|
|
|
|
|
|
|
if line.startswith(tails): |
|
|
|
|
|
|
|
return True |
|
|
|
|
|
|
|
return False |
|
|
|