def login(request: _Parsed): json = request.json get = json.get user = get("user", "").strip().lower() password = get("password", "") invalids = [] if not user: invalids.append("username") if not password: invalids.append("password") if invalids: raise AppException(f"Invalid {' and '.join(invalids)}", HTTPStatus.UNPROCESSABLE_ENTITY) user_data = get_user_by_id(user) password_hash = user_data.password_hash if not check_password_hash(password_hash, password): raise AppException("Incorrect Password", HTTPStatus.FORBIDDEN) username = user_data.user is_admin = user_data.is_admin access_token = create_token(issue_access_token(username, is_admin)) refresh_token = create_token(issue_refresh_token(username, password_hash)) return json_response( { "success": True, "user_data": user_data.as_json }, headers={ "x-access-token": access_token, "x-refresh-token": refresh_token }, )
def assert_hunt_running(event): ev = get_event_details(event) curr_time = time() has_not_started = ev["event_start_time"] > curr_time is_over = ev["is_over"] or ev["event_end_time"] < curr_time if is_over: raise AppException("Hunt is over", HTTPStatus.FORBIDDEN) if has_not_started: raise AppException("Hunt hasn't started yet..", HTTPStatus.FORBIDDEN)
def check_integrity_error(e): # pylint: disable=E1101 orig = getattr(e, "orig", None) if isinstance(orig, UniqueViolation): args = orig.args[0] ret = get_integrity_error_cause(args) if ret is None: raise AppException("User exists", HTTPStatus.BAD_REQUEST) k, v = ret raise AppException(f'Another account exists with the {k} "{v}"') raise e
def _validate_question_content(self, key, val): prefix = f"Error on question {self._id}:" if not isinstance(val, dict): raise AppException( f"{prefix} Please send a JSON object for the question") t = val.get("type", "text") if t not in ("text", "link", "image-embed"): raise AppException(f"{prefix} invalid question content type") if not val.get("content"): raise AppException(f"{prefix} Question content cannot be blank!") return {"type": val["type"], "content": val["content"]}
def _handle_permissions(self, visibility): if visibility not in PERMISSION: raise AppException(EVENTS.REQUEST_EXCEPTION, 422, (EXCODES.WRONG_VALUE, STRINGS.PERMISSION_WRONG_VALUE, 'visibility')) if visibility < self._visibility: raise AppException( EVENTS.REQUEST_EXCEPTION, 422, (EXCODES.WRONG_VALUE, STRINGS.PERMISSION_SMALLER_VALUE, 'visibility')) self._visibility = visibility
def confirm_email(req: _Parsed): token = req.json.get("token") data = decode_token(token) if data is None: raise AppException("Token expired", HTTPStatus.UNAUTHORIZED) if data["token_type"] == EMAIL_CONF_TOKEN: user = data["user"] u = get_user_by_id(user) if u.has_verified_email: return {"success": True} u.has_verified_email = True save_to_db() return {"success": True} raise AppException("Invalid token", HTTPStatus.BAD_REQUEST)
def delete(user, creds: CredManager = CredManager): user_data = get_user_by_id(user) if user_data.is_admin: raise AppException("Cannot delete an admin account!") delete_from_db(user_data) send_admin_action_webhook([f"{user} was deleted by {creds.user}"]) return {"success": True}
def answer(req: ParsedRequest, event, creds: CredManager = CredManager): # assert_hunt_running(event) js = req.json answer = sanitize(js.get("answer", "")) # don't even bother if the user is trying an absurdly large answer if len(answer) > 50 or not answer: return {"is_correct": False} user = get_user_by_id(creds.user) if user.event != event: raise AppException("Not your event..") if user.is_disqualified: return {"disqualified": True, "reason": user.disqualification_reason} try: try: q = get_question(event, user.level) except AppException: return {"game_over": True} is_correct = sanitize(q["_secure_"]["answer"]) == answer log_answer(user.user, q["question_number"], js["answer"], is_correct) if is_correct: user.level += 1 user.points += q["question_points"] user.last_question_answered_at = time() save_to_db() return invalidate(f"{event}-leaderboard", {"is_correct": is_correct}) return {"is_correct": is_correct} except Exception as e: print(e) raise Exception("An unknown error occured")
def register(request: _Parsed): json = request.json get = json.get user = get("user") name = get("name") email = get("email") institution = get("institution") password = get("password") event = get("event") if event == "intra": raise AppException("Inta is over..see you in the main event") try: user_data = User( user=user, name=name, email=email, institution=institution, password=password, event=event, ) js = user_data.as_json add_to_db(user_data) send_acount_creation_webhook(user, name, event) return invalidate(f"{event}-leaderboard", js) except Exception as e: check_integrity_error(e)
def disqualify(req: ParsedRequest, user, *, creds: CredManager = CredManager): json = req.json reason = json.get("reason") deduct_points = json.get("points") if deduct_points < 0: raise AppException("You're adding points! Don't use negative symbol") user_data = get_user_by_id(user) if user_data.is_admin: raise AppException("Cannot disqualify an admin!") user_data.is_disqualified = True user_data.disqualification_reason = reason user_data.points -= deduct_points js = user_data.as_json send_admin_action_webhook([f"{user} was disqualified by {creds.user}"]) save_to_db() return invalidate(f"{user_data.event}-leaderboard", js)
def verify_password_reset(req: _Parsed, user_name: str): token = req.json.get("token") new_password = req.json.get("new_password") data = decode_token(token) if data is None: raise AppException("Token expired", HTTPStatus.UNAUTHORIZED) if data["token_type"] == RESET_PASSWORD_TOKEN: user = data["user"] if user != user_name.lower(): raise AppException("Lol") u = get_user_by_id(user) state = data["state"] if not check_password_hash(state, f"{u.user}{u.password_hash}"): raise AppException("Token expired", HTTPStatus.UNAUTHORIZED) u.password_hash = new_password save_to_db() return {"success": True} raise AppException("Invalid token", HTTPStatus.BAD_REQUEST)
def __init__( self, question_number: int = None, question_points: int = None, event: str = None, question_content: dict = None, question_hints: list = None, answer: str = None, ): raise_if_invalid_data(question_points, event, question_content, answer) if question_number < 0 or question_points < 1: raise AppException("Invalid question number/points") if event not in EVENT_NAMES: raise AppException("Invalid event value") self._id = f"{event}:{question_number}" self.question_number = question_number self.question_points = question_points self.event = event self.question_content = question_content self.question_hints = question_hints self.answer = answer
def edit(request: _Parsed, user: str, creds: CredManager = CredManager): current_user = creds.user if user != current_user and not creds.is_admin: raise AppException("Cannot edit ( not allowed )", HTTPStatus.FORBIDDEN) json = request.json keys = json.keys() if any(x not in editable_fields for x in keys) and not creds.is_admin: raise AppException("Requested field cannot be edited", HTTPStatus.BAD_REQUEST) user_data = get_user_by_id(user) text = [] who = creds.user did_change = False for k, v in json.items(): prev = getattr(user_data, k, "N/A") if prev == v: continue did_change = True setattr(user_data, k, v) if creds.is_admin: text.append( f"{who} changed {k} of `{user}` from `{prev}` to `{v}`") if k == "email": user_data.has_verified_email = False event = user_data.event js = user_data.as_json try: save_to_db() except Exception as e: check_integrity_error(e) if creds.is_admin and text: send_admin_action_webhook(text) if did_change: return invalidate(f"{event}-leaderboard", js) return js
def _validate_user(self, _, user: str): if not user: raise AppException("Username cannot be blank", HTTPStatus.UNPROCESSABLE_ENTITY) user = user.strip() length = len(user) if length > 30: raise AppException( "Username cannot be longer than 30 characters", HTTPStatus.UNPROCESSABLE_ENTITY, ) if length < 3: raise AppException( "Username cannot be shorter than 3 characters", HTTPStatus.UNPROCESSABLE_ENTITY, ) if sanitize(user) != user: raise AppException( "Username cannot have special characters or whitespace", HTTPStatus.UNPROCESSABLE_ENTITY, ) return user
def get_user_details(user: str, creds: CredManager = CredManager): current_user = creds.user if user == "me" or current_user == user.lower(): if current_user is not None: return self_details(creds) raise AppException("Not Authenticated", HTTPStatus.UNAUTHORIZED) user_details = get_user_by_id(user) if not creds.is_admin: json = clean_secure(user_details) else: json = user_details.as_json return {"user_data": json}
def question(event, creds: CredManager = CredManager): # assert_hunt_running(event) user = get_user_by_id(creds.user) if user.event != event: raise AppException("Not your event..") if user.is_disqualified: return {"disqualified": True, "reason": user.disqualification_reason} try: try: q = get_question(event, user.level) except AppException: return {"game_over": True} q.pop("_secure_") return q except: raise Exception("Unknown error occured")
def re_authenticate(req: _Parsed): headers = req.headers access_token = get_bearer_token(req.headers) decoded_access = decode_token(access_token) if decoded_access is None: refresh_token = headers.get("x-refresh-token") decoded_refresh = decode_token(refresh_token) access, refresh = regenerate_access_token(decoded_refresh) if access is None: raise AppException("re-auth", HTTPStatus.FORBIDDEN) return json_response( {}, headers={ "x-access-token": create_token(access), "x-refresh-token": create_token(refresh), }, )
def send_password_reset_email(req: _Parsed, user): handler = get_subdomain(req.json.get("handler")) user_data = get_user_by_id(user) token = create_token( issue_password_reset_token(user_data.user, user_data.password_hash)) qs = urlencode({"token": token, "user": user}) url = f"https://{handler}.halocrypt.com/-/reset-password?{qs}" if not user: raise AppException("Invalid request") send_email( user_data.email, "Reset password", EMAIL_TEMPLATE.replace(r"{url}", url).replace( r"{message}", "Our records indicate that you have requested a password reset. You can do so by clicking the button below", ).replace(r"{action}", "Reset Password"), f"Reset your password here: {url}", ) return {"success": True}
def get_subdomain(handler): if handler not in EVENT_NAMES: raise AppException("Invalid handler") return "www" if handler == "main" else handler
def raise_if_invalid_data(*args): if any(not x or not ((x).strip() if isinstance(x, str) else True) for x in args): raise AppException("Invalid Input")
def _validate_email(self, _, email: str): email = email.strip() if email else None if not email: raise AppException("Email cannot be blank") return validate_email_address(email)
def _validate_password(self, _, password: str): length = len(password) if length < 4: raise AppException("Password cannot be shorter than 4 characters") return generate_password_hash(password)
def _validate_name(self, _, name: str): name = (name or "").strip() if not name: raise AppException("name cannot be blank") return name
def _validate_event(self, _, event: str): if event not in EVENT_NAMES: raise AppException("Invalid event") return event
def _validate_question_points(self, key, val): num = validate_num(val) if num <= 0: raise AppException("Points should be greater than 0") return num