def request_password_reset(): data = request.json if not data: return abort(make_response(jsonify(message="Missing payload"), 400)) email = data.get("email", "").lstrip().rstrip().lower() if not email or not is_email(email): return abort( make_response(jsonify(message="Missing or bad email"), 400)) existing = users.get_by_email(email) # we are not leaking used or unused e-mails, that's why this responds 202 if not existing or existing["activated"] is None: return "", 202 local = local_users.get_local_user(existing["uuid"]) if not local: return abort( make_response(jsonify(message="User does not have a password"), 400)) pwd_reset_key = guid.uuid4() pwd_reset_key_expires = datetime.now().astimezone() + timedelta(hours=1) local_users.update_local_user( user_uuid=existing["uuid"], pwd_reset_key_hash=hashlib.sha256( str(pwd_reset_key).encode("utf-8")).hexdigest(), pwd_reset_key_expires=pwd_reset_key_expires, ) send_mail.delay( [email], "PASSWORD_RESET_LINK", { "pwd_reset_key": pwd_reset_key, "pwd_reset_key_expires": int(pwd_reset_key_expires.timestamp()), "email": email, }, ) return "", 202
def activate_user(): data = request.json if not data: return abort(make_response(jsonify(message="Missing payload"), 400)) email = data.get("email", "").lstrip().rstrip().lower() activation_key = data.get("activation_key", None) password = data.get("password", None) password_confirmation = data.get("password_confirmation", None) if not email or not is_email(email): return abort( make_response(jsonify(message="Missing or bad email"), 400)) if not password: return abort(make_response(jsonify(message="Missing password"), 400)) if not activation_key: return abort( make_response(jsonify(message="Missing activation_key"), 400)) if password != password_confirmation: return abort( make_response(jsonify(message="Passwords do not match"), 400)) existing = users.get_by_email(email) if not existing: return abort(make_response(jsonify(message="Cannot activate"), 400)) if existing["activated"] is not None: return abort(make_response(jsonify(message="Already activated"), 409)) if (hashlib.sha256(str(activation_key).encode("utf-8")).hexdigest() != existing["activation_key_hash"]): return abort(make_response(jsonify(message="Cannot activate"), 400)) if existing["activation_key_expires"] < datetime.now().astimezone(): return abort( make_response(jsonify(message="Activation key expired"), 400)) memberships = organization_roles.get_by_user_uuid(existing["uuid"]) if len(memberships) == 0: orguuid = guid.uuid4() organizations.add_organization(uuid=orguuid, name="Default organization") organization_roles.set_organization_role(orguuid, existing["uuid"], "admin") pwd_hash = bcrypt.hashpw(password.encode("utf8"), bcrypt.gensalt()) users.update_user( uuid=existing["uuid"], activated=datetime.now().astimezone(), providers=["local"], providers_data={}, ) local_users.add_local_user( user_uuid=existing["uuid"], pwd_hash=pwd_hash.decode("utf8"), force_pwd_change=False, ) return "", 204
def reset_password(): data = request.json if not data: return abort(make_response(jsonify(message="Missing payload"), 400)) email = data.get("email", "").lstrip().rstrip().lower() pwd_reset_key = data.get("pwd_reset_key", None) password = data.get("password", None) password_confirmation = data.get("password_confirmation", None) if not email or not is_email(email): return abort( make_response(jsonify(message="Missing or bad email"), 400)) if not password: return abort(make_response(jsonify(message="Missing password"), 400)) if not pwd_reset_key: return abort(make_response(jsonify(message="Missing reset key"), 400)) if password != password_confirmation: return abort(make_response(jsonify(message="Password mismatch"), 400)) existing = users.get_by_email(email) if not existing: return abort(make_response(jsonify(message="Cannot reset"), 400)) local = local_users.get_local_user(existing["uuid"]) if not local: return abort( make_response(jsonify(message="User does not have a password"), 400)) if (hashlib.sha256(str(pwd_reset_key).encode("utf-8")).hexdigest() != local["pwd_reset_key_hash"]): return abort(make_response(jsonify(message="Cannot reset"), 400)) if local["pwd_reset_key_expires"] < datetime.now().astimezone(): return abort(make_response(jsonify(message="Reset key expired"), 400)) pwd_hash = bcrypt.hashpw(password.encode("utf8"), bcrypt.gensalt()) local_users.update_local_user( user_uuid=existing["uuid"], pwd_hash=pwd_hash.decode("utf8"), pwd_reset_key_hash=None, pwd_reset_key_expires=None, force_pwd_change=False, ) send_mail.delay( [email], "PASSWORD_RESET_CONFIRMATION", { "email": email, }, ) return "", 204
def create_inactive_user(): data = request.json if not data: return abort(make_response(jsonify(message="Missing payload"), 400)) email = data.get("email", "").lstrip().rstrip().lower() if not email or not is_email(email): return abort( make_response(jsonify(message="Missing or bad email"), 400)) existing = users.get_by_email(email) # activated users if existing and existing["activated"] is not None: return "", 202 activation_key = guid.uuid4() activation_key_expires = datetime.now().astimezone() + timedelta( minutes=10) # reissue activation_key if existing: users.update_user( uuid=existing["uuid"], activation_key_hash=hashlib.sha256( str(activation_key).encode("utf-8")).hexdigest(), activation_key_expires=activation_key_expires, ) # completely new user else: users.add_user( uuid=guid.uuid4(), username=email, email=email, system_role="user", providers=[], activation_key_hash=hashlib.sha256( str(activation_key).encode("utf-8")).hexdigest(), activation_key_expires=activation_key_expires, ) send_mail.delay( [email], "REGISTRATION_VERIFICATION_EMAIL", { "activation_key": activation_key, "activation_key_expires": int(activation_key_expires.timestamp()), "email": email, }, ) return "", 202
def test_no_email(self): self.assertFalse(is_email(""))
def test_bad_email(self): self.assertFalse(is_email("not an email"))
def test_email(self): self.assertTrue(is_email("*****@*****.**"))
def add_user_to_org(org_uuid): data = request.json if not data: return abort(make_response(jsonify(message="Missing payload"), 400)) email = data.get("email", None) org_role = data.get("role", None) if not email or not org_role: return abort(make_response(jsonify(message="Missing email"), 400)) if org_role not in ["admin", "user"]: return abort(make_response(jsonify(message="Bad role"), 400)) email = email.lstrip().rstrip().lower() if not is_email(email): return abort(make_response(jsonify(message="Invalid email"), 400)) existing = users.get_by_email(email) # completely new user if existing is None: user_uuid = guid.uuid4() activation_key = guid.uuid4() activation_key_expires = datetime.now().astimezone() + timedelta(hours=24) users.add_user( uuid=user_uuid, email=email, username=email, system_role="user", providers=[], activation_key_hash=hashlib.sha256( str(activation_key).encode("utf-8") ).hexdigest(), activation_key_expires=activation_key_expires, ) else: user_uuid = existing.get("uuid", None) # an activated account that already has a role in this organization if ( organization_roles.get_organization_role(org_uuid, user_uuid) and existing["activated"] ): return abort(make_response(jsonify(message="User already exists"), 409)) # an account that is not activated but has already been sent an invite activation_key = guid.uuid4() activation_key_expires = datetime.now().astimezone() + timedelta(hours=24) users.update_user( uuid=user_uuid, activation_key_hash=hashlib.sha256( str(activation_key).encode("utf-8") ).hexdigest(), activation_key_expires=activation_key_expires, ) organization = organizations.get_by_uuid(org_uuid) organization_roles.set_organization_role(org_uuid, user_uuid, org_role) if existing is None or existing["activated"] is None: send_mail.delay( [email], "REGISTRATION_VERIFICATION_EMAIL", { "activation_key": activation_key, "activation_key_expires": int(activation_key_expires.timestamp()), "email": email, "inviter_username": get_current_user()["username"], "organization_name": organization["name"], "organization_uuid": organization["uuid"], }, ) else: send_mail.delay( [email], "ORGANIZATION_INVITATION", { "email": email, "inviter_username": get_current_user()["username"], "organization_name": organization["name"], "organization_uuid": organization["uuid"], }, ) return ( jsonify( { "uuid": str(user_uuid), "username": existing["username"] if existing else email, "email": email, "role": org_role, "activated": existing["activated"] is not None if existing else False, } ), 201, )