示例#1
0
def method_not_allowed(error: MethodNotAllowed) -> Tuple[Dict, int]:
    api_errors = ApiErrors()
    api_errors.add_error(
        "global",
        "La méthode que vous utilisez n'existe pas sur notre serveur")
    app.logger.error("405 %s" % str(error))
    return jsonify(api_errors.errors), 405
示例#2
0
def change_beneficiary_email(body: ChangeBeneficiaryEmailBody) -> None:
    try:
        users_api.change_user_email(body.token)
    except InvalidTokenError as error:
        errors = ApiErrors()
        errors.status_code = 400
        raise errors from error
示例#3
0
def check_stock_can_be_created_for_offer(offer: Offer) -> None:
    check_validation_status(offer)
    if offer.isFromProvider:
        api_errors = ApiErrors()
        api_errors.add_error("global",
                             "Les offres importées ne sont pas modifiables")
        raise api_errors
def unsubscribe_user():
    source_ip = ipaddress.IPv4Address(
        request.headers.get("X-Forwarded-For", "0.0.0.0"))
    if source_ip not in SENDINBLUE_IP_RANGE and settings.IS_DEV is False:
        raise ApiErrors(
            {"IP": "Source IP address is not whitelisted"},
            status_code=401,
        )

    user_email = request.json.get("email")
    if not user_email:
        raise ApiErrors(
            {"email": "Email missing in request payload"},
            status_code=400,
        )

    user_to_unsubscribe = find_user_by_email(user_email)
    if user_to_unsubscribe is None:
        raise ApiErrors({"User": "******" % user_email},
                        status_code=400)

    user_to_unsubscribe.notificationSubscriptions = ({
        **user_to_unsubscribe.notificationSubscriptions, "marketing_email":
        False
    } if user_to_unsubscribe.notificationSubscriptions is not None else {
        "marketing_email": False
    })
    repository.save(user_to_unsubscribe)
示例#5
0
def check_stock_has_no_custom_reimbursement_rule(stock: Stock):
    if stock.offer.custom_reimbursement_rules:
        api_errors = ApiErrors()
        api_errors.status_code = 400
        api_errors.add_error(
            "price", "Vous ne pouvez pas modifier le prix de cette offre")
        raise api_errors
示例#6
0
def ratelimit_handler(error: Exception) -> tuple[dict, int]:
    # `pcapi.utis.login_manager` cannot be imported at module-scope,
    # because the application context may not be available and that
    # module needs it.
    from pcapi.utils.login_manager import get_request_authorization

    identifier = None
    try:
        if request.is_json and "identifier" in request.json:
            identifier = request.json["identifier"]
    except json.JSONDecodeError as e:
        logger.info("Could not extract user identifier from request: %s", e)
    auth = get_request_authorization()
    if auth and auth.username:
        identifier = auth.username
    extra = {
        "method": request.method,
        "identifier": identifier,
        "route": str(request.url_rule),
        "path": request.path,
        "queryParams": request.query_string.decode("UTF-8"),
    }

    logger.warning("Requests ratelimit exceeded on routes url=%s",
                   request.url,
                   extra=extra)
    api_errors = ApiErrors()
    api_errors.add_error(
        "global",
        "Nombre de tentatives de connexion dépassé, veuillez réessayer dans une minute"
    )
    return jsonify(api_errors.errors), 429
示例#7
0
    def validate_email_not_empty(cls, email: str) -> Optional[str]:  # typing: ignore # pylint: disable=no-self-argument
        if not email or email.isspace():
            errors = ApiErrors()
            errors.add_error("email", "L'email renseigné est vide")
            raise errors

        return email
示例#8
0
def check_offer_name_length_is_valid(offer_name: str):
    max_offer_name_length = 90
    if len(offer_name) > max_offer_name_length:
        api_error = ApiErrors()
        api_error.add_error(
            "name", "Le titre de l’offre doit faire au maximum 90 caractères.")
        raise api_error
