def login(): try: json_schema_manager.validate(request.json, "login.json") email = request.json.get("email", None) password = request.json.get("password", None) user = User.query.filter_by(email=email).first() if not user: return json_api(AccessDenied, ErrorSchema), 403 if not user.confirmed: return json_api(UserNotConfirmed, ErrorSchema), 403 if not user.check_password(password): return json_api(AccessDenied, ErrorSchema), 403 access_token = create_access_token(identity=user.uuid) refresh_token = create_refresh_token(identity=user.uuid) ret = { "access_token": access_token, "token_type": "Bearer", "expires-in": 3600, "refresh_token": refresh_token, } return jsonify(ret), 200 except ValidationError: return json_api(BadRequest, ErrorSchema), 401
def tunnel_admin() -> Tuple[Response, int]: """ Stop any currently running tunnel if you are an admin """ current_user = User.query.filter_by(uuid=get_jwt_identity()).first_or_404() if current_user.tier != "admin": return json_api(NotFoundError, ErrorSchema), 404 try: json_schema_manager.validate(request.json, "admin_tunnel.json") subdomain_name = dig(request.json, "data/attributes/subdomainName") reason = dig(request.json, "data/attributes/reason") except ValidationError as e: return json_api(BadRequest(source=e.message), ErrorSchema), 400 subdomain = Subdomain.query.filter_by(name=subdomain_name).first_or_404() tunnel = Tunnel.query.filter_by(subdomain_id=subdomain.id).first_or_404() try: TunnelDeletionService(current_user, tunnel).delete() logger.info( "%s deleted %s.holepunch.io for reason %s", current_user.email, subdomain_name, reason, ) return make_response(""), 204 except TunnelError: return json_api(TunnelError, ErrorSchema), 500
def check_api_version(): if "Api-Version" in request.headers: if not re.match(r"^\d+\.\d+\.\d+\.\d+$", request.headers["Api-Version"]): return json_api(MalformedAPIHeader, ErrorSchema), 403 if check_version(request.headers["Api-Version"]): return else: return json_api(OldAPIVersion, ErrorSchema), 400
def config_release(config_id): current_user = User.query.filter_by(uuid=get_jwt_identity()).first_or_404() config = Config.query.filter_by(user=current_user, id=config_id).first_or_404() try: ConfigDeletionService(current_user, config).release() return "", 204 except AccessDenied: return json_api(BadRequest, ErrorSchema), 403 except ConfigInUse: return json_api(ConfigInUse, ErrorSchema), 403
def subdomain_release(subdomain_id): current_user = User.query.filter_by(uuid=get_jwt_identity()).first_or_404() subdomain = Subdomain.query.filter_by( user=current_user, id=subdomain_id ).first_or_404() try: SubdomainDeletionService(current_user, subdomain).release() return "", 204 except AccessDenied: return json_api(BadRequest, ErrorSchema), 403 except SubdomainInUse: return json_api(SubdomainInUse, ErrorSchema), 403
def debug_error_handler(e): if env == "production": rollbar.report_exc_info() return json_api(InternalServerError, ErrorSchema), 500 else: return ( json_api( InternalServerError( detail=str(e), backtrace=traceback.format_exc().split("\n") ), ErrorSchema, ), 500, )
def stripe_webhook_wrapper(*args, **kwargs): payload = request.data.decode("utf-8") try: sig_header = request.headers["stripe-signature"] event = stripe.Webhook.construct_event( payload, sig_header, current_app.config["STRIPE_ENDPOINT_SECRET"]) except ValueError: # Invalid JSON return json_api(BadRequest(), ErrorSchema), 400 except (KeyError, stripe.error.SignatureVerificationError): return json_api(AccessDenied(), ErrorSchema), 403 return func(event=event, *args, **kwargs)
def register_user(): """ Create a new User Record""" try: json_schema_manager.validate(request.json, "user_create.json") user = UserCreation( email=dig(request.json, "data/attributes/email"), password=dig(request.json, "data/attributes/password"), ).create() return json_api(user, UserSchema), 204 except UserError as e: return json_api(e, ErrorSchema), 422 except ValidationError as e: return json_api(BadRequest(source=e.message), ErrorSchema), 400
def get_tunnel(tunnel_id) -> Tuple[Response, int]: """ Retrieve Tunnel Resource """ current_user = User.query.filter_by(uuid=get_jwt_identity()).first_or_404() tunnel = Tunnel.query.filter_by(user=current_user, id=tunnel_id).first_or_404() return json_api(tunnel, TunnelSchema), 200
def get_box(box_id) -> Tuple[Response, int]: """ Retrieve Box Resource """ current_user = User.query.filter_by(uuid=get_jwt_identity()).first_or_404() box = Box.query.filter_by(user=current_user, id=box_id).first_or_404() return json_api(box, BoxSchema), 200
def start_box() -> Tuple[Response, int]: try: json_schema_manager.validate(request.json, "box_create.json") config_id = dig(request.json, "data/relationships/config/data/id") ssh_key = dig(request.json, "data/attributes/sshKey") current_user = User.query.filter_by(uuid=get_jwt_identity()).first_or_404() try: box_info = BoxCreationService(current_user, config_id, ssh_key).create() except ConfigLimitReached: return json_api(ConfigLimitReached, ErrorSchema), 403 except BoxLimitReached: return json_api(BoxLimitReached, ErrorSchema), 403 except BoxError: rollbar.report_exc_info(sys.exc_info()) return json_api(BoxError, ErrorSchema), 500 return json_api(box_info, BoxSchema), 201 except ValidationError as e: return json_api(BadRequest(detail=e.message), ErrorSchema), 400 except AccessDenied: return json_api(AccessDenied, ErrorSchema), 403 except ConfigInUse: return json_api(ConfigInUse, ErrorSchema), 403
def start_tunnel() -> Tuple[Response, int]: try: json_schema_manager.validate(request.json, "tunnel_create.json") subdomain_id = dig(request.json, "data/relationships/subdomain/data/id") port_types = dig(request.json, "data/attributes/port") ssh_key = dig(request.json, "data/attributes/sshKey") current_user = User.query.filter_by(uuid=get_jwt_identity()).first_or_404() try: tunnel_info = TunnelCreationService( current_user, subdomain_id, port_types, ssh_key ).create() except SubdomainLimitReached: return json_api(SubdomainLimitReached, ErrorSchema), 403 except TunnelLimitReached: return json_api(TunnelLimitReached, ErrorSchema), 403 except TunnelError: return json_api(TunnelError, ErrorSchema), 500 return json_api(tunnel_info, TunnelSchema), 201 except ValidationError as e: return json_api(BadRequest(detail=e.message), ErrorSchema), 400 except AccessDenied: return json_api(AccessDenied, ErrorSchema), 403 except SubdomainInUse: return json_api(SubdomainInUse, ErrorSchema), 403
def subdomain_index(): subdomains = User.query.filter_by(uuid=get_jwt_identity()).first_or_404().subdomains name = dig(request.query_params, "filter/name") if name: subdomains = subdomains.filter_by(name=name) if subdomains: return json_api(subdomains, SubdomainSchema, many=True)
def get_config(config_id): """ Stop a currently running config """ current_user = User.query.filter_by(uuid=get_jwt_identity()).first_or_404() config = Config.query.filter_by(user=current_user, id=config_id).first_or_404() return json_api(config, ConfigSchema)
def delete_user(): """ Delete an existing User Record""" try: json_schema_manager.validate(request.json, "user_delete.json") current_user = User.query.filter_by(uuid=get_jwt_identity()).first_or_404() attributes = dig(request.json, "data/attributes", None) service = UserDeletion( current_user, scopes=authentication.jwt_scopes(), **attributes ) entries_deleted = service.delete() return json_api(entries_deleted, UserSchema), 200 except UserError: return json_api(UnprocessableEntity, ErrorSchema), 422 except AccessDenied as e: return json_api(e, ErrorSchema), 403
def config_index(): configs = User.query.filter_by( uuid=get_jwt_identity()).first_or_404().configs name = dig(request.query_params, "filter/name") if name: configs = configs.filter_by(name=name) if configs: return json_api(configs, ConfigSchema, many=True)
def get_subdomain(subdomain_id): """ Stop a currently running subdomain """ current_user = User.query.filter_by(uuid=get_jwt_identity()).first_or_404() subdomain = Subdomain.query.filter_by( user=current_user, id=subdomain_id ).first_or_404() return json_api(subdomain, SubdomainSchema)
def account_hook(event): """ This endpoint is called by Stripes webhook integrations for async events """ job_id = None if event.type == "invoice.payment_suceeded": job_id = payment_jobs.user_async_subscribe.queue(event).id elif event.type == "invoice.payment_failed": job_id = payment_jobs.user_async_unsubscribe.queue(event).id return json_api(AsyncJob(id=job_id), AsyncSchema), 202
def subdomain_reserve(): try: json_schema_manager.validate(request.json, "subdomain_create.json") current_user = User.query.filter_by(uuid=get_jwt_identity()).first_or_404() subdomain = SubdomainCreationService( current_user, dig(request.json, "data/attributes/name") ).reserve(True) return json_api(subdomain, SubdomainSchema), 200 except ValidationError: return ( json_api(BadRequest(detail="Request does not match schema."), ErrorSchema), 400, ) except SubdomainLimitReached: return json_api(SubdomainLimitReached, ErrorSchema), 403 except SubdomainTaken: return json_api(SubdomainTaken, ErrorSchema), 400 except SubdomainError: return json_api(SubdomainError, ErrorSchema), 500
def box_index() -> Tuple[Response, int]: """ Fetch index of a users current boxes """ boxes = User.query.filter_by(uuid=get_jwt_identity()).first_or_404().boxes name = dig(request.query_params, "filter/config/name") if name: boxes = boxes.join(Config).filter(Config.name == name) return json_api(boxes, BoxSchema, many=True), 200
def tunnel_index() -> Tuple[Response, int]: """ Fetch index of a users current tunnels """ tunnels = User.query.filter_by(uuid=get_jwt_identity()).first_or_404().tunnels name = dig(request.query_params, "filter/subdomain/name") if name: tunnels = tunnels.join(Subdomain).filter(Subdomain.name == name) return json_api(tunnels, TunnelSchema, many=True), 200
def session(): uuid = get_jwt_identity() user = User.query.filter_by(uuid=uuid).first() if user is None: return json_api(AccessDenied, ErrorSchema), 403 ret = { "access_token": create_access_token(identity=uuid), "token_type": "Bearer", "expires-in": 3600, } return jsonify(ret), 200
def stop_box(box_id) -> Tuple[Response, int]: """ Stop a currently running box """ current_user = User.query.filter_by(uuid=get_jwt_identity()).first_or_404() box = Box.query.filter_by(user=current_user, id=box_id).first_or_404() try: BoxDeletionService(current_user, box).delete() return make_response(""), 204 except BoxError: return json_api(BoxError, ErrorSchema), 500
def stop_tunnel(tunnel_id) -> Tuple[Response, int]: """ Stop a currently running tunnel """ current_user = User.query.filter_by(uuid=get_jwt_identity()).first_or_404() tunnel = Tunnel.query.filter_by(user=current_user, id=tunnel_id).first_or_404() try: TunnelDeletionService(current_user, tunnel).delete() return make_response(""), 204 except TunnelError: return json_api(TunnelError, ErrorSchema), 500
def update_user(): """ Update an existing User Record""" try: json_schema_manager.validate(request.json, "user_update.json") current_user = User.query.filter_by(uuid=get_jwt_identity()).first_or_404() new_attrs = dig(request.json, "data/attributes", None) service = UserUpdate( current_user, scopes=authentication.jwt_scopes(), **new_attrs ) updated_user = service.update() return json_api(updated_user, UserSchema), 200 except UserError as e: return json_api(e, ErrorSchema), 422 except AccessDenied as e: return json_api(e, ErrorSchema), 403 except ValidationError as e: return json_api(BadRequest(source=e.message), ErrorSchema), 400
def config_create(): try: json_schema_manager.validate(request.json, "config_create.json") current_user = User.query.filter_by( uuid=get_jwt_identity()).first_or_404() config = ConfigCreationService( current_user, dig(request.json, "data/attributes/name")).create(True) return json_api(config, ConfigSchema), 200 except ValidationError: return ( json_api(BadRequest(detail="Request does not match schema."), ErrorSchema), 400, ) except ConfigLimitReached: return json_api(ConfigLimitReached, ErrorSchema), 403 except ConfigTaken: return json_api(ConfigTaken, ErrorSchema), 400 except ConfigError: return json_api(ConfigError, ErrorSchema), 500
def confirm(token): for salt in token_types: uuid = authentication.decode_token(token, salt=salt) if salt == "password-reset-salt" and uuid: task_token = UserToken(uuid).issue_task_token("update:user:new_password") return ( jsonify( { "access_token": task_token, "token_type": "Bearer", "expires-in": 3600, } ), 200, ) elif salt == "email-confirm-salt" and uuid: uuid = authentication.decode_token(token, salt=salt) if not UserToken(uuid).confirm(): return json_api(AccessDenied, ErrorSchema), 403 return "", 204 return json_api(AccessDenied, ErrorSchema), 403
def start_box() -> Tuple[Response, int]: try: json_schema_manager.validate(request.json, "box_create.json") config_id = dig(request.json, "data/relationships/config/data/id") ssh_key = dig(request.json, "data/attributes/sshKey") image = dig(request.json, "data/attributes/image", "ubuntu") #right now there is only one valid choice #in the future there will be multiple choices and customer snapshots #we will want to throw an error in the future for an invalid choice if image == "ubuntu": image = "cypherpunkarmory/ubuntu:0.0.1" elif image == "debian": image = "cypherpunkarmory/debian:0.0.1" elif image == "kali": image = "cypherpunkarmory/kali:0.0.1" elif image == "alpine": image = "cypherpunkarmory/alpine:0.0.1" elif image == "arch": image = "cypherpunkarmory/arch:0.0.1" else: image = "cypherpunkarmory/ubuntu:0.0.1" current_user = User.query.filter_by( uuid=get_jwt_identity()).first_or_404() try: box_info = BoxCreationService(current_user, config_id, ssh_key, image).create() except ConfigLimitReached: return json_api(ConfigLimitReached, ErrorSchema), 403 except BoxLimitReached: return json_api(BoxLimitReached, ErrorSchema), 403 except BoxError: rollbar.report_exc_info(sys.exc_info()) return json_api(BoxError, ErrorSchema), 500 return json_api(box_info, BoxSchema), 201 except ValidationError as e: return json_api(BadRequest(detail=e.message), ErrorSchema), 400 except AccessDenied: return json_api(AccessDenied, ErrorSchema), 403 except ConfigInUse: return json_api(ConfigInUse, ErrorSchema), 403
def create_token(): try: json_schema_manager.validate(request.json, "token.json") email = dig(request.json, "data/attributes/email", None) token_type = dig(request.json, "data/type", None) user = User.query.filter_by(email=email).first_or_404() uns = UserNotification(user) if token_type == "email_confirm": uns.activation_emails() elif token_type == "password_reset": uns.password_reset_email() except ValidationError as e: return json_api(BadRequest(source=e.message), ErrorSchema), 400 except NotFound: return "", 200 return "", 200
def page_not_found(e): return json_api(NotFoundError(detail=e.description), ErrorSchema), 404