Example #1
0
        def decorated_endpoint(*args, **kwargs):

            args = list(args)
            if len(args) == 0:
                current_app.logger.warning("Request missing id.")
                return required_info_missing(["id"])
            if len(args) > 1:
                return (
                    dict(
                        status="UNEXPECTED_PARAMS",
                        message="Only expected one parameter (id).",
                    ),
                    400,
                )

            try:
                id = int(args[0])
            except ValueError:
                current_app.logger.warning(
                    "Cannot parse ID argument from request.")
                return required_info_missing(["id"],
                                             "Cannot parse ID arg as int.")

            user: UserModel = UserModel.query.filter_by(
                id=int(id)).one_or_none()

            if user is None:
                raise abort(404, f"User {id} not found")

            if not current_user.has_role("admin"):
                if admins_only or user != current_user:
                    return unauthorized_handler(None, [])

            args = (user, )
            return fn(*args, **kwargs)
Example #2
0
def get(args):
    """List all assets, or the ones owned by a certain user.
    Raise if a non-admin tries to see assets owned by someone else.
    """
    if "owner_id" in args:
        # get_assets ignores owner_id if user is not admin. Here we want to raise a proper auth error.
        if not (current_user.has_role("admin")
                or args["owner_id"] == current_user.id):
            return unauthorized_handler(None, [])
        assets = get_assets(owner_id=int(args["owner_id"]))
    else:
        assets = get_assets()

    return assets_schema.dump(assets), 200
Example #3
0
def post(asset_data):
    """Create new asset"""

    if current_user.has_role("anonymous"):
        return unauthorized_handler(
            None, []
        )  # Disallow edit access, even to own assets TODO: review, such a role should not exist

    asset = AssetModel(**asset_data)
    db.session.add(asset)
    try:
        db.session.commit()
    except IntegrityError as ie:
        return dict(message="Duplicate asset already exists",
                    detail=ie._message()), 400

    return asset_schema.dump(asset), 201
Example #4
0
def patch(db_user: UserModel, user_data: dict):
    """Update a user given its identifier"""
    allowed_fields = [
        "email", "username", "active", "timezone", "flexmeasures_roles"
    ]
    for k, v in [(k, v) for k, v in user_data.items() if k in allowed_fields]:
        # Don't allow users who edit themselves to edit sensitive fields
        if current_user.id == db_user.id and k in ("active",
                                                   "flexmeasures_roles"):
            return unauthorized_handler(None, [])
        setattr(db_user, k, v)
    db.session.add(db_user)
    try:
        db.session.commit()
    except IntegrityError as ie:
        return dict(message="Duplicate user already exists",
                    detail=ie._message()), 400
    return user_schema.dump(db_user), 200
Example #5
0
    def get(self, id: str):
        """GET from /assets/<id> where id can be 'new' (and thus the form for asset creation is shown)"""

        if id == "new":
            if not current_user.has_role("admin"):
                return unauthorized_handler(None, [])

            asset_form = with_options(NewAssetForm())
            return render_flexmeasures_template(
                "crud/asset_new.html",
                asset_form=asset_form,
                msg="",
                map_center=get_center_location(db, user=current_user),
                mapboxAccessToken=current_app.config.get(
                    "MAPBOX_ACCESS_TOKEN", ""),
            )

        get_asset_response = InternalApi().get(
            url_for("flexmeasures_api_v2_0.get_asset", id=id))
        asset_dict = get_asset_response.json()

        asset_form = with_options(AssetForm())

        asset = process_internal_api_response(asset_dict,
                                              int(id),
                                              make_obj=True)
        asset_form.process(data=process_internal_api_response(asset_dict))

        latest_measurement_time_str, asset_plot_html = get_latest_power_as_plot(
            asset)
        return render_flexmeasures_template(
            "crud/asset.html",
            asset=asset,
            asset_form=asset_form,
            msg="",
            latest_measurement_time_str=latest_measurement_time_str,
            asset_plot_html=asset_plot_html,
            mapboxAccessToken=current_app.config.get("MAPBOX_ACCESS_TOKEN",
                                                     ""),
        )
Example #6
0
    def post(self, id: str):
        """POST to /assets/<id>, where id can be 'create' (and thus a new asset is made from POST data)
        Most of the code deals with creating a user for the asset if no existing is chosen.
        """

        asset: Asset = None
        error_msg = ""

        if id == "create":
            asset_form = with_options(NewAssetForm())

            # We make our own auth check here because set_owner might alter the DB;
            # otherwise we trust the API is handling auth.
            if not current_user.has_role("admin"):
                return unauthorized_handler(None, [])
            owner, owner_error = set_owner(asset_form,
                                           create_if_not_exists=True)
            market, market_error = set_market(asset_form)

            if asset_form.asset_type_name.data == "none chosen":
                asset_form.asset_type_name.data = ""

            form_valid = asset_form.validate_on_submit()

            # Fill up the form with useful errors for the user
            if owner_error is not None:
                form_valid = False
                asset_form.owner.errors.append(owner_error)
            if market_error is not None:
                form_valid = False
                asset_form.market_id.errors.append(market_error)

            # Create new asset or return the form for new assets with a message
            if form_valid and owner is not None and market is not None:
                post_asset_response = InternalApi().post(
                    url_for("flexmeasures_api_v2_0.post_assets"),
                    args=asset_form.to_json(),
                    do_not_raise_for=[400],
                )

                if post_asset_response.status_code in (200, 201):
                    asset_dict = post_asset_response.json()
                    asset = process_internal_api_response(
                        asset_dict, int(asset_dict["id"]), make_obj=True)
                    msg = "Creation was successful."
                else:
                    current_app.logger.error(
                        f"Internal asset API call unsuccessful [{post_asset_response.status_code}]: {post_asset_response.text}"
                    )
                    asset_form.process_api_validation_errors(
                        post_asset_response.json())
                    if "message" in post_asset_response.json():
                        error_msg = post_asset_response.json()["message"]
            if asset is None:
                msg = "Cannot create asset. " + error_msg
                return render_flexmeasures_template(
                    "crud/asset_new.html",
                    asset_form=asset_form,
                    msg=msg,
                    map_center=get_center_location(db, user=current_user),
                    mapboxAccessToken=current_app.config.get(
                        "MAPBOX_ACCESS_TOKEN", ""),
                )

        else:
            asset_form = with_options(AssetForm())
            if not asset_form.validate_on_submit():
                return render_flexmeasures_template(
                    "crud/asset_new.html",
                    asset_form=asset_form,
                    msg="Cannot edit asset.",
                    map_center=get_center_location(db, user=current_user),
                    mapboxAccessToken=current_app.config.get(
                        "MAPBOX_ACCESS_TOKEN", ""),
                )
            patch_asset_response = InternalApi().patch(
                url_for("flexmeasures_api_v2_0.patch_asset", id=id),
                args=asset_form.to_json(),
                do_not_raise_for=[400],
            )
            asset_dict = patch_asset_response.json()
            if patch_asset_response.status_code in (200, 201):
                asset = process_internal_api_response(asset_dict,
                                                      int(id),
                                                      make_obj=True)
                msg = "Editing was successful."
            else:
                current_app.logger.error(
                    f"Internal asset API call unsuccessful [{patch_asset_response.status_code}]: {patch_asset_response.text}"
                )
                asset_form.process_api_validation_errors(
                    patch_asset_response.json())
                asset = Asset.query.get(id)

        latest_measurement_time_str, asset_plot_html = get_latest_power_as_plot(
            asset)
        return render_flexmeasures_template(
            "crud/asset.html",
            asset=asset,
            asset_form=asset_form,
            msg=msg,
            latest_measurement_time_str=latest_measurement_time_str,
            asset_plot_html=asset_plot_html,
            mapboxAccessToken=current_app.config.get("MAPBOX_ACCESS_TOKEN",
                                                     ""),
        )