def check_offer_is_editable(offer: Offer):
    if not offer.isEditable:
        error = ApiErrors()
        error.status_code = 400
        error.add_error("global",
                        "Les offres importées ne sont pas modifiables")
        raise error
def check_demarches_simplifiees_webhook_payload(payload: Dict):
    try:
        request.form["dossier_id"]
    except:
        errors = ApiErrors()
        errors.add_error("application_id", "Invalid application id")
        raise errors
示例#11
0
def check_stock_is_updatable(stock: Stock) -> None:
    check_offer_existing_stocks_are_editable(stock.offer)
    if stock.isEventExpired:
        api_errors = ApiErrors()
        api_errors.add_error("global",
                             "Les événements passés ne sont pas modifiables")
        raise api_errors
示例#12
0
def check_offer_is_digital(offer: Offer) -> None:
    if not offer.isDigital:
        errors = ApiErrors()
        errors.add_error(
            "global",
            "Impossible de créer des codes d'activation sur une offre non-numérique",
        )
        raise errors
示例#13
0
def check_offer_existing_stocks_are_editable(offer: Offer) -> None:
    check_validation_status(offer)
    if not offer.isEditable:
        error = ApiErrors()
        error.status_code = 400
        error.add_error("global",
                        "Les offres importées ne sont pas modifiables")
        raise error
示例#14
0
def check_offer_isbn_is_valid(isbn: str):
    isbn_length = 13
    if not (isbn and isbn.isnumeric() and len(isbn) == isbn_length):
        api_errors = ApiErrors()
        api_errors.add_error(
            "isbn",
            "Format d’ISBN incorrect. Exemple de format correct : 9782020427852"
        )
        raise api_errors
示例#15
0
def ensure_current_user_has_rights(rights, offerer_id, user=current_user):
    if not user.hasRights(rights, offerer_id):
        errors = ApiErrors()
        errors.add_error(
            "global",
            "Vous n'avez pas les droits d'accès suffisant pour accéder à cette information."
        )
        errors.status_code = 403
        raise errors
示例#16
0
def check_update_only_allowed_offer_fields_for_allocine_offer(
        updated_fields: set) -> None:
    rejected_fields = updated_fields - EDITABLE_FIELDS_FOR_ALLOCINE_OFFER
    if rejected_fields:
        api_error = ApiErrors()
        for field in rejected_fields:
            api_error.add_error(field, "Vous ne pouvez pas modifier ce champ")

        raise api_error
示例#17
0
def check_user_has_access_to_offerer(user: User, offerer_id: int):
    if not user.has_access(offerer_id):
        errors = ApiErrors()
        errors.add_error(
            "global",
            "Vous n'avez pas les droits d'accès suffisant pour accéder à cette information."
        )
        errors.status_code = 403
        raise errors
示例#18
0
def check_validation_status(offer: Offer) -> None:
    if offer.validation in (OfferValidationStatus.REJECTED,
                            OfferValidationStatus.PENDING):
        error = ApiErrors()
        error.add_error(
            "global",
            "Les offres refusées ou en attente de validation ne sont pas modifiables"
        )
        raise error
示例#19
0
def check_update_only_allowed_stock_fields_for_allocine_offer(
        updated_fields: set) -> None:
    if not updated_fields.issubset(EDITABLE_FIELDS_FOR_ALLOCINE_STOCK):
        api_errors = ApiErrors()
        api_errors.status_code = 400
        api_errors.add_error(
            "global",
            "Pour les offres importées, certains champs ne sont pas modifiables"
        )
        raise api_errors
示例#20
0
def internal_error(error: Exception) -> Union[Tuple[Dict, int], HTTPException]:
    # pass through HTTP errors
    if isinstance(error, HTTPException):
        return error
    app.logger.exception("Unexpected error on method=%s url=%s: %s",
                         request.method, request.url, error)
    errors = ApiErrors()
    errors.add_error(
        "global", "Il semble que nous ayons des problèmes techniques :(" +
        " On répare ça au plus vite.")
    return jsonify(errors.errors), 500
