diff --git a/src/octoprint/server/api/printer.py b/src/octoprint/server/api/printer.py index 2f5662d5..f78beba5 100644 --- a/src/octoprint/server/api/printer.py +++ b/src/octoprint/server/api/printer.py @@ -6,6 +6,7 @@ __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agp __copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms of the AGPLv3 License" from flask import request, jsonify, make_response, Response +from flask.exceptions import JSONBadRequest import re from octoprint.settings import settings, valid_boolean_trues @@ -306,7 +307,10 @@ def printerCommand(): if not "application/json" in request.headers["Content-Type"]: return make_response("Expected content type JSON", 400) - data = request.json + try: + data = request.json + except JSONBadRequest: + return make_response("Malformed JSON body in request", 400) parameters = dict() if "parameters" in data.keys(): parameters = data["parameters"] diff --git a/src/octoprint/server/api/printer_profiles.py b/src/octoprint/server/api/printer_profiles.py index 92dadf6a..e7c1e7c8 100644 --- a/src/octoprint/server/api/printer_profiles.py +++ b/src/octoprint/server/api/printer_profiles.py @@ -9,6 +9,7 @@ __copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms import copy from flask import jsonify, make_response, request, url_for +from flask.exceptions import JSONBadRequest from octoprint.server.api import api, NO_CONTENT from octoprint.server.util.flask import restricted_access @@ -27,11 +28,15 @@ def printerProfilesList(): @restricted_access def printerProfilesAdd(): if not "application/json" in request.headers["Content-Type"]: - return None, None, make_response("Expected content-type JSON", 400) + return make_response("Expected content-type JSON", 400) + + try: + json_data = request.json + except JSONBadRequest: + return make_response("Malformed JSON body in request", 400) - json_data = request.json if not "profile" in json_data: - return None, None, make_response("No profile included in request", 400) + return make_response("No profile included in request", 400) base_profile = printerProfileManager.get_default() if "basedOn" in json_data and isinstance(json_data["basedOn"], basestring): @@ -82,11 +87,15 @@ def printerProfilesDelete(identifier): @restricted_access def printerProfilesUpdate(identifier): if not "application/json" in request.headers["Content-Type"]: - return None, None, make_response("Expected content-type JSON", 400) + return make_response("Expected content-type JSON", 400) + + try: + json_data = request.json + except JSONBadRequest: + return make_response("Malformed JSON body in request", 400) - json_data = request.json if not "profile" in json_data: - make_response("No profile included in request", 400) + return make_response("No profile included in request", 400) profile = printerProfileManager.get(identifier) if profile is None: diff --git a/src/octoprint/server/api/settings.py b/src/octoprint/server/api/settings.py index 7a5bb860..9cd2d8cb 100644 --- a/src/octoprint/server/api/settings.py +++ b/src/octoprint/server/api/settings.py @@ -7,7 +7,8 @@ __copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms import logging -from flask import request, jsonify +from flask import request, jsonify, make_response +from flask.exceptions import JSONBadRequest from octoprint.events import eventManager, Events from octoprint.settings import settings @@ -116,102 +117,107 @@ def getSettings(): @restricted_access @admin_permission.require(403) def setSettings(): - if "application/json" in request.headers["Content-Type"]: + if not "application/json" in request.headers["Content-Type"]: + return make_response("Expected content-type JSON", 400) + + try: data = request.json - s = settings() + except JSONBadRequest: + return make_response("Malformed JSON body in request", 400) + s = settings() - if "api" in data.keys(): - if "enabled" in data["api"].keys(): s.setBoolean(["api", "enabled"], data["api"]["enabled"]) - if "key" in data["api"].keys(): s.set(["api", "key"], data["api"]["key"], True) - if "allowCrossOrigin" in data["api"].keys(): s.setBoolean(["api", "allowCrossOrigin"], data["api"]["allowCrossOrigin"]) + if "api" in data.keys(): + if "enabled" in data["api"].keys(): s.setBoolean(["api", "enabled"], data["api"]["enabled"]) + if "key" in data["api"].keys(): s.set(["api", "key"], data["api"]["key"], True) + if "allowCrossOrigin" in data["api"].keys(): s.setBoolean(["api", "allowCrossOrigin"], data["api"]["allowCrossOrigin"]) - if "appearance" in data.keys(): - if "name" in data["appearance"].keys(): s.set(["appearance", "name"], data["appearance"]["name"]) - if "color" in data["appearance"].keys(): s.set(["appearance", "color"], data["appearance"]["color"]) + if "appearance" in data.keys(): + if "name" in data["appearance"].keys(): s.set(["appearance", "name"], data["appearance"]["name"]) + if "color" in data["appearance"].keys(): s.set(["appearance", "color"], data["appearance"]["color"]) - if "printer" in data.keys(): - if "defaultExtrusionLength" in data["printer"]: s.setInt(["printerParameters", "defaultExtrusionLength"], data["printer"]["defaultExtrusionLength"]) + if "printer" in data.keys(): + if "defaultExtrusionLength" in data["printer"]: s.setInt(["printerParameters", "defaultExtrusionLength"], data["printer"]["defaultExtrusionLength"]) - if "webcam" in data.keys(): - if "streamUrl" in data["webcam"].keys(): s.set(["webcam", "stream"], data["webcam"]["streamUrl"]) - if "snapshotUrl" in data["webcam"].keys(): s.set(["webcam", "snapshot"], data["webcam"]["snapshotUrl"]) - if "ffmpegPath" in data["webcam"].keys(): s.set(["webcam", "ffmpeg"], data["webcam"]["ffmpegPath"]) - if "bitrate" in data["webcam"].keys(): s.set(["webcam", "bitrate"], data["webcam"]["bitrate"]) - if "watermark" in data["webcam"].keys(): s.setBoolean(["webcam", "watermark"], data["webcam"]["watermark"]) - if "flipH" in data["webcam"].keys(): s.setBoolean(["webcam", "flipH"], data["webcam"]["flipH"]) - if "flipV" in data["webcam"].keys(): s.setBoolean(["webcam", "flipV"], data["webcam"]["flipV"]) + if "webcam" in data.keys(): + if "streamUrl" in data["webcam"].keys(): s.set(["webcam", "stream"], data["webcam"]["streamUrl"]) + if "snapshotUrl" in data["webcam"].keys(): s.set(["webcam", "snapshot"], data["webcam"]["snapshotUrl"]) + if "ffmpegPath" in data["webcam"].keys(): s.set(["webcam", "ffmpeg"], data["webcam"]["ffmpegPath"]) + if "bitrate" in data["webcam"].keys(): s.set(["webcam", "bitrate"], data["webcam"]["bitrate"]) + if "watermark" in data["webcam"].keys(): s.setBoolean(["webcam", "watermark"], data["webcam"]["watermark"]) + if "flipH" in data["webcam"].keys(): s.setBoolean(["webcam", "flipH"], data["webcam"]["flipH"]) + if "flipV" in data["webcam"].keys(): s.setBoolean(["webcam", "flipV"], data["webcam"]["flipV"]) - if "feature" in data.keys(): - if "gcodeViewer" in data["feature"].keys(): s.setBoolean(["gcodeViewer", "enabled"], data["feature"]["gcodeViewer"]) - if "temperatureGraph" in data["feature"].keys(): s.setBoolean(["feature", "temperatureGraph"], data["feature"]["temperatureGraph"]) - if "waitForStart" in data["feature"].keys(): s.setBoolean(["feature", "waitForStartOnConnect"], data["feature"]["waitForStart"]) - if "alwaysSendChecksum" in data["feature"].keys(): s.setBoolean(["feature", "alwaysSendChecksum"], data["feature"]["alwaysSendChecksum"]) - if "sdSupport" in data["feature"].keys(): s.setBoolean(["feature", "sdSupport"], data["feature"]["sdSupport"]) - if "sdAlwaysAvailable" in data["feature"].keys(): s.setBoolean(["feature", "sdAlwaysAvailable"], data["feature"]["sdAlwaysAvailable"]) - if "swallowOkAfterResend" in data["feature"].keys(): s.setBoolean(["feature", "swallowOkAfterResend"], data["feature"]["swallowOkAfterResend"]) - if "repetierTargetTemp" in data["feature"].keys(): s.setBoolean(["feature", "repetierTargetTemp"], data["feature"]["repetierTargetTemp"]) - if "keyboardControl" in data["feature"].keys(): s.setBoolean(["feature", "keyboardControl"], data["feature"]["keyboardControl"]) + if "feature" in data.keys(): + if "gcodeViewer" in data["feature"].keys(): s.setBoolean(["gcodeViewer", "enabled"], data["feature"]["gcodeViewer"]) + if "temperatureGraph" in data["feature"].keys(): s.setBoolean(["feature", "temperatureGraph"], data["feature"]["temperatureGraph"]) + if "waitForStart" in data["feature"].keys(): s.setBoolean(["feature", "waitForStartOnConnect"], data["feature"]["waitForStart"]) + if "alwaysSendChecksum" in data["feature"].keys(): s.setBoolean(["feature", "alwaysSendChecksum"], data["feature"]["alwaysSendChecksum"]) + if "sdSupport" in data["feature"].keys(): s.setBoolean(["feature", "sdSupport"], data["feature"]["sdSupport"]) + if "sdAlwaysAvailable" in data["feature"].keys(): s.setBoolean(["feature", "sdAlwaysAvailable"], data["feature"]["sdAlwaysAvailable"]) + if "swallowOkAfterResend" in data["feature"].keys(): s.setBoolean(["feature", "swallowOkAfterResend"], data["feature"]["swallowOkAfterResend"]) + if "repetierTargetTemp" in data["feature"].keys(): s.setBoolean(["feature", "repetierTargetTemp"], data["feature"]["repetierTargetTemp"]) + if "keyboardControl" in data["feature"].keys(): s.setBoolean(["feature", "keyboardControl"], data["feature"]["keyboardControl"]) - if "serial" in data.keys(): - if "autoconnect" in data["serial"].keys(): s.setBoolean(["serial", "autoconnect"], data["serial"]["autoconnect"]) - if "port" in data["serial"].keys(): s.set(["serial", "port"], data["serial"]["port"]) - if "baudrate" in data["serial"].keys(): s.setInt(["serial", "baudrate"], data["serial"]["baudrate"]) - if "timeoutConnection" in data["serial"].keys(): s.setFloat(["serial", "timeout", "connection"], data["serial"]["timeoutConnection"]) - if "timeoutDetection" in data["serial"].keys(): s.setFloat(["serial", "timeout", "detection"], data["serial"]["timeoutDetection"]) - if "timeoutCommunication" in data["serial"].keys(): s.setFloat(["serial", "timeout", "communication"], data["serial"]["timeoutCommunication"]) - if "timeoutTemperature" in data["serial"].keys(): s.setFloat(["serial", "timeout", "temperature"], data["serial"]["timeoutTemperature"]) - if "timeoutSdStatus" in data["serial"].keys(): s.setFloat(["serial", "timeout", "sdStatus"], data["serial"]["timeoutSdStatus"]) + if "serial" in data.keys(): + if "autoconnect" in data["serial"].keys(): s.setBoolean(["serial", "autoconnect"], data["serial"]["autoconnect"]) + if "port" in data["serial"].keys(): s.set(["serial", "port"], data["serial"]["port"]) + if "baudrate" in data["serial"].keys(): s.setInt(["serial", "baudrate"], data["serial"]["baudrate"]) + if "timeoutConnection" in data["serial"].keys(): s.setFloat(["serial", "timeout", "connection"], data["serial"]["timeoutConnection"]) + if "timeoutDetection" in data["serial"].keys(): s.setFloat(["serial", "timeout", "detection"], data["serial"]["timeoutDetection"]) + if "timeoutCommunication" in data["serial"].keys(): s.setFloat(["serial", "timeout", "communication"], data["serial"]["timeoutCommunication"]) + if "timeoutTemperature" in data["serial"].keys(): s.setFloat(["serial", "timeout", "temperature"], data["serial"]["timeoutTemperature"]) + if "timeoutSdStatus" in data["serial"].keys(): s.setFloat(["serial", "timeout", "sdStatus"], data["serial"]["timeoutSdStatus"]) - oldLog = s.getBoolean(["serial", "log"]) - if "log" in data["serial"].keys(): s.setBoolean(["serial", "log"], data["serial"]["log"]) - if oldLog and not s.getBoolean(["serial", "log"]): - # disable debug logging to serial.log - logging.getLogger("SERIAL").debug("Disabling serial logging") - logging.getLogger("SERIAL").setLevel(logging.CRITICAL) - elif not oldLog and s.getBoolean(["serial", "log"]): - # enable debug logging to serial.log - logging.getLogger("SERIAL").setLevel(logging.DEBUG) - logging.getLogger("SERIAL").debug("Enabling serial logging") + oldLog = s.getBoolean(["serial", "log"]) + if "log" in data["serial"].keys(): s.setBoolean(["serial", "log"], data["serial"]["log"]) + if oldLog and not s.getBoolean(["serial", "log"]): + # disable debug logging to serial.log + logging.getLogger("SERIAL").debug("Disabling serial logging") + logging.getLogger("SERIAL").setLevel(logging.CRITICAL) + elif not oldLog and s.getBoolean(["serial", "log"]): + # enable debug logging to serial.log + logging.getLogger("SERIAL").setLevel(logging.DEBUG) + logging.getLogger("SERIAL").debug("Enabling serial logging") - if "folder" in data.keys(): - if "uploads" in data["folder"].keys(): s.setBaseFolder("uploads", data["folder"]["uploads"]) - if "timelapse" in data["folder"].keys(): s.setBaseFolder("timelapse", data["folder"]["timelapse"]) - if "timelapseTmp" in data["folder"].keys(): s.setBaseFolder("timelapse_tmp", data["folder"]["timelapseTmp"]) - if "logs" in data["folder"].keys(): s.setBaseFolder("logs", data["folder"]["logs"]) - if "watched" in data["folder"].keys(): s.setBaseFolder("watched", data["folder"]["watched"]) + if "folder" in data.keys(): + if "uploads" in data["folder"].keys(): s.setBaseFolder("uploads", data["folder"]["uploads"]) + if "timelapse" in data["folder"].keys(): s.setBaseFolder("timelapse", data["folder"]["timelapse"]) + if "timelapseTmp" in data["folder"].keys(): s.setBaseFolder("timelapse_tmp", data["folder"]["timelapseTmp"]) + if "logs" in data["folder"].keys(): s.setBaseFolder("logs", data["folder"]["logs"]) + if "watched" in data["folder"].keys(): s.setBaseFolder("watched", data["folder"]["watched"]) - if "temperature" in data.keys(): - if "profiles" in data["temperature"].keys(): s.set(["temperature", "profiles"], data["temperature"]["profiles"]) + if "temperature" in data.keys(): + if "profiles" in data["temperature"].keys(): s.set(["temperature", "profiles"], data["temperature"]["profiles"]) - if "terminalFilters" in data.keys(): - s.set(["terminalFilters"], data["terminalFilters"]) + if "terminalFilters" in data.keys(): + s.set(["terminalFilters"], data["terminalFilters"]) - if "system" in data.keys(): - if "actions" in data["system"].keys(): s.set(["system", "actions"], data["system"]["actions"]) - if "events" in data["system"].keys(): s.set(["system", "events"], data["system"]["events"]) + if "system" in data.keys(): + if "actions" in data["system"].keys(): s.set(["system", "actions"], data["system"]["actions"]) + if "events" in data["system"].keys(): s.set(["system", "events"], data["system"]["events"]) - cura = data.get("cura", None) - if cura: - path = cura.get("path") - if path: - s.set(["cura", "path"], path) + cura = data.get("cura", None) + if cura: + path = cura.get("path") + if path: + s.set(["cura", "path"], path) - config = cura.get("config") - if config: - s.set(["cura", "config"], config) + config = cura.get("config") + if config: + s.set(["cura", "config"], config) - # Enabled is a boolean so we cannot check that we have a result - enabled = cura.get("enabled") - s.setBoolean(["cura", "enabled"], enabled) + # Enabled is a boolean so we cannot check that we have a result + enabled = cura.get("enabled") + s.setBoolean(["cura", "enabled"], enabled) - if "plugins" in data: - for name, plugin in octoprint.plugin.plugin_manager().get_implementations(octoprint.plugin.SettingsPlugin).items(): - if name in data["plugins"]: - plugin.on_settings_save(data["plugins"][name]) + if "plugins" in data: + for name, plugin in octoprint.plugin.plugin_manager().get_implementations(octoprint.plugin.SettingsPlugin).items(): + if name in data["plugins"]: + plugin.on_settings_save(data["plugins"][name]) - if s.save(): - eventManager().fire(Events.SETTINGS_UPDATED) + if s.save(): + eventManager().fire(Events.SETTINGS_UPDATED) return getSettings() diff --git a/src/octoprint/server/api/slicing.py b/src/octoprint/server/api/slicing.py index 6d03ced3..28ab35e9 100644 --- a/src/octoprint/server/api/slicing.py +++ b/src/octoprint/server/api/slicing.py @@ -6,6 +6,7 @@ __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agp __copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms of the AGPLv3 License" from flask import request, jsonify, make_response, url_for +from flask.exceptions import JSONBadRequest from octoprint.server import slicingManager from octoprint.server.util.flask import restricted_access @@ -58,7 +59,10 @@ def slicingAddSlicerProfile(slicer, name): if not "application/json" in request.headers["Content-Type"]: return None, None, make_response("Expected content-type JSON", 400) - json_data = request.json + try: + json_data = request.json + except JSONBadRequest: + return make_response("Malformed JSON body in request", 400) data = dict() display_name = None @@ -90,7 +94,10 @@ def slicingPatchSlicerProfile(slicer, name): if not profile: return make_response("Profile not found", 404) - json_data = request.json + try: + json_data = request.json + except JSONBadRequest: + return make_response("Malformed JSON body in request", 400) data = dict() display_name = None diff --git a/src/octoprint/server/api/users.py b/src/octoprint/server/api/users.py index 2b6f4022..cf2e7fd3 100644 --- a/src/octoprint/server/api/users.py +++ b/src/octoprint/server/api/users.py @@ -6,6 +6,7 @@ __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agp __copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms of the AGPLv3 License" from flask import request, jsonify, abort, make_response +from flask.exceptions import JSONBadRequest from flask.ext.login import current_user import octoprint.users as users @@ -35,21 +36,26 @@ def addUser(): if userManager is None: return jsonify(SUCCESS) - if "application/json" in request.headers["Content-Type"]: + if not "application/json" in request.headers["Content-Type"]: + return make_response("Expected content-type JSON", 400) + + try: data = request.json + except JSONBadRequest: + return make_response("Malformed JSON body in request", 400) - name = data["name"] - password = data["password"] - active = data["active"] + name = data["name"] + password = data["password"] + active = data["active"] - roles = ["user"] - if "admin" in data.keys() and data["admin"]: - roles.append("admin") + roles = ["user"] + if "admin" in data.keys() and data["admin"]: + roles.append("admin") - try: - userManager.addUser(name, password, active, roles) - except users.UserAlreadyExists: - abort(409) + try: + userManager.addUser(name, password, active, roles) + except users.UserAlreadyExists: + abort(409) return getUsers() @@ -78,18 +84,23 @@ def updateUser(username): user = userManager.findUser(username) if user is not None: - if "application/json" in request.headers["Content-Type"]: + if not "application/json" in request.headers["Content-Type"]: + return make_response("Expected content-type JSON", 400) + + try: data = request.json + except JSONBadRequest: + return make_response("Malformed JSON body in request", 400) - # change roles - roles = ["user"] - if "admin" in data.keys() and data["admin"]: - roles.append("admin") - userManager.changeUserRoles(username, roles) + # change roles + roles = ["user"] + if "admin" in data.keys() and data["admin"]: + roles.append("admin") + userManager.changeUserRoles(username, roles) - # change activation - if "active" in data.keys(): - userManager.changeUserActivation(username, data["active"]) + # change activation + if "active" in data.keys(): + userManager.changeUserActivation(username, data["active"]) return getUsers() else: abort(404) @@ -116,13 +127,22 @@ def changePasswordForUser(username): return jsonify(SUCCESS) if current_user is not None and not current_user.is_anonymous() and (current_user.get_name() == username or current_user.is_admin()): - if "application/json" in request.headers["Content-Type"]: + if not "application/json" in request.headers["Content-Type"]: + return make_response("Expected content-type JSON", 400) + + try: data = request.json - if "password" in data.keys() and data["password"]: - try: - userManager.changeUserPassword(username, data["password"]) - except users.UnknownUser: - return make_response(("Unknown user: %s" % username, 404, [])) + except JSONBadRequest: + return make_response("Malformed JSON body in request", 400) + + if not "password" in data.keys() or not data["password"]: + return make_response("password is missing from request", 400) + + try: + userManager.changeUserPassword(username, data["password"]) + except users.UnknownUser: + return make_response(("Unknown user: %s" % username, 404, [])) + return jsonify(SUCCESS) else: return make_response(("Forbidden", 403, []))