From 3f3b45a27b5b2f4b8913ab5308da33c41bc3531e Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sat, 23 Jul 2022 13:44:20 +0200 Subject: [PATCH] sim-rest-server: Render error messages as JSON Let's make sure even error messages are returned in JSON format. While at it, also reduce some code duplication between the 'auth' and 'info' route handlers by using the klein handle_errors decorator instead of manual exception catching. Change-Id: I1e0364e28ba7ce7451993f57c8228f9a7ade6b0e Closes: OS#5607 --- contrib/sim-rest-server.py | 118 ++++++++++++++++++++----------------- 1 file changed, 64 insertions(+), 54 deletions(-) diff --git a/contrib/sim-rest-server.py b/contrib/sim-rest-server.py index a31aeed3..62498b45 100755 --- a/contrib/sim-rest-server.py +++ b/contrib/sim-rest-server.py @@ -21,7 +21,7 @@ import json import sys import argparse -from klein import run, route +from klein import Klein from pySim.transport import ApduTracer from pySim.transport.pcsc import PcscSimLink @@ -50,78 +50,87 @@ def connect_to_card(slot_nr:int): return tp, scc, card +class ApiError: + def __init__(self, msg:str): + self.msg = msg + + def __str__(self): + return json.dumps({'error': {'message':self.msg}}) + + def set_headers(request): request.setHeader('Content-Type', 'application/json') -@route('/sim-auth-api/v1/slot/') -def auth(request, slot): - """REST API endpoint for performing authentication against a USIM. - Expects a JSON body containing RAND and AUTN. - Returns a JSON body containing RES, CK, IK and Kc.""" - try: - # there are two hex-string JSON parameters in the body: rand and autn - content = json.loads(request.content.read()) - rand = content['rand'] - autn = content['autn'] - except: - request.setResponseCode(400) - return "Malformed Request" +class SimRestServer: + app = Klein() - try: - tp, scc, card = connect_to_card(slot) - except ReaderError: - request.setResponseCode(404) - return "Specified SIM Slot doesn't exist" - except ProtocolError: - request.setResponseCode(500) - return "Error" - except NoCardError: + @app.handle_errors(NoCardError) + def no_card_error(self, request, failure): + set_headers(request) request.setResponseCode(410) - return "No SIM card inserted in slot" + return str(ApiError("No SIM card inserted in slot")) + + @app.handle_errors(ReaderError) + def reader_error(self, request, failure): + set_headers(request) + request.setResponseCode(404) + return str(ApiError("Reader Error: Specified SIM Slot doesn't exist")) + + @app.handle_errors(ProtocolError) + def protocol_error(self, request, failure): + set_headers(request) + request.setResponseCode(500) + return str(ApiError("Protocol Error")) + + @app.handle_errors(SwMatchError) + def sw_match_error(self, request, failure): + set_headers(request) + request.setResponseCode(500) + return str(ApiError("Card Communication Error %s" % failure)) + + + @app.route('/sim-auth-api/v1/slot/') + def auth(self, request, slot): + """REST API endpoint for performing authentication against a USIM. + Expects a JSON body containing RAND and AUTN. + Returns a JSON body containing RES, CK, IK and Kc.""" + try: + # there are two hex-string JSON parameters in the body: rand and autn + content = json.loads(request.content.read()) + rand = content['rand'] + autn = content['autn'] + except: + set_headers(request) + request.setResponseCode(400) + return str(ApiError("Malformed Request")) + + tp, scc, card = connect_to_card(slot) - try: card.select_adf_by_aid(adf='usim') res, sw = scc.authenticate(rand, autn) - except SwMatchError as e: - request.setResponseCode(500) - return "Communication Error %s" % e - tp.disconnect() + tp.disconnect() - set_headers(request) - return json.dumps(res, indent=4) + set_headers(request) + return json.dumps(res, indent=4) -@route('/sim-info-api/v1/slot/') -def info(request, slot): - """REST API endpoint for obtaining information about an USIM. - Expects empty body in request. - Returns a JSON body containing ICCID, IMSI.""" + @app.route('/sim-info-api/v1/slot/') + def info(self, request, slot): + """REST API endpoint for obtaining information about an USIM. + Expects empty body in request. + Returns a JSON body containing ICCID, IMSI.""" - try: tp, scc, card = connect_to_card(slot) - except ReaderError: - request.setResponseCode(404) - return "Specified SIM Slot doesn't exist" - except ProtocolError: - request.setResponseCode(500) - return "Error" - except NoCardError: - request.setResponseCode(410) - return "No SIM card inserted in slot" - try: card.select_adf_by_aid(adf='usim') iccid, sw = card.read_iccid() imsi, sw = card.read_imsi() res = {"imsi": imsi, "iccid": iccid } - except SwMatchError as e: - request.setResponseCode(500) - return "Communication Error %s" % e - tp.disconnect() + tp.disconnect() - set_headers(request) - return json.dumps(res, indent=4) + set_headers(request) + return json.dumps(res, indent=4) def main(argv): @@ -132,7 +141,8 @@ def main(argv): args = parser.parse_args() - run(args.host, args.port) + srr = SimRestServer() + srr.app.run(args.host, args.port) if __name__ == "__main__": main(sys.argv)