示例#21
0
def database_error_handler(error: DatabaseError) -> Response:
    logger.error(
        "Database error %s with the following query.\n\n🚨🚨🚨🚨🚨 BEFORE COPYING THE QUERY MAKE SURE THERE IS NO SQL INJECTION 🚨🚨🚨🚨🚨🚨\n\n%s;",
        error.__class__.__name__,
        error.statement % format_sql_statement_params(error.params),
    )
    errors = ApiErrors()
    errors.add_error(
        "global", "Il semble que nous ayons des problèmes techniques :(" +
        " On répare ça au plus vite.")
    return jsonify(errors.errors), 500
示例#22
0
def check_single_order_by_string(order_by_string):
    order_by_string = order_by_string.strip(" ")
    optional_table_prefix = '("?\\w+"?\\.|)'
    column_identifier = '"?\\w+"?'
    optional_sorting_order = "(|\\s+desc|\\s+asc)"
    if not re.match(
            f"^{optional_table_prefix}{column_identifier}{optional_sorting_order}$",
            order_by_string, re.IGNORECASE):
        api_errors = ApiErrors()
        api_errors.add_error("order_by",
                             'Invalid order_by field : "%s"' % order_by_string)
        raise api_errors
示例#23
0
def upsert_stocks(
    offer_id: int, stock_data_list: List[Union[StockCreationBodyModel,
                                               StockEditionBodyModel]]
) -> List[Stock]:
    stocks = []
    edited_stocks = []
    edited_stocks_previous_beginnings = {}

    offer = offer_queries.get_offer_by_id(offer_id)

    for stock_data in stock_data_list:
        if isinstance(stock_data, StockEditionBodyModel):
            stock = Stock.queryNotSoftDeleted().filter_by(
                id=stock_data.id).first_or_404()
            if stock.offerId != offer_id:
                errors = ApiErrors()
                errors.add_error(
                    "global",
                    "Vous n'avez pas les droits d'accès suffisant pour accéder à cette information."
                )
                errors.status_code = 403
                raise errors
            edited_stocks_previous_beginnings[
                stock.id] = stock.beginningDatetime
            edited_stock = _edit_stock(
                stock,
                price=stock_data.price,
                quantity=stock_data.quantity,
                beginning=stock_data.beginning_datetime,
                booking_limit_datetime=stock_data.booking_limit_datetime,
            )
            edited_stocks.append(edited_stock)
            stocks.append(edited_stock)
        else:
            created_stock = _create_stock(
                offer=offer,
                price=stock_data.price,
                quantity=stock_data.quantity,
                beginning=stock_data.beginning_datetime,
                booking_limit_datetime=stock_data.booking_limit_datetime,
            )
            stocks.append(created_stock)

    repository.save(*stocks)

    for stock in edited_stocks:
        previous_beginning = edited_stocks_previous_beginnings[stock.id]
        if stock.beginningDatetime != previous_beginning:
            _notify_beneficiaries_upon_stock_edit(stock)
    if feature_queries.is_active(FeatureToggle.SYNCHRONIZE_ALGOLIA):
        redis.add_offer_id(client=app.redis_client, offer_id=offer.id)

    return stocks
示例#24
0
def load_or_raise_error(obj_class, human_id):
    data = obj_class.query.filter_by(id=dehumanize(human_id)).first()
    if data is None:
        errors = ApiErrors()
        errors.add_error(
            "global",
            "Aucun objet ne correspond à cet identifiant dans notre base de données"
        )
        errors.status_code = 400
        raise errors

    return data
示例#25
0
def check_update_only_allowed_fields_for_offer_from_provider(
        updated_fields: set, provider: Provider) -> None:
    if provider.isAllocine:
        rejected_fields = updated_fields - EDITABLE_FIELDS_FOR_ALLOCINE_OFFER
    else:
        rejected_fields = updated_fields - EDITABLE_FIELDS_FOR_OFFER_FROM_PROVIDER
    if rejected_fields:
        api_error = ApiErrors()
        for field in rejected_fields:
            api_error.add_error(field, "Vous ne pouvez pas modifier ce champ")

        raise api_error
示例#26
0
def mark_booking_as_used(educational_booking_id: int) -> prebooking_serialization.EducationalBookingResponse:
    """Mark a booking used by the educational institute

    Can only work if booking is in CONFIRMED status"""
    try:
        educational_booking = api.mark_educational_booking_as_used_by_institute(educational_booking_id)
    except exceptions.EducationalBookingNotFound:
        raise ApiErrors({"code": constants.EDUCATIONAL_BOOKING_NOT_FOUND}, status_code=404)
    except exceptions.EducationalBookingNotConfirmedYet:
        raise ApiErrors({"code": "EDUCATIONAL_BOOKING_NOT_CONFIRMED_YET"}, status_code=422)

    return prebooking_serialization.serialize_educational_booking(educational_booking)
示例#27
0
def _load_product_by_isbn_and_check_is_gcu_compatible_or_raise_error(
        isbn: str) -> Product:
    product = Product.query.filter(
        Product.extraData["isbn"].astext == isbn).first()
    if product is None or not product.isGcuCompatible:
        errors = ApiErrors()
        errors.add_error(
            "isbn",
            "Ce produit n’est pas éligible au pass Culture.",
        )
        errors.status_code = 400
        raise errors
    return product
def signin(body: authentication.SigninRequest) -> authentication.SigninResponse:
    try:
        user = users_repo.get_user_with_credentials(body.identifier, body.password)
    except users_exceptions.UnvalidatedAccount as exc:
        raise ApiErrors({"code": "EMAIL_NOT_VALIDATED", "general": ["L'email n'a pas été validé."]}) from exc
    except users_exceptions.CredentialsException as exc:
        raise ApiErrors({"general": ["Identifiant ou Mot de passe incorrect"]}) from exc

    users_api.update_last_connection_date(user)
    return authentication.SigninResponse(
        access_token=users_api.create_user_access_token(user),
        refresh_token=create_refresh_token(identity=user.email),
    )
示例#29
0
def list_venue_providers():
    venue_id = request.args.get("venueId")
    if venue_id is None:
        e = ApiErrors()
        e.add_error("venueId",
                    "Vous devez obligatoirement fournir le paramètre venueId")
        return jsonify(e.errors), 400

    vp_query = VenueProvider.query.filter_by(venueId=dehumanize(venue_id))
    return jsonify([
        as_dict(venue_provider, includes=VENUE_PROVIDER_INCLUDES)
        for venue_provider in vp_query.all()
    ])
示例#30
0
def check_activation_codes_expiration_datetime(
    activation_codes_expiration_datetime: Optional[datetime],
    booking_limit_datetime: Optional[datetime],
) -> None:
    if activation_codes_expiration_datetime is None:
        return

    if booking_limit_datetime is None and activation_codes_expiration_datetime is not None:
        errors = ApiErrors()
        errors.add_error(
            "bookingLimitDatetime",
            ("Une date limite de validité a été renseignée. Dans ce cas, il faut également"
             " renseigner une date limite de réservation qui doit être antérieure d'au moins 7 jours."
             ),
        )
        raise errors

    if (booking_limit_datetime is not None
            and activation_codes_expiration_datetime < booking_limit_datetime +
            timedelta(days=7)  # type: ignore[operator]
        ):
        errors = ApiErrors()
        errors.add_error(
            "activationCodesExpirationDatetime",
            ("La date limite de validité des codes d'activation doit être ultérieure"
             " d'au moins 7 jours à la date limite de réservation"),
        )
        raise errors