123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- """
- FlexMeasures API routes and implementations.
- """
- from flask import Flask, Blueprint, request
- from flask_security.utils import verify_password
- from flask_json import as_json
- from flask_login import current_user
- from sqlalchemy.exc import IntegrityError
- from sqlalchemy import select
- from flexmeasures.data import db
- from flexmeasures import __version__ as flexmeasures_version
- from flexmeasures.api.common.utils.api_utils import catch_timed_belief_replacements
- from flexmeasures.data.models.user import User
- from flexmeasures.api.common.utils.args_parsing import (
- validation_error_handler,
- )
- from flexmeasures.api.common.responses import invalid_sender
- from flexmeasures.data.schemas.utils import FMValidationError
- # The api blueprint. It is registered with the Flask app (see app.py)
- flexmeasures_api = Blueprint("flexmeasures_api", __name__)
- @flexmeasures_api.route("/requestAuthToken", methods=["POST"])
- @as_json
- def request_auth_token():
- """API endpoint to get a fresh authentication access token. Be aware that this fresh token has a limited lifetime
- (which depends on the current system setting SECURITY_TOKEN_MAX_AGE).
- Pass the `email` parameter to identify the user.
- Pass the `password` parameter to authenticate the user (if not already authenticated in current session)
- .. :quickref: Public; Obtain an authentication token
- """
- """
- The login of flask security returns the auth token, as well, but we'd like to
- * skip authentication if the user is authenticated already
- * be exempt from csrf protection (this is a JSON-only endpoint)
- * use a more fitting name inside the api namespace
- * return the information in a nicer structure
- """
- try:
- if not request.is_json:
- return {"errors": ["Content-type of request must be application/json"]}, 400
- if "email" not in request.json:
- return {"errors": ["Please provide the 'email' parameter."]}, 400
- email = request.json["email"]
- if current_user.is_authenticated and current_user.email == email:
- user = current_user
- else:
- user = db.session.execute(
- select(User).filter_by(email=email)
- ).scalar_one_or_none()
- if not user:
- return (
- {"errors": ["User with email '%s' does not exist" % email]},
- 404,
- )
- if "password" not in request.json:
- return {"errors": ["Please provide the 'password' parameter."]}, 400
- if not verify_password(request.json["password"], user.password):
- return {"errors": ["User password does not match."]}, 401
- token = user.get_auth_token()
- return {"auth_token": token, "user_id": user.id}
- except Exception as e:
- return {"errors": [str(e)]}, 500
- @flexmeasures_api.route("/", methods=["GET"])
- @as_json
- def get_versions() -> dict:
- """Public endpoint to list API versions.
- .. :quickref: Public; List available API versions
- """
- response = {
- "message": "For these API versions a public endpoint is available, listing its service. For example: "
- "/api/v3_0. An authentication token can be requested at: "
- "/api/requestAuthToken",
- "versions": ["v3_0"],
- "flexmeasures_version": flexmeasures_version,
- }
- return response
- def register_at(app: Flask):
- """This can be used to register this blueprint together with other api-related things"""
- # handle API specific errors
- app.register_error_handler(FMValidationError, validation_error_handler)
- app.register_error_handler(IntegrityError, catch_timed_belief_replacements)
- app.unauthorized_handler_api = invalid_sender
- app.register_blueprint(
- flexmeasures_api, url_prefix="/api"
- ) # now registering the blueprint will affect all endpoints
- # Load API endpoints for internal operations
- from flexmeasures.api.common import register_at as ops_register_at
- ops_register_at(app)
- # Load API endpoints for play mode
- if app.config.get("FLEXMEASURES_MODE", "") == "play":
- from flexmeasures.api.play import register_at as play_register_at
- play_register_at(app)
- # Load all versions of the API functionality
- from flexmeasures.api.v3_0 import register_at as v3_0_register_at
- from flexmeasures.api.dev import register_at as dev_register_at
- from flexmeasures.api.sunset import register_at as sunset_register_at
- v3_0_register_at(app)
- dev_register_at(app)
- sunset_register_at(app)
|