Source code for server.views_api.device

from flask import Blueprint, request, jsonify, current_app
from flask_socketio import disconnect

from .. import db, socketio, connections_devices
from ..models import Device
from .validations import get_device_metadata_errors, get_password_errors
from ..functions import submit_device
from ..protections import admin_required, content_json

import itertools

bp = Blueprint("device_api", __name__, url_prefix="/api")


[docs] @bp.route("/device", methods=["GET"]) @admin_required def get_all_devices(): devices = Device.query.all() return jsonify(devices=[s.to_dict() for s in devices]), 200
[docs] @bp.route("/device/<id_>", methods=["GET"]) @admin_required def get_device(id_): device = Device.by_id(id_) if not device: return jsonify(message="Device not found."), 404 return jsonify(device=device.to_dict()), 200
[docs] @bp.route("/device", methods=["POST"]) @admin_required @content_json(["name", "password", "ipv4"]) def create_device(): data = request.get_json() metadata = { "name": data["name"], "ipv4": data["ipv4"], } password = data["password"] errors = dict(itertools.chain( get_device_metadata_errors(**metadata), get_password_errors(password) )) if Device.by_name(data["name"]): errors["name"] = "Device name conflict." if not errors: device = submit_device(password=password, **metadata) return jsonify(message="Device '{}' created.".format(device.name), device=device.to_dict()), 201 else: return jsonify(message="Device creation failed.", errors=errors), 400
[docs] @bp.route("/device/<id_>/password", methods=["PUT"]) @admin_required @content_json(["new_password"]) def change_password(id_): # TODO: SECURITY: Changing device password does not require old pass => ok? device = Device.by_id(id_) if not device: return jsonify(message="Device not found."), 404 data = request.get_json() errors = dict(get_password_errors(data["new_password"])) if not errors: device.set_password(data["new_password"]) db.session.commit() return jsonify(message="Device password changed successfully."), 200 else: return jsonify(message="Device password change failed.", errors=errors), 400
[docs] @bp.route("/device/<id_>", methods=["DELETE"]) @admin_required def delete_device(id_): device = Device.by_id(id_) if not device: return jsonify(message="Device not found."), 404 db.session.delete(device) db.session.commit() # It is always just one sid, but lets insure against future changes for sid in connections_devices.by_pubid.get(id_, []): disconnect(sid, "/device") return jsonify(message="Device '{}' has been deleted.".format(device.name)), 200
[docs] @bp.route("/device/<id_>", methods=["PATCH"]) @admin_required @content_json([]) def modify_device(id_): device = Device.by_id(id_) if not device: return jsonify(message="Device not found."), 404 data = request.get_json() data = { "name": data.get("name", device.name), "ipv4": data.get("ipv4", device.ipv4), } errors = dict(get_device_metadata_errors(**data)) if data["name"] != device.name and Device.by_name(data["name"]): errors["name"] = "Device name conflict." if not errors: submit_device(password=None, **data, replace_device=device) return jsonify(message="Device '{}' modified.".format(device.name)), 200 else: return jsonify(message="Device modification failed.", errors=errors), 400
[docs] @bp.route("/device/<id_>/disconnect", methods=["POST"]) @admin_required def disconnect_device(id_): """Api endpoint to disconnect and stop the device. Convenient when device crashes and leaves an old connection hanging, which in turn blocks any new connection of the same device. If the device is actually connected it gets restartad (similarly to server restart using ``/api/shutdown``, see -> deployment/systemd). """ device = Device.by_id(id_) if not device: return jsonify(message="Device not found."), 404 sids = connections_devices.by_pubid.get(id_, []) if len(sids) == 0: return jsonify(message="Device not connected."), 404 current_app.logger.info("Disconnect request for device '{}'.".format(device.name)) # It is always just one sid, but lets insure against future changes for sid in sids: socketio.emit("bye", namespace="/device", to=sid) disconnect(sid, "/device") return jsonify(message="Device '{}' has been disconnected.".format(device.name)), 200