Attention is currently required from: dexter.
Hello Jenkins Builder, laforge,
I'd like you to reexamine a change. Please visit
https://gerrit.osmocom.org/c/onomondo-eim/+/43449?usp=email
to look at the new patch set (#2).
The following approvals got outdated and were removed:
Verified+1 by Jenkins Builder
The change is no longer submittable: Verified is unsatisfied now.
Change subject: esipa_asn_handler: add TODO about missing download options
......................................................................
esipa_asn_handler: add TODO about missing download options
SGP.32 also defines other download trigger options next to the
commonly used activationCode method. Let's add a related TODO
to the code.
Related: SYS#8100
Change-Id: I5e72416284027731627c264fa81c62a4a9a0d407
---
M src/esipa_asn1_handler.erl
1 file changed, 3 insertions(+), 0 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/onomondo-eim refs/changes/49/43449/2
--
To view, visit https://gerrit.osmocom.org/c/onomondo-eim/+/43449?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: newpatchset
Gerrit-Project: onomondo-eim
Gerrit-Branch: master
Gerrit-Change-Id: I5e72416284027731627c264fa81c62a4a9a0d407
Gerrit-Change-Number: 43449
Gerrit-PatchSet: 2
Gerrit-Owner: dexter <pmaier(a)sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <laforge(a)osmocom.org>
Gerrit-Attention: dexter <pmaier(a)sysmocom.de>
dexter has submitted this change. ( https://gerrit.osmocom.org/c/onomondo-eim/+/43142?usp=email )
Change subject: contrib: restructure REST API usage examples (tryme-scripts)
......................................................................
contrib: restructure REST API usage examples (tryme-scripts)
The so called tryme scripts are using a very clumsy method to set
the varying parameters (tryme.cfg). The files also mix with other
unrelated files in the contrib directory. There is also a lot of
distracting output displayed which complicates the usage of the
examples even further. This patch fixes those shortcomings.
- put everything related to the usage example into a dedicated
directory.
- renovate restop.py so that it produces more readable output.
- in the tryme_*.sh scripts, avoid output that is not needed.
- cleanup tryme.cfg so that it only contains one set of parameters,
add some helpful comments.
- add functionality to pass the the parameters either via
commandline options or via tryme.cfg, add a parameter to point
to an alternative .cfg file, so that users can use multiple
different configurations.
- update documentation
- cleanup other minor inconsistencies
Related: SYS#8100
Change-Id: I4e42ce85c84b47d3561011083da4a1e54958fd8e
---
A contrib/rest_api_usage_example/getopts.sh
A contrib/rest_api_usage_example/lookup.sh
A contrib/rest_api_usage_example/restop.py
A contrib/rest_api_usage_example/tryme.cfg
R contrib/rest_api_usage_example/tryme_addEim.sh
R contrib/rest_api_usage_example/tryme_configureImmediateEnable.sh
R contrib/rest_api_usage_example/tryme_delete.sh
R contrib/rest_api_usage_example/tryme_deleteEim.sh
R contrib/rest_api_usage_example/tryme_disable.sh
R contrib/rest_api_usage_example/tryme_download.sh
R contrib/rest_api_usage_example/tryme_enable.sh
R contrib/rest_api_usage_example/tryme_euiccDataRequest.sh
R contrib/rest_api_usage_example/tryme_euicc_get_state.sh
R contrib/rest_api_usage_example/tryme_euicc_set_state.sh
R contrib/rest_api_usage_example/tryme_getRAT.sh
R contrib/rest_api_usage_example/tryme_listEim.sh
R contrib/rest_api_usage_example/tryme_listProfileInfo.sh
R contrib/rest_api_usage_example/tryme_updateEim.sh
D contrib/restop.py
D contrib/tryme.cfg
D contrib/tryme_lookup.sh
M doc/examples.md
22 files changed, 252 insertions(+), 239 deletions(-)
Approvals:
laforge: Looks good to me, but someone else must approve
Jenkins Builder: Verified
dexter: Looks good to me, approved
jolly: Looks good to me, but someone else must approve
diff --git a/contrib/rest_api_usage_example/getopts.sh b/contrib/rest_api_usage_example/getopts.sh
new file mode 100755
index 0000000..b97fb72
--- /dev/null
+++ b/contrib/rest_api_usage_example/getopts.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+
+EID=""
+AC="(none)"
+ICCID="(none)"
+CFG_FILE="tryme.cfg"
+
+while getopts "c:e:a:i:h" opt; do
+ case $opt in
+ c)
+ CFG_FILE=$OPTARG
+ ;;
+ e)
+ EID=$OPTARG
+ CFG_FILE=""
+ ;;
+ a)
+ AC=$OPTARG
+ CFG_FILE=""
+ ;;
+ i)
+ ICCID=$OPTARG
+ CFG_FILE=""
+ ;;
+ h)
+ echo "Usage: $0 -c path/to/my/tryme.cfg"
+ echo " $0 -e EID -a AC -i ICCID"
+ exit 0
+ ;;
+ \?)
+ echo "invalid option: -$OPTARG"
+ ;;
+ :)
+ echo "option -$OPTARG requires an argument."
+ ;;
+ esac
+done
+
+if [ -n "$CFG_FILE" ]; then
+ echo "sourcing parameters from config file: $CFG_FILE"
+ source $CFG_FILE
+fi
+
+if [ -z "$EID" ]; then
+ echo "error: no EID (mandatory) specified!"
+ exit 1
+fi
+
+echo "parameters:"
+echo "EID: $EID"
+echo "ICCID: $ICCID"
+echo "AC: $AC"
diff --git a/contrib/rest_api_usage_example/lookup.sh b/contrib/rest_api_usage_example/lookup.sh
new file mode 100755
index 0000000..988f5a3
--- /dev/null
+++ b/contrib/rest_api_usage_example/lookup.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+if [[ $# == 2 ]]; then
+ sleep 5
+ while true; do
+ ./restop.py -l -f $1 -r $2 > /dev/null
+ sleep 5
+ done
+else
+ echo "Usage: $0 facility resource-id"
+fi
+
+
+
diff --git a/contrib/rest_api_usage_example/restop.py b/contrib/rest_api_usage_example/restop.py
new file mode 100755
index 0000000..cf31e4d
--- /dev/null
+++ b/contrib/rest_api_usage_example/restop.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2025 Onomondo ApS & sysmocom - s.f.m.c. GmbH. All rights reserved.
+# SPDX-License-Identifier: AGPL-3.0-only
+# Author: Philipp Maier <pmaier(a)sysmocom.de> / sysmocom - s.f.m.c. GmbH
+
+import sys
+import argparse
+import json
+import requests
+import pprint
+import textwrap
+
+DOWNLOAD_DEFAULT='{ "eidValue" : "89882119900000000000000000000005", "order" : {"activationCode" : "1$testsmdpplus1.example.com$OPxLD-UVRuC-jysPI-YkOwT"}}'
+PSMO_DEFAULT='{ "eidValue" : "89882119900000000000000000000005", "order" : [{"psmo" : "enable", "iccid" : "98001032547698103285", "rollback" : false }]}'
+
+req_headers = {
+ 'Content-Type': 'application/json',
+ 'X-Admin-Protocol': 'onomondo/eim/v1.0.0',
+}
+
+def h2b(s) -> bytearray:
+ """convert from a string of hex nibbles to a sequence of bytes"""
+ return bytes.fromhex(s)
+
+def rest_create(host, facility, Json):
+ r = requests.post("http://" + str(host) + "/" + str(facility) + "/create", json=Json, headers=req_headers)
+ return str(r.url)
+
+def rest_lookup(host, facility, ResourceId):
+ r = requests.get("http://" + str(host) + "/" + str(facility) + "/lookup/" + str(ResourceId), headers=req_headers)
+ return r.json()
+
+def rest_delete(host, facility, ResourceId):
+ r = requests.get("http://" + str(host) + "/" + str(facility) + "/delete/" + str(ResourceId), headers=req_headers)
+ return r.json()
+
+def rest_list(host, facility):
+ r = requests.get("http://" + str(host) + "/" + str(facility) + "/list/", headers=req_headers)
+ return r.json()
+
+def main(argv):
+ parser = argparse.ArgumentParser(prog='restop', description='utility to operate on the REST API of onomondo_eim')
+ parser.add_argument("-s", "--host", default="127.0.0.1:8080")
+ parser.add_argument("-f", "--facility", help="REST API facility", default="download")
+ parser.add_argument("-c", "--create", help="creata a new REST resource", action='store_true')
+ parser.add_argument("-l", "--lookup", help="lookup an existing REST resource", action='store_true')
+ parser.add_argument("-d", "--delete", help="delete a no longer needed REST resource", action='store_true')
+ parser.add_argument("-t", "--list", help="list all currently existing REST resources", action='store_true')
+ parser.add_argument("-r", "--resource-id", help="REST resource identifier")
+ parser.add_argument("-j", "--json", help="JSON input")
+ parser.add_argument("-e", "--erlang-debuginfo", help="decode and display erlang debug information", action='store_true')
+
+ args = parser.parse_args()
+ pp = pprint.PrettyPrinter()
+
+ if args.create:
+ print("create on: " + str(args.host), file=sys.stderr)
+ print(" facility: " + str(args.facility), file=sys.stderr)
+ if args.json:
+ args_json = json.loads(str(args.json))
+ else:
+ if args.facility == "download":
+ args_json = json.loads(DOWNLOAD_DEFAULT)
+ elif args.facility == "psmo":
+ args_json = json.loads(PSMO_DEFAULT)
+ args_json_pretty = pp.pformat(args_json)
+ print(" json: " + textwrap.indent(args_json_pretty, " " * 7)[7:], file=sys.stderr)
+ resource_url = rest_create(args.host, args.facility, args_json)
+ print(" resourceId: " + resource_url.rsplit('/')[-1], file=sys.stderr)
+ print(resource_url)
+
+ elif args.lookup:
+ print("lookup on: " + str(args.host), file=sys.stderr)
+ print(" facility: " + str(args.facility), file=sys.stderr)
+ print(" resourceId: " + str(args.resource_id), file=sys.stderr)
+ result_json = rest_lookup(args.host, args.facility, args.resource_id)
+ result_json_pretty = pp.pformat(result_json)
+ print(" json: " + textwrap.indent(result_json_pretty, " " * 7)[7:], file=sys.stderr)
+ if args.erlang_debuginfo and 'debuginfo' in result_json:
+ import erlang
+ debuginfo_hexstr=result_json['debuginfo']
+ debuginfo_bytes=h2b(debuginfo_hexstr)
+ debuginfo_term=erlang.binary_to_term(debuginfo_bytes)
+ debuginfo_pretty = pp.pformat(debuginfo_term)
+ print(" debuginfo: " + textwrap.indent(debuginfo_pretty, " " * 12)[12:], file=sys.stderr)
+ print(json.dumps(result_json))
+
+ elif args.delete:
+ print("delete on: " + str(args.host), file=sys.stderr)
+ print(" facility: " + str(args.facility), file=sys.stderr)
+ print(" resourceId: " + str(args.resource_id), file=sys.stderr)
+ result_json = rest_delete(args.host, args.facility, args.resource_id)
+ result_json_pretty = pp.pformat(result_json)
+ print(" json: " + textwrap.indent(result_json_pretty, " " * 7)[7:], file=sys.stderr)
+ print(json.dumps(result_json))
+
+ elif args.list:
+ print("list on: " + str(args.host), file=sys.stderr)
+ print(" facility: " + str(args.facility), file=sys.stderr)
+ result_json = rest_list(args.host, args.facility)
+ result_json_pretty = pp.pformat(result_json)
+ print(" json: " + textwrap.indent(result_json_pretty, " " * 7)[7:], file=sys.stderr)
+ print(json.dumps(result_json))
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
+
diff --git a/contrib/rest_api_usage_example/tryme.cfg b/contrib/rest_api_usage_example/tryme.cfg
new file mode 100644
index 0000000..0506a19
--- /dev/null
+++ b/contrib/rest_api_usage_example/tryme.cfg
@@ -0,0 +1,14 @@
+# Set this value to the EID of the eUICC you want to target. The EID is the
+# primary identifier on which the eIM identifies the requests from the IoT
+# device (IPAd).
+EID='89049044900000000000000000102452'
+
+# Change this value to the activation code (AC) of your profile download
+AC='1$smdpp.test.rsp.sysmocom.de$TS48V1-A-UNIQUE'
+
+# Set this value to the ICCID of your profile. The ICCID format used here is
+# the binary form as it can be found in EF.ICCID. (In case you do not know the
+# ICCID (yet), you may leave this field empty. The ICCID is returned by the eIM
+# (tryme_download.sh) when the profile download is complete. In case the profile
+# is already installed, you may request a list (tryme_listProfileInfo.sh).
+ICCID='989444999999990920F3' #8949449999999990023
diff --git a/contrib/tryme_addEim.sh b/contrib/rest_api_usage_example/tryme_addEim.sh
similarity index 90%
rename from contrib/tryme_addEim.sh
rename to contrib/rest_api_usage_example/tryme_addEim.sh
index a504010..70a246b 100755
--- a/contrib/tryme_addEim.sh
+++ b/contrib/rest_api_usage_example/tryme_addEim.sh
@@ -1,11 +1,10 @@
#!/bin/bash
-. ./tryme.cfg
+. ./getopts.sh
JSON='{ "eidValue" : "'$EID'", "order" : { "eco" : [ { "addEim" : { "eimConfigurationData" : "3079800465494D32810E3132372E302E302E313A383030308301018401FFA55BA059301306072A8648CE3D020106082A8648CE3D03010703420004FE584A6F450459574AECA195D0299737F74C89BA2D36DF9286EC25D973037A0FBA70D14DF3E1F7D0A305E57B95B731C4DE218D2D7F9F22113ED5D18C2E3DDF1C" } } ] } }'
RC=`./restop.py -c -f eco -j "$JSON"`
-echo $RC
echo "---------------------------------------8<---------------------------------------"
RESOURCE_ID=`echo $RC | cut -d '/' -f 6`
echo "ResourceId =" $RESOURCE_ID
-./tryme_lookup.sh eco $RESOURCE_ID
+./lookup.sh eco $RESOURCE_ID
diff --git a/contrib/tryme_configureImmediateEnable.sh b/contrib/rest_api_usage_example/tryme_configureImmediateEnable.sh
similarity index 86%
rename from contrib/tryme_configureImmediateEnable.sh
rename to contrib/rest_api_usage_example/tryme_configureImmediateEnable.sh
index 9cf4fde..9538beb 100755
--- a/contrib/tryme_configureImmediateEnable.sh
+++ b/contrib/rest_api_usage_example/tryme_configureImmediateEnable.sh
@@ -1,11 +1,10 @@
#!/bin/bash
-. ./tryme.cfg
+. ./getopts.sh
JSON='{ "eidValue" : "'$EID'", "order" : { "psmo": [ { "configureImmediateEnable" : { "immediateEnableFlag" : true, "defaultSmdpAddress" : "testsmdpplus1.example.com" } } ] } }'
RC=`./restop.py -c -f psmo -j "$JSON"`
-echo $RC
echo "---------------------------------------8<---------------------------------------"
RESOURCE_ID=`echo $RC | cut -d '/' -f 6`
echo "ResourceId =" $RESOURCE_ID
-./tryme_lookup.sh psmo $RESOURCE_ID
+./lookup.sh psmo $RESOURCE_ID
diff --git a/contrib/tryme_delete.sh b/contrib/rest_api_usage_example/tryme_delete.sh
similarity index 84%
rename from contrib/tryme_delete.sh
rename to contrib/rest_api_usage_example/tryme_delete.sh
index bf1a9b2..bedb3db 100755
--- a/contrib/tryme_delete.sh
+++ b/contrib/rest_api_usage_example/tryme_delete.sh
@@ -1,12 +1,11 @@
#!/bin/bash
-. ./tryme.cfg
+. ./getopts.sh
JSON='{ "eidValue" : "'$EID'", "order" : { "psmo" : [ { "delete" : { "iccid" : "'$ICCID'" } } ] } }'
RC=`./restop.py -c -f psmo -j "$JSON"`
-echo $RC
echo "---------------------------------------8<---------------------------------------"
RESOURCE_ID=`echo $RC | cut -d '/' -f 6`
echo "ResourceId =" $RESOURCE_ID
-./tryme_lookup.sh psmo $RESOURCE_ID
+./lookup.sh psmo $RESOURCE_ID
diff --git a/contrib/tryme_deleteEim.sh b/contrib/rest_api_usage_example/tryme_deleteEim.sh
similarity index 84%
rename from contrib/tryme_deleteEim.sh
rename to contrib/rest_api_usage_example/tryme_deleteEim.sh
index 8faf657..5177e6e 100755
--- a/contrib/tryme_deleteEim.sh
+++ b/contrib/rest_api_usage_example/tryme_deleteEim.sh
@@ -1,11 +1,11 @@
#!/bin/bash
-. ./tryme.cfg
+. ./getopts.sh
+
EIM_ID="eIM2"
JSON='{ "eidValue" : "'$EID'", "order" : { "eco" : [ { "deleteEim" : { "eimId" : "'$EIM_ID'" } } ] } }'
RC=`./restop.py -c -f eco -j "$JSON"`
-echo $RC
echo "---------------------------------------8<---------------------------------------"
RESOURCE_ID=`echo $RC | cut -d '/' -f 6`
echo "ResourceId =" $RESOURCE_ID
-./tryme_lookup.sh eco $RESOURCE_ID
+./lookup.sh eco $RESOURCE_ID
diff --git a/contrib/tryme_disable.sh b/contrib/rest_api_usage_example/tryme_disable.sh
similarity index 84%
rename from contrib/tryme_disable.sh
rename to contrib/rest_api_usage_example/tryme_disable.sh
index a45af94..cc55a41 100755
--- a/contrib/tryme_disable.sh
+++ b/contrib/rest_api_usage_example/tryme_disable.sh
@@ -1,11 +1,10 @@
#!/bin/bash
-. ./tryme.cfg
+. ./getopts.sh
JSON='{ "eidValue" : "'$EID'", "order" : { "psmo" : [ { "disable" : { "iccid" : "'$ICCID'" } } ] } }'
RC=`./restop.py -c -f psmo -j "$JSON"`
-echo $RC
echo "---------------------------------------8<---------------------------------------"
RESOURCE_ID=`echo $RC | cut -d '/' -f 6`
echo "ResourceId =" $RESOURCE_ID
-./tryme_lookup.sh psmo $RESOURCE_ID
+./lookup.sh psmo $RESOURCE_ID
diff --git a/contrib/tryme_download.sh b/contrib/rest_api_usage_example/tryme_download.sh
similarity index 82%
rename from contrib/tryme_download.sh
rename to contrib/rest_api_usage_example/tryme_download.sh
index 10725a2..56f9096 100755
--- a/contrib/tryme_download.sh
+++ b/contrib/rest_api_usage_example/tryme_download.sh
@@ -1,11 +1,10 @@
#!/bin/bash
-. ./tryme.cfg
+. ./getopts.sh
JSON='{ "eidValue" : "'$EID'", "order" : { "download" : {"activationCode" : "'$AC'" } } }'
RC=`./restop.py -c -f download -j "$JSON"`
-echo $RC
echo "---------------------------------------8<---------------------------------------"
RESOURCE_ID=`echo $RC | cut -d '/' -f 6`
echo "ResourceId =" $RESOURCE_ID
-./tryme_lookup.sh download $RESOURCE_ID
+./lookup.sh download $RESOURCE_ID
diff --git a/contrib/tryme_enable.sh b/contrib/rest_api_usage_example/tryme_enable.sh
similarity index 84%
rename from contrib/tryme_enable.sh
rename to contrib/rest_api_usage_example/tryme_enable.sh
index ac08706..93f52c6 100755
--- a/contrib/tryme_enable.sh
+++ b/contrib/rest_api_usage_example/tryme_enable.sh
@@ -1,11 +1,10 @@
#!/bin/bash
-. ./tryme.cfg
+. ./getopts.sh
JSON='{ "eidValue" : "'$EID'", "order" : { "psmo": [ { "enable" : { "iccid" : "'$ICCID'", "rollback" : false } } ] } }'
RC=`./restop.py -c -f psmo -j "$JSON"`
-echo $RC
echo "---------------------------------------8<---------------------------------------"
RESOURCE_ID=`echo $RC | cut -d '/' -f 6`
echo "ResourceId =" $RESOURCE_ID
-./tryme_lookup.sh psmo $RESOURCE_ID
+./lookup.sh psmo $RESOURCE_ID
diff --git a/contrib/tryme_euiccDataRequest.sh b/contrib/rest_api_usage_example/tryme_euiccDataRequest.sh
similarity index 74%
rename from contrib/tryme_euiccDataRequest.sh
rename to contrib/rest_api_usage_example/tryme_euiccDataRequest.sh
index ba70a69..28aa282 100755
--- a/contrib/tryme_euiccDataRequest.sh
+++ b/contrib/rest_api_usage_example/tryme_euiccDataRequest.sh
@@ -1,10 +1,10 @@
#!/bin/bash
-. ./tryme.cfg
-JSON='{ "eidValue" : "'$EID'", "order" : { "edr" : { "tagList" : "81BF20BF228384A5A6A8A9A0" } } }'
+. ./getopts.sh
+
+JSON='{ "eidValue" : "'$EID'", "order" : { "edr" : { "tagList" : "81BF20BF228384A5A6A8A9A0A2" } } }'
RC=`./restop.py -c -f edr -j "$JSON"`
-echo $RC
echo "---------------------------------------8<---------------------------------------"
RESOURCE_ID=`echo $RC | cut -d '/' -f 6`
echo "ResourceId =" $RESOURCE_ID
-./tryme_lookup.sh edr $RESOURCE_ID
+./lookup.sh edr $RESOURCE_ID
diff --git a/contrib/tryme_euicc_get_state.sh b/contrib/rest_api_usage_example/tryme_euicc_get_state.sh
similarity index 87%
rename from contrib/tryme_euicc_get_state.sh
rename to contrib/rest_api_usage_example/tryme_euicc_get_state.sh
index dc96cf0..f41ecc3 100755
--- a/contrib/tryme_euicc_get_state.sh
+++ b/contrib/rest_api_usage_example/tryme_euicc_get_state.sh
@@ -1,14 +1,13 @@
#!/bin/bash
-. ./tryme.cfg
+. ./getopts.sh
# States we want to retrieve with this request.
STATES='"counterValue", "consumerEuicc", "signAlgo", "signPubKey", "stateChangeCauseList"'
JSON='{ "eidValue" : "'$EID'", "order" : { "euicc" : { "get" : [ '$STATES' ] } } }'
RC=`./restop.py -c -f euicc -j "$JSON"`
-echo $RC
echo "---------------------------------------8<---------------------------------------"
RESOURCE_ID=`echo $RC | cut -d '/' -f 6`
echo "ResourceId =" $RESOURCE_ID
-./tryme_lookup.sh euicc $RESOURCE_ID
+./lookup.sh euicc $RESOURCE_ID
diff --git a/contrib/tryme_euicc_set_state.sh b/contrib/rest_api_usage_example/tryme_euicc_set_state.sh
similarity index 92%
rename from contrib/tryme_euicc_set_state.sh
rename to contrib/rest_api_usage_example/tryme_euicc_set_state.sh
index 04d8c51..5d30ea1 100755
--- a/contrib/tryme_euicc_set_state.sh
+++ b/contrib/rest_api_usage_example/tryme_euicc_set_state.sh
@@ -1,5 +1,5 @@
#!/bin/bash
-. ./tryme.cfg
+. ./getopts.sh
# States we want to set with this request (list does not have to be complete, states not listest here are left
# unchanged.
@@ -11,9 +11,8 @@
JSON='{ "eidValue" : "'$EID'", "order" : { "euicc" : { "set" : '$STATES' } } }'
RC=`./restop.py -c -f euicc -j "$JSON"`
-echo $RC
echo "---------------------------------------8<---------------------------------------"
RESOURCE_ID=`echo $RC | cut -d '/' -f 6`
echo "ResourceId =" $RESOURCE_ID
-./tryme_lookup.sh euicc $RESOURCE_ID
+./lookup.sh euicc $RESOURCE_ID
diff --git a/contrib/tryme_getRAT.sh b/contrib/rest_api_usage_example/tryme_getRAT.sh
similarity index 83%
rename from contrib/tryme_getRAT.sh
rename to contrib/rest_api_usage_example/tryme_getRAT.sh
index fa61d51..4c3dcdf 100755
--- a/contrib/tryme_getRAT.sh
+++ b/contrib/rest_api_usage_example/tryme_getRAT.sh
@@ -1,11 +1,10 @@
#!/bin/bash
-. ./tryme.cfg
+. ./getopts.sh
JSON='{ "eidValue" : "'$EID'", "order" : { "psmo" : [ { "getRAT" : { } } ] } }'
RC=`./restop.py -c -f psmo -j "$JSON"`
-echo $RC
echo "---------------------------------------8<---------------------------------------"
RESOURCE_ID=`echo $RC | cut -d '/' -f 6`
echo "ResourceId =" $RESOURCE_ID
-./tryme_lookup.sh psmo $RESOURCE_ID
+./lookup.sh psmo $RESOURCE_ID
diff --git a/contrib/tryme_listEim.sh b/contrib/rest_api_usage_example/tryme_listEim.sh
similarity index 83%
rename from contrib/tryme_listEim.sh
rename to contrib/rest_api_usage_example/tryme_listEim.sh
index 0d86787..25ece68 100755
--- a/contrib/tryme_listEim.sh
+++ b/contrib/rest_api_usage_example/tryme_listEim.sh
@@ -1,11 +1,10 @@
#!/bin/bash
-. ./tryme.cfg
+. ./getopts.sh
JSON='{ "eidValue" : "'$EID'", "order" : { "eco" : [ { "listEim" : { } } ] } }'
RC=`./restop.py -c -f eco -j "$JSON"`
-echo $RC
echo "---------------------------------------8<---------------------------------------"
RESOURCE_ID=`echo $RC | cut -d '/' -f 6`
echo "ResourceId =" $RESOURCE_ID
-./tryme_lookup.sh eco $RESOURCE_ID
+./lookup.sh eco $RESOURCE_ID
diff --git a/contrib/tryme_listProfileInfo.sh b/contrib/rest_api_usage_example/tryme_listProfileInfo.sh
similarity index 83%
rename from contrib/tryme_listProfileInfo.sh
rename to contrib/rest_api_usage_example/tryme_listProfileInfo.sh
index 7d64b98..a555308 100755
--- a/contrib/tryme_listProfileInfo.sh
+++ b/contrib/rest_api_usage_example/tryme_listProfileInfo.sh
@@ -1,11 +1,10 @@
#!/bin/bash
-. ./tryme.cfg
+. ./getopts.sh
JSON='{ "eidValue" : "'$EID'", "order" : { "psmo" : [ { "listProfileInfo" : { } } ] } }'
RC=`./restop.py -c -f psmo -j "$JSON"`
-echo $RC
echo "---------------------------------------8<---------------------------------------"
RESOURCE_ID=`echo $RC | cut -d '/' -f 6`
echo "ResourceId =" $RESOURCE_ID
-./tryme_lookup.sh psmo $RESOURCE_ID
+./lookup.sh psmo $RESOURCE_ID
diff --git a/contrib/tryme_updateEim.sh b/contrib/rest_api_usage_example/tryme_updateEim.sh
similarity index 86%
rename from contrib/tryme_updateEim.sh
rename to contrib/rest_api_usage_example/tryme_updateEim.sh
index 5d08398..c228f1d 100755
--- a/contrib/tryme_updateEim.sh
+++ b/contrib/rest_api_usage_example/tryme_updateEim.sh
@@ -1,11 +1,10 @@
#!/bin/bash
-. ./tryme.cfg
+. ./getopts.sh
JSON='{ "eidValue" : "'$EID'", "order" : { "eco" : [ { "updateEim" : { "eimConfigurationData" : "3017800465494D32810F3132372E302E302E34323A39303030" } } ] } }'
RC=`./restop.py -c -f eco -j "$JSON"`
-echo $RC
echo "---------------------------------------8<---------------------------------------"
RESOURCE_ID=`echo $RC | cut -d '/' -f 6`
echo "ResourceId =" $RESOURCE_ID
-./tryme_lookup.sh eco $RESOURCE_ID
+./lookup.sh eco $RESOURCE_ID
diff --git a/contrib/restop.py b/contrib/restop.py
deleted file mode 100755
index a12ccd4..0000000
--- a/contrib/restop.py
+++ /dev/null
@@ -1,95 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (c) 2025 Onomondo ApS & sysmocom - s.f.m.c. GmbH. All rights reserved.
-# SPDX-License-Identifier: AGPL-3.0-only
-# Author: Philipp Maier <pmaier(a)sysmocom.de> / sysmocom - s.f.m.c. GmbH
-
-import sys
-import argparse
-import json
-import requests
-import erlang
-import pprint
-
-DOWNLOAD_DEFAULT='{ "eidValue" : "89882119900000000000000000000005", "order" : {"activationCode" : "1$testsmdpplus1.example.com$OPxLD-UVRuC-jysPI-YkOwT"}}'
-PSMO_DEFAULT='{ "eidValue" : "89882119900000000000000000000005", "order" : [{"psmo" : "enable", "iccid" : "98001032547698103285", "rollback" : false }]}'
-
-req_headers = {
- 'Content-Type': 'application/json',
- 'X-Admin-Protocol': 'onomondo/eim/v1.0.0',
-}
-
-def h2b(s) -> bytearray:
- """convert from a string of hex nibbles to a sequence of bytes"""
- return bytes.fromhex(s)
-
-def rest_create(host, facility, Json):
- r = requests.post("http://" + str(host) + "/" + str(facility) + "/create", json=Json, headers=req_headers)
- return str(r.url)
-
-def rest_lookup(host, facility, ResourceId):
- r = requests.get("http://" + str(host) + "/" + str(facility) + "/lookup/" + str(ResourceId), headers=req_headers)
- return r.json()
-
-def rest_delete(host, facility, ResourceId):
- r = requests.get("http://" + str(host) + "/" + str(facility) + "/delete/" + str(ResourceId), headers=req_headers)
- return r.json()
-
-def rest_list(host, facility):
- r = requests.get("http://" + str(host) + "/" + str(facility) + "/list/", headers=req_headers)
- return r.json()
-
-def main(argv):
- parser = argparse.ArgumentParser(prog='restop', description='utility to operate on the REST API of onomondo_eim')
- parser.add_argument("-s", "--host", default="127.0.0.1:8080")
- parser.add_argument("-f", "--facility", default="download")
- parser.add_argument("-c", "--create", action='store_true', default=False)
- parser.add_argument("-l", "--lookup", action='store_true', default=False)
- parser.add_argument("-d", "--delete", action='store_true', default=False)
- parser.add_argument("-t", "--list", action='store_true', default=False)
- parser.add_argument("-r", "--resource-id")
- parser.add_argument("-j", "--json")
-
- args = parser.parse_args()
-
- if args.create:
- print("create on: " + str(args.host), file=sys.stderr)
- print(" facility: " + str(args.facility), file=sys.stderr)
- if args.json:
- args_json = str(args.json)
- else:
- if args.facility == "download":
- args_json = DOWNLOAD_DEFAULT
- elif args.facility == "psmo":
- args_json = PSMO_DEFAULT
- print(" json: " + args_json, file=sys.stderr)
- print("result:", file=sys.stderr)
- print(rest_create(args.host, args.facility, json.loads(args_json)))
- elif args.lookup:
- print("lookup on: " + str(args.host), file=sys.stderr)
- print(" facility: " + str(args.facility), file=sys.stderr)
- print(" resourceId: " + str(args.resource_id), file=sys.stderr)
- result = rest_lookup(args.host, args.facility, args.resource_id)
- print(json.dumps(result))
- if 'debuginfo' in result:
- debuginfo_hexstr=result['debuginfo']
- debuginfo_bytes=h2b(debuginfo_hexstr)
- debuginfo_term=erlang.binary_to_term(debuginfo_bytes)
- pp = pprint.PrettyPrinter(depth=4)
- debuginfo_pretty = pp.pformat(debuginfo_term)
- print("decoded debuginfo: " + str(debuginfo_pretty), file=sys.stderr)
- elif args.delete:
- print("delete on: " + str(args.host), file=sys.stderr)
- print(" facility: " + str(args.facility), file=sys.stderr)
- print(" resourceId: " + str(args.resource_id), file=sys.stderr)
- print("result:", file=sys.stderr)
- print(json.dumps(rest_delete(args.host, args.facility, args.resource_id)))
- elif args.list:
- print("list on: " + str(args.host), file=sys.stderr)
- print(" facility: " + str(args.facility), file=sys.stderr)
- print("result:", file=sys.stderr)
- print(json.dumps(rest_list(args.host, args.facility)), file=sys.stderr)
-
-if __name__ == "__main__":
- main(sys.argv[1:])
-
diff --git a/contrib/tryme.cfg b/contrib/tryme.cfg
deleted file mode 100644
index 674da53..0000000
--- a/contrib/tryme.cfg
+++ /dev/null
@@ -1,63 +0,0 @@
-#EID='89882119900000000000000000000005' #NIST+BRP Test eUICC (Consumer)
-#EID='89044045118427484800000000011628' #NIST+BRP Test eUICC (IoT)
-#EID='89044045116727494800000004479172' #SLM17 ECu10.07 GSMA sample eUICC
-#EID='89086030202200000022000027485428' #eSIM me eUICC (Consumer)
-#EID='89049032123451234512345678901235' #Comprion Test eUICC (Consumer)
-EID='89034011022310000000000006803372' #Consumer eUICC for testing/demo
-
-case $1 in
- "A")
- #GSMA TS.48 V1 A
- AC='1$smdpp.test.rsp.sysmocom.de$TS48V1-A-UNIQUE'
- ICCID='989444999999990920F3' #8949449999999990023
- ;;
-
- "B")
- #GSMA TS.48 V1 B
- AC='1$smdpp.test.rsp.sysmocom.de$TS48V1-B-UNIQUE'
- ICCID='989444999999990930F1' #8949449999999990031
- ;;
- "C")
- #A test profile from a commercial operator (use GSMA eUICC)
- AC='1$rsp.truphone.com$QR-G-5C-1LS-1W1Z9P7'
- ICCID='984474680000230631F1' #changes after each download
- ;;
- "D")
- #TS48V1-B-UNIQUE-nojavacard-nocsim on self hosted SMDP+ instance
- #Osmo-smdpplus will verify the server address, you can put the SMPD+ hostname
- #(testsmdpplus1.example.com) into /etc/hosts: "127.0.0.1 testsmdpplus1.example.com"
- AC='1$testsmdpplus1.example.com$TS48V1-B-UNIQUE-nojavacard-nocsim'
- ICCID='989444999999990930F1' #8949449999999990031
- ;;
- "E")
- #A test profile that is installed on a test eUICC at some other location
- AC='1$rsp.example.com$UNKNOWN' #dummy value, unknown
- ICCID='98543700000012181938' #89457300000021819183
- ;;
- "X")
- #A way to sumbint any EID, ICCID and AC directly without putting it here.
- EID=$2
- ICCID=$3
- AC=$4
- ;;
- *)
- echo "EID=$EID"
- echo "Please select a profile:"
- echo "A: GSMA TS.48 V1 A"
- echo "B: GSMA TS.48 V1 B"
- echo "C: Truephone (commercial, GSMA)"
- echo "D: TS48V1-B-UNIQUE-nojavacard-nocsim (for debugging)"
- echo "E: Testprofile on another test eUICC (for testing/demo)"
- echo "X: Use any EID, ICCID or AC directly (tryme_*.sh EID [ICCID] [AC])"
- echo ""
- echo "A few examples:"
- echo "./tryme_download.sh X 89086030202200000022000027485428 NOT_NEEDED '1\$rsp.truphone.com\$QR-G-5C-1LS-1W1Z9P7'"
- echo "./tryme_enable.sh X 89086030202200000022000027485428 984474680000730600F7"
- echo "./tryme_listProfileInfo.sh X 89086030202200000022000027485428"
- echo "./tryme_download.sh E"
- echo "./tryme_enable.sh E"
- echo "./tryme_listProfileInfo.sh E"
- echo ""
- exit
- ;;
-esac
diff --git a/contrib/tryme_lookup.sh b/contrib/tryme_lookup.sh
deleted file mode 100755
index df8e129..0000000
--- a/contrib/tryme_lookup.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/bash
-
-if [[ $# == 2 ]]; then
- sleep 5
- while true; do
- ./restop.py -l -f $1 -r $2
- sleep 5
- done
-else
- echo "Usage: $0 facility resource-id"
-fi
-
-
-
diff --git a/doc/examples.md b/doc/examples.md
index 9f3726a..ab7f510 100644
--- a/doc/examples.md
+++ b/doc/examples.md
@@ -2,8 +2,8 @@
------------------
The REST API is complex interface that is difficult to operate out of the box without any prior familiarization. To
-give a system integrator a good starting point, the contrib directory contains "tryme-scripts" that serve as examples
-and an easy way try out the REST API.
+give a system integrator a good starting point, the contrib/rest_api_usage_example directory contains "tryme-scripts"
+that serve as examples and an easy way try out the REST API.
### Scripts
@@ -14,6 +14,17 @@
It should be noted that the tryme_*.sh scripts are really just simple examples that were created to simplify testing
during development.
+#### tryme.cfg
+
+This is just a simple shellscript that sets some initial variables. Some sample values are already present. It is
+recommended to edit tryme.cfg and to replace the sample values with some useful values. This is in particular the
+$EID variable at the top of the file.
+
+The file also defines a sample profiles along with a matching activation code (`$AC`) and ICCID (`$ICCID`). The ICCID
+is usually not known in advance. It becomes known after the eUICC has decrypted and installed the profile package. It
+should also be noted that the ICCID parameter is always issued in its raw format (digits swapped, padded with 'F' at
+the end).
+
#### restop.py
The python-script "restop.py" is called by the tryme "tryme_*.sh" scripts. This script can be used as a stand-alone
@@ -22,19 +33,19 @@
user must keep track of the REST resources he created, monitor them, check for errors, delete REST resources, resubmit
REST resources in case an `order` has failed, etc.
-#### tryme.cfg
+#### lookup.sh
-This is just a simple shellscript that sets some initial variables. Some sample values are already present. It is
-recommended to edit tryme.cfg and to replace the sample values with some useful values. This is in particular the
-$EID variable at the top of the file.
+This shellscript serves as a helper for the "tryme_*.sh" scripts. It is uses "restop.py" to query and display the
+status of the current REST resource in regular intervals.
-The file also defines some sample profiles along with their activation codes (`$AC`) and ICCIDs (`$ICCID`). The ICCID is
-usually not known in advance. It becomes known after the eUICC has decrypted and installed the profile package. It
-should also be noted that the ICCID parameter is always issued in its raw format (digits swapped, padded with 'F' at
-the end).
+#### getopts.sh
-In tryme.cfg one will also find a profile `X`, this profile is a placeholder in case the user decides not to edit
-tryme.cfg and to pass all parameters from the commandline instead.
+This shellscript is executed by the "tryme_*.sh" on each startup. It is not meant to be called directly by the user.
+The purpose of "getopts.sh" to either read the configurable parameters (EID, AC, ICCID) from "tryme.cfg" or to accept
+those parameters directly from the commandline. For a commandline help, the user may call any "tryme_*.sh" with the
+`-h` options. In case the "tryme_*.sh" scripts are called without parameters, the parameters are loaded from "tryme.cfg"
+In case a different config file shall be used, the user use the `-c` parameter to change the location of the config
+file.
### Downloading And Enabling A Profile
@@ -42,13 +53,11 @@
following example we will pass all parameters directly from the commandline. It is assumed that the REST API of
onomondo-eim is available at 127.0.0.1:8080.
-In the first step we will issue a download `order` using the `tryme_download.sh`. The parameter `X` tells tryme.cfg to
-use the placeholder profile. The second parameter is the EID (not to be confused with ICCID) of the eUICC. The
-third parameter serves as a placeholder for the ICCID, which we do not know or need yet. The last parameter is the
-`activationCode`.
+In the first step we will issue a download `order` using the `tryme_download.sh`. The parameter `-e` specifies the
+EID, The second parameter `-a` specifies the `activationCode` (AC).
```
-./tryme_download.sh X 12345678900000000000000000001234 NOT_NEEDED '1$rsp.example.com$EXAMPLE'
+./tryme_download.sh -e 12345678900000000000000000001234 -a '1$rsp.example.com$EXAMPLE'
```
When the script is executed, it will `create` the related REST resource and then `lookup` the REST `resource`
@@ -66,11 +75,11 @@
(to keep the rest table clean one should `delete` the rest resource now as described above)
However, the profile is not enabled yet. In order to use it, we must issue an `enable` PSMO first. To do that we may
-run `tryme_enable.sh`. The parameter `X` tells tryme.cfg to use the placeholder profile again. The second parameter is
-the EID and the third parameter is the ICCID that we have just taken from the JSON output above.
+run `tryme_enable.sh`. The parameter `-e` tells specifies the EID again. The second parameter `-i` specifies the
+the ICCID that we have just taken from the JSON output above.
```
-./tryme_enable.sh X 12345678900000000000000000001234 12324567899999911191
+./tryme_enable.sh -e 12345678900000000000000000001234 -i 12324567899999911191
```
We now must wait again until the IPAd fetches the related eIM package with the eUICC package that contains the `enable`
@@ -90,7 +99,7 @@
To send a `listProfileInfo` run:
```
-./tryme_listProfileInfo.sh X 12345678900000000000000000001234
+./tryme_listProfileInfo.sh -e 12345678900000000000000000001234
```
As soon as the IPAd has fetched and executed the related eUICC package, the JSON output should look like this:
@@ -111,7 +120,7 @@
To perform an eUICC data request, execute the following script like so:
```
-./tryme_euiccDataRequest.sh X 12345678900000000000000000001234
+./tryme_euiccDataRequest.sh -e 12345678900000000000000000001234
```
There should be response like this:
@@ -138,7 +147,7 @@
We would edit the order into tryme_set_euicc_param.sh and run the script:
```
-./tryme_euicc_set_state.sh X 12345678900000000000000000001234
+./tryme_euicc_set_state.sh -e 12345678900000000000000000001234
```
The order will execute as an internal process. This means that no IPAd interaction is involved. However, on the REST
@@ -165,7 +174,7 @@
We would edit the order into tryme_set_euicc_param.sh and run the script:
```
-./tryme_euicc_get_state.sh X 12345678900000000000000000001234
+./tryme_euicc_get_state.sh -e 12345678900000000000000000001234
```
The eIM will respond with a list of key value pairs of the requested eUICC states:
--
To view, visit https://gerrit.osmocom.org/c/onomondo-eim/+/43142?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: merged
Gerrit-Project: onomondo-eim
Gerrit-Branch: master
Gerrit-Change-Id: I4e42ce85c84b47d3561011083da4a1e54958fd8e
Gerrit-Change-Number: 43142
Gerrit-PatchSet: 6
Gerrit-Owner: dexter <pmaier(a)sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: dexter <pmaier(a)sysmocom.de>
Gerrit-Reviewer: jolly <andreas(a)eversberg.eu>
Gerrit-Reviewer: laforge <laforge(a)osmocom.org>
dexter has submitted this change. ( https://gerrit.osmocom.org/c/onomondo-eim/+/43013?usp=email )
(
7 is the latest approved patch-set.
No files were changed between the latest approved patch-set and the submitted one.
)Change subject: esipa_asn1_handler: store IPAd stateChangeCause
......................................................................
esipa_asn1_handler: store IPAd stateChangeCause
The IPAd may pass a notifyStateChange and a stateChangeCause code.
The notifyStateChange flag tells that a state change happened and
the stateChangeCause code tells what the cause was.
let's store the stateChangeCause code as a state in the euicc
table, so that the REST API user can poll for stateChangeCause
codes in suitable intervals.
Change-Id: I3fe64687786d9184ae82cbb12f9d1292fdc945c8
Related: SYS#8100
---
M contrib/tryme_euicc_get_state.sh
M contrib/tryme_euicc_set_state.sh
M include/mnesia_db_euicc.hrl
M src/esipa_asn1_handler.erl
M src/mnesia_db_euicc.erl
5 files changed, 38 insertions(+), 10 deletions(-)
Approvals:
jolly: Looks good to me, but someone else must approve
Jenkins Builder: Verified
laforge: Looks good to me, approved
diff --git a/contrib/tryme_euicc_get_state.sh b/contrib/tryme_euicc_get_state.sh
index 524d37d..dc96cf0 100755
--- a/contrib/tryme_euicc_get_state.sh
+++ b/contrib/tryme_euicc_get_state.sh
@@ -2,7 +2,7 @@
. ./tryme.cfg
# States we want to retrieve with this request.
-STATES='"counterValue", "consumerEuicc", "signAlgo", "signPubKey"'
+STATES='"counterValue", "consumerEuicc", "signAlgo", "signPubKey", "stateChangeCauseList"'
JSON='{ "eidValue" : "'$EID'", "order" : { "euicc" : { "get" : [ '$STATES' ] } } }'
RC=`./restop.py -c -f euicc -j "$JSON"`
diff --git a/contrib/tryme_euicc_set_state.sh b/contrib/tryme_euicc_set_state.sh
index d016ce1..04d8c51 100755
--- a/contrib/tryme_euicc_set_state.sh
+++ b/contrib/tryme_euicc_set_state.sh
@@ -6,7 +6,8 @@
STATES='[{ "counterValue" : 1000 },
{ "consumerEuicc" : false },
{ "signAlgo" : "prime256v1" },
- { "signPubKey" : "04BD2C55B28B4E801CA0B14F195788FD86C7FF49764C88A2934C69594A26FF647549F3F16060BD9C8D793D95D0126FA429C94966FE7842967263795A73C498F1DB" }]'
+ { "signPubKey" : "04BD2C55B28B4E801CA0B14F195788FD86C7FF49764C88A2934C69594A26FF647549F3F16060BD9C8D793D95D0126FA429C94966FE7842967263795A73C498F1DB" },
+ { "stateChangeCauseList" : [] }]'
JSON='{ "eidValue" : "'$EID'", "order" : { "euicc" : { "set" : '$STATES' } } }'
RC=`./restop.py -c -f euicc -j "$JSON"`
diff --git a/include/mnesia_db_euicc.hrl b/include/mnesia_db_euicc.hrl
index 78f45f1..6fccaf3 100644
--- a/include/mnesia_db_euicc.hrl
+++ b/include/mnesia_db_euicc.hrl
@@ -12,5 +12,7 @@
% Pubkey to authenticate eUICC Package Results (see also SGP.32, section 2.11.2.1)
signPubKey :: binary(),
% Algorithem to authenticate eUICC Package Results (prime256v1 or brainpoolP256r1)
- signAlgo :: binary()
+ signAlgo :: binary(),
+ % A list with the reported IPAd state change cause codes (has to be polled and reset by REST API user)
+ stateChangeCauseList :: list()
}).
diff --git a/src/esipa_asn1_handler.erl b/src/esipa_asn1_handler.erl
index e3a6abf..3d0d6b0 100644
--- a/src/esipa_asn1_handler.erl
+++ b/src/esipa_asn1_handler.erl
@@ -266,13 +266,33 @@
emptyResponse;
%GSMA SGP.32, section 6.3.2.6
handle_asn1(Pid, {getEimPackageRequest, EsipaReq}) ->
- % TODO: The purpose of the notifyStateChange field in the getEimPackageRequest is to inform the eIM that some state
- % in the eUICC has changed and that the eIM (and in particular the REST API user) should perform an update of its
- % local records (eUICC data request, listProfileInfo PSMO etc...) This is a feature that this eIM does not support
- % yet. To implement the feature we could use a flag in the euicc table to tell the REST API user to perform the
- % update. Get the notifyStateChange flag like so: NotifStateChg = maps:is_key(notifyStateChange, EsipaReq).
-
EidValue = maps:get(eidValue, EsipaReq),
+
+ % Store stateChangeCause, but only in case the EID is already known to this eIM as we do not want to record any
+ % information from foreigen eUICCs.
+ case maps:get(notifyStateChange, EsipaReq, none) of
+ 'NULL' ->
+ case mnesia_db_euicc:state_get(utils:binary_to_hex(EidValue), stateChangeCauseList) of
+ {ok, StateChangeCauseList} ->
+ StateChangeCause = maps:get(stateChangeCause, EsipaReq, undefined),
+ case lists:member(StateChangeCause, StateChangeCauseList) of
+ false ->
+ mnesia_db_euicc:state_set(
+ utils:binary_to_hex(EidValue),
+ stateChangeCauseList,
+ StateChangeCauseList ++ [StateChangeCause]
+ );
+ _ ->
+ ok
+ end;
+ _ ->
+ ok
+ end;
+ _ ->
+ ok
+ end,
+
+ % Continue with the processing of the getEimPackageRequest
Work = mnesia_db_work:fetch(utils:binary_to_hex(EidValue), Pid),
EsipaResp =
case Work of
diff --git a/src/mnesia_db_euicc.erl b/src/mnesia_db_euicc.erl
index 5a5634f..2c18990 100644
--- a/src/mnesia_db_euicc.erl
+++ b/src/mnesia_db_euicc.erl
@@ -31,7 +31,8 @@
consumerEuicc = ConsumerEuicc,
associationToken = 1,
signPubKey = <<>>,
- signAlgo = <<"prime256v1">>
+ signAlgo = <<"prime256v1">>,
+ stateChangeCauseList = []
},
Q = qlc:q([X#euicc.eidValue || X <- mnesia:table(euicc), X#euicc.eidValue == EidValue]),
Present = qlc:e(Q),
@@ -111,6 +112,8 @@
mnesia:write(Row#euicc{signPubKey = Value});
signAlgo ->
mnesia:write(Row#euicc{signAlgo = Value});
+ stateChangeCauseList ->
+ mnesia:write(Row#euicc{stateChangeCauseList = Value});
_ ->
throw(badState)
end;
@@ -136,6 +139,8 @@
Row#euicc.signPubKey;
signAlgo ->
Row#euicc.signAlgo;
+ stateChangeCauseList ->
+ Row#euicc.stateChangeCauseList;
_ ->
throw(badState)
end;
--
To view, visit https://gerrit.osmocom.org/c/onomondo-eim/+/43013?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: merged
Gerrit-Project: onomondo-eim
Gerrit-Branch: master
Gerrit-Change-Id: I3fe64687786d9184ae82cbb12f9d1292fdc945c8
Gerrit-Change-Number: 43013
Gerrit-PatchSet: 9
Gerrit-Owner: dexter <pmaier(a)sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: dexter <pmaier(a)sysmocom.de>
Gerrit-Reviewer: jolly <andreas(a)eversberg.eu>
Gerrit-Reviewer: laforge <laforge(a)osmocom.org>
dexter has submitted this change. ( https://gerrit.osmocom.org/c/onomondo-eim/+/42993?usp=email )
Change subject: mnesia_db_euicc: renovate handling of euicc table (mnesia, REST)
......................................................................
mnesia_db_euicc: renovate handling of euicc table (mnesia, REST)
The REST API that allows users to set certain eUICC parameters
(we call them "states" from now on) only allows to set eUICC
states, but doesn't allow to read them back.
With this patch, we renovate the handling of the mnesia euicc
table from the ground up so that REST API users have read and
write access to the states stored in the mnesia euicc table.
Change-Id: I74c602555b194a28d6eb9dd67ec4c6a8865fcb77
Related: SYS#8100
---
M contrib/rest_api_resource_schema.json
M contrib/rest_api_response_schema.json
A contrib/tryme_euicc_get_state.sh
A contrib/tryme_euicc_set_state.sh
D contrib/tryme_set_euicc_param.sh
M doc/database.md
M doc/examples.md
M doc/rest_api.md
M src/crypto_utils.erl
M src/esipa_asn1_handler_utils.erl
M src/mnesia_db.erl
M src/mnesia_db_euicc.erl
12 files changed, 315 insertions(+), 175 deletions(-)
Approvals:
laforge: Looks good to me, but someone else must approve
Jenkins Builder: Verified
dexter: Looks good to me, approved
diff --git a/contrib/rest_api_resource_schema.json b/contrib/rest_api_resource_schema.json
index 0a7764b..8a9e91d 100644
--- a/contrib/rest_api_resource_schema.json
+++ b/contrib/rest_api_resource_schema.json
@@ -286,33 +286,47 @@
},
{
"euicc": {
- "description": "set a parameter in the euicc master data table",
- "type": "array",
- "items": {
- "counterValue": {
- "description": "sets the signature counter to a specified value (use with caution)",
- "type": "integer"
+ "description": "order to perform an operation on eUICC states stored in the euicc table (mnesia)",
+ "type": "object",
+ "properties": {
+ "set": {
+ "description": "values of eUICC states to set",
+ "type": "array",
+ "items": {
+ "counterValue": {
+ "description": "signature counter to a specified value (use with caution)",
+ "type": "integer"
+ },
+ "consumerEuicc": {
+ "description": "tells the eIM that the remote end (IPAd) uses a consumer eUICC with an IoT eUICC emulation mode",
+ "type": "boolean"
+ },
+ "associationToken": {
+ "description": "association token that the eUICC uses to identify this eIM internally",
+ "type": "integer"
+ },
+ "signPubKey": {
+ "description": "public key to be used for checking eUICC package results",
+ "type": "string",
+ "pattern": "^[0-9A-F].*$"
+ },
+ "signAlgo": {
+ "description": "algorithm to be used for checking eUICC package results",
+ "type": "string",
+ "enum": [
+ "prime256v1",
+ "brainpoolP256r1"
+ ]
+ }
+ }
},
- "consumerEuicc": {
- "description": "tells the eIM that the remote end (IPAd) uses a consumer eUICC with an IoT eUICC emulation mode",
- "type": "boolean"
- },
- "associationToken": {
- "description": "association token that the eUICC uses to identify this eIM internally",
- "type": "integer"
- },
- "signAlgo": {
- "description": "algorithm to be used for checking eUICC package results",
- "type": "string",
- "enum": [
- "prime256v1",
- "brainpoolP256r1"
- ]
- },
- "signPubKey": {
- "description": "public key to be used for checking eUICC package results",
- "type": "string",
- "pattern": "^[0-9,A-F]{2,32}$"
+ "get": {
+ "description": "names of the eUICC states to retrieve",
+ "type": "array",
+ "items": {
+ "type": "string",
+ "enum": ["counterValue", "consumerEuicc", "associationToken", "signAlgo", "signPubKey"]
+ }
}
}
}
diff --git a/contrib/rest_api_response_schema.json b/contrib/rest_api_response_schema.json
index 867baa4..e0bc6ed 100644
--- a/contrib/rest_api_response_schema.json
+++ b/contrib/rest_api_response_schema.json
@@ -413,14 +413,36 @@
}
},
{
- "euiccUpdateResult": {
- "description": "contains the result of a parameter update in the euicc master data table",
- "type": "string",
- "enum": [
- "ok",
- "badParamFormat",
- "badParam"
- ]
+ "euiccStateResult": {
+ "description": "contains the result of an eUICC state set/get operation",
+ "type": "array",
+ "items": {
+ "counterValue": {
+ "description": "signature counter to a specified value (use with caution)",
+ "type": "integer"
+ },
+ "consumerEuicc": {
+ "description": "tells the eIM that the remote end (IPAd) uses a consumer eUICC with an IoT eUICC emulation mode",
+ "type": "boolean"
+ },
+ "associationToken": {
+ "description": "association token that the eUICC uses to identify this eIM internally",
+ "type": "integer"
+ },
+ "signPubKey": {
+ "description": "public key to be used for checking eUICC package results",
+ "type": "string",
+ "pattern": "^[0-9A-F].*$"
+ },
+ "signAlgo": {
+ "description": "algorithm to be used for checking eUICC package results",
+ "type": "string",
+ "enum": [
+ "prime256v1",
+ "brainpoolP256r1"
+ ]
+ }
+ }
}
},
{
@@ -514,6 +536,7 @@
"badPsmo",
"badEco",
"badEdr",
+ "badState",
"badOrder",
"abortedOrder",
"stuckOrder",
diff --git a/contrib/tryme_euicc_get_state.sh b/contrib/tryme_euicc_get_state.sh
new file mode 100755
index 0000000..524d37d
--- /dev/null
+++ b/contrib/tryme_euicc_get_state.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+. ./tryme.cfg
+
+# States we want to retrieve with this request.
+STATES='"counterValue", "consumerEuicc", "signAlgo", "signPubKey"'
+
+JSON='{ "eidValue" : "'$EID'", "order" : { "euicc" : { "get" : [ '$STATES' ] } } }'
+RC=`./restop.py -c -f euicc -j "$JSON"`
+echo $RC
+
+echo "---------------------------------------8<---------------------------------------"
+RESOURCE_ID=`echo $RC | cut -d '/' -f 6`
+echo "ResourceId =" $RESOURCE_ID
+./tryme_lookup.sh euicc $RESOURCE_ID
diff --git a/contrib/tryme_euicc_set_state.sh b/contrib/tryme_euicc_set_state.sh
new file mode 100755
index 0000000..d016ce1
--- /dev/null
+++ b/contrib/tryme_euicc_set_state.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+. ./tryme.cfg
+
+# States we want to set with this request (list does not have to be complete, states not listest here are left
+# unchanged.
+STATES='[{ "counterValue" : 1000 },
+ { "consumerEuicc" : false },
+ { "signAlgo" : "prime256v1" },
+ { "signPubKey" : "04BD2C55B28B4E801CA0B14F195788FD86C7FF49764C88A2934C69594A26FF647549F3F16060BD9C8D793D95D0126FA429C94966FE7842967263795A73C498F1DB" }]'
+
+JSON='{ "eidValue" : "'$EID'", "order" : { "euicc" : { "set" : '$STATES' } } }'
+RC=`./restop.py -c -f euicc -j "$JSON"`
+echo $RC
+
+echo "---------------------------------------8<---------------------------------------"
+RESOURCE_ID=`echo $RC | cut -d '/' -f 6`
+echo "ResourceId =" $RESOURCE_ID
+./tryme_lookup.sh euicc $RESOURCE_ID
diff --git a/contrib/tryme_set_euicc_param.sh b/contrib/tryme_set_euicc_param.sh
deleted file mode 100755
index 32c9ec9..0000000
--- a/contrib/tryme_set_euicc_param.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/bash
-. ./tryme.cfg
-
-JSON='{ "eidValue" : "'$EID'", "order" : { "euicc": [ { "counterValue" : 1000 }, { "consumerEuicc" : false }, { "signAlgo" : "prime256v1" }, { "signPubKey" : "04BD2C55B28B4E801CA0B14F195788FD86C7FF49764C88A2934C69594A26FF647549F3F16060BD9C8D793D95D0126FA429C94966FE7842967263795A73C498F1DB" } ] } }'
-RC=`./restop.py -c -f euicc -j "$JSON"`
-echo $RC
-
-echo "---------------------------------------8<---------------------------------------"
-RESOURCE_ID=`echo $RC | cut -d '/' -f 6`
-echo "ResourceId =" $RESOURCE_ID
-./tryme_lookup.sh euicc $RESOURCE_ID
diff --git a/doc/database.md b/doc/database.md
index 00c377e..8ae5471 100644
--- a/doc/database.md
+++ b/doc/database.md
@@ -88,10 +88,13 @@
### `euicc` Table
-The `euicc` table contains the eUICC master data. This is in particular the EID and other meta information that is
-required for the eIM to operate. The data in this table is usually automatically collected and updated by the eIM.
-However, there may be situations where the REST API user wants to update certain parameters of the `euicc` table.
-This is why the REST API allows to modify the contents of the `euicc` table via `orders` from the `euicc` `facility`
+The `euicc` table contains the eUICC state data for each eUICC. eUICC states are any type of information that is
+related to a specific eUICC (EID). A prominet example would be the `counterValue`, which is used for replay protection,
+but also the `signPubKey` is considered as an eUICC state, even though it remains constant.
+
+The states stored in this table are usually automatically collected and updated by the eIM. However, the REST API user
+may retrieve (get) or update (set) any of the states at any time if needed. eUICC states are managed using `orders`
+from the `euicc` `facility`
The `euicc` table maintains the following columns:
diff --git a/doc/examples.md b/doc/examples.md
index 889681d..9f3726a 100644
--- a/doc/examples.md
+++ b/doc/examples.md
@@ -123,7 +123,7 @@
information back. The returned data fields are in their ASN.1 encoded representation unless the requested tag consists
of a single primitive type (e.g. `associationToken`).
-### Setting a Parameters in the `euicc` Table
+### Setting States in the `euicc` Table
Even though the `euicc` table is populated automatically, it may still be that the REST API user wants to adjust
certain parameters. Let's assume that we have a setup that mostly uses consumer eUICCs in an IoT emulation mode. Now we
@@ -133,21 +133,43 @@
In this case we would craft an order like this:
```
-{ "eidValue" : "'$EID'", "order" : { "euicc": [ { "counterValue" : 1000 }, { "consumerEuicc" : false }, { "signAlgo" : "prime256v1" }, { "signPubKey" : "04BD2C55B28B4E801CA0B14F195788FD86C7FF49764C88A2934C69594A26FF647549F3F16060BD9C8D793D95D0126FA429C94966FE7842967263795A73C498F1DB" } ] } }'
+{ "eidValue" : "'$EID'", "order" : { "euicc" : { "set" : [ { "counterValue" : 1000 }, { "consumerEuicc" : false }, { "signAlgo" : "prime256v1" }, { "signPubKey" : "04BD2C55B28B4E801CA0B14F195788FD86C7FF49764C88A2934C69594A26FF647549F3F16060BD9C8D793D95D0126FA429C94966FE7842967263795A73C498F1DB" } ] } } }
```
We would edit the order into tryme_set_euicc_param.sh and run the script:
```
-./tryme_set_euicc_param.sh X 12345678900000000000000000001234
+./tryme_euicc_set_state.sh X 12345678900000000000000000001234
```
The order will execute as an internal process. This means that no IPAd interaction is involved. However, on the REST
-API the behavior will not be any different, except that the `status` will change from `new` to `done` directly. When
-all changes to the `euicc` table are made accordingly, we should get a result like this:
+API the behavior will not be any different. When all changes to the `euicc` table are made accordingly the eIM will
+echo the state values in the result:
```
-{"status": "done", "timestamp": "1721309044", "resource": {"eidValue": "12345678900000000000000000001234", "order": {"euicc": [{"counterValue": 1000}, {"consumerEuicc": false}, {"signAlgo": "prime256v1"}, {"signPubKey": "04BD2C55B28B4E801CA0B14F195788FD86C7FF49764C88A2934C69594A26FF647549F3F16060BD9C8D793D95D0126FA429C94966FE7842967263795A73C498F1DB"}]}}, "outcome": [{"euiccUpdateResult": "ok"}], "debuginfo": "836400046E6F6E65"}
+{"status": "done", "timestamp": "1783694896", "resource": {"eidValue": "12345678900000000000000000001234", "order": {"euicc": {"set": [{"counterValue": 1000}, {"consumerEuicc": false}, {"signAlgo": "prime256v1"}, {"signPubKey": "04BD2C55B28B4E801CA0B14F195788FD86C7FF49764C88A2934C69594A26FF647549F3F16060BD9C8D793D95D0126FA429C94966FE7842967263795A73C498F1DB"}]}}}, "outcome": [{"euiccStateResult": [{"counterValue": 1000}, {"consumerEuicc": false}, {"signAlgo": "prime256v1"}, {"signPubKey": "04BD2C55B28B4E801CA0B14F195788FD86C7FF49764C88A2934C69594A26FF647549F3F16060BD9C8D793D95D0126FA429C94966FE7842967263795A73C498F1DB"}]}], "debuginfo": "8377046E6F6E65"}
```
Now the eIM will treat the eUICC as a native IoT eUICC, a proper public key set and the counter value is high enough
so that we can sign new eUICC packages correctly.
+
+### Retrieving a States from the `euicc` Table
+
+eUICC states stored in the `euicc` Table can be retrieved at any time by sending a list with the states we want to
+retrieve to the eIM. The list does not have to be complete. It is possible to limit the focus on a small subset of
+states. The following example shows an order that would retrieve the `counterValue`, the `consumerEuicc` flag, the
+`signAlgo` and the `signPubKey`:
+
+```
+{ "eidValue" : "12345678900000000000000000001234", "order" : { "euicc" : { "get" : [ "counterValue", "consumerEuicc", "signAlgo", "signPubKey" ] } } }
+```
+
+We would edit the order into tryme_set_euicc_param.sh and run the script:
+```
+./tryme_euicc_get_state.sh X 12345678900000000000000000001234
+```
+
+The eIM will respond with a list of key value pairs of the requested eUICC states:
+
+```
+{"status": "done", "timestamp": "1783695656", "resource": {"eidValue": "12345678900000000000000000001234", "order": {"euicc": {"get": ["counterValue", "consumerEuicc", "signAlgo", "signPubKey"]}}}, "outcome": [{"euiccStateResult": [{"counterValue": 1000}, {"consumerEuicc": false}, {"signAlgo": "prime256v1"}, {"signPubKey": "04BD2C55B28B4E801CA0B14F195788FD86C7FF49764C88A2934C69594A26FF647549F3F16060BD9C8D793D95D0126FA429C94966FE7842967263795A73C498F1DB"}]}], "debuginfo": "8377046E6F6E65"}
+```
diff --git a/doc/rest_api.md b/doc/rest_api.md
index d49299e..59af59c 100644
--- a/doc/rest_api.md
+++ b/doc/rest_api.md
@@ -13,7 +13,7 @@
* `psmo`: Profile State Management Operations (PSMO)
* `eco`: Eim Configuration Operations (eCO)
* `edr`: eUICC data request (see also GSMA SGP.32, section 2.11.1.2)
-* `euicc`: eim-local eUICC configuration Operations
+* `euicc`: eim-local eUICC state update (set) or retrieve (get) operations
The purpose of the facilities is to provide separation at URL level. This allows for easier filtering to restrict
access for specific REST API users.
diff --git a/src/crypto_utils.erl b/src/crypto_utils.erl
index 63abc59..21929b5 100644
--- a/src/crypto_utils.erl
+++ b/src/crypto_utils.erl
@@ -40,7 +40,7 @@
sign_euiccPackageSigned(EuiccPackageSigned, EidValue) ->
% Read the AssociationToken
- {ok, AssociationToken} = mnesia_db_euicc:param_get(EidValue, associationToken),
+ {ok, AssociationToken} = mnesia_db_euicc:state_get(EidValue, associationToken),
%Format message to be signed
{ok, EuiccPackageSignedEnc} = 'SGP32Definitions':encode(
@@ -64,10 +64,10 @@
verify_signature(Message, Signature, EidValue) ->
DERSignature = plain_to_der(Signature),
- {ok, SubjectPublicKeyHex} = mnesia_db_euicc:param_get(EidValue, signPubKey),
+ {ok, SubjectPublicKeyHex} = mnesia_db_euicc:state_get(EidValue, signPubKey),
SubjectPublicKey = utils:hex_to_binary(SubjectPublicKeyHex),
- {ok, SignAlgo} = mnesia_db_euicc:param_get(EidValue, signAlgo),
+ {ok, SignAlgo} = mnesia_db_euicc:state_get(EidValue, signAlgo),
NamedCurve =
case SignAlgo of
<<"prime256v1">> ->
@@ -108,11 +108,11 @@
end.
verify_euiccPackageResultSigned(EuiccPackageResult, EidValue) ->
- {ok, ConsumerEuicc} = mnesia_db_euicc:param_get(EidValue, consumerEuicc),
+ {ok, ConsumerEuicc} = mnesia_db_euicc:state_get(EidValue, consumerEuicc),
case ConsumerEuicc of
false ->
% Read the AssociationToken
- {ok, AssociationToken} = mnesia_db_euicc:param_get(EidValue, associationToken),
+ {ok, AssociationToken} = mnesia_db_euicc:state_get(EidValue, associationToken),
case EuiccPackageResult of
{euiccPackageResultSigned, EuiccPackageResultSigned} ->
@@ -275,15 +275,15 @@
_ ->
<<"unknown">>
end,
- ok = mnesia_db_euicc:param_set(EidValue, signPubKey, utils:binary_to_hex(SignPubKey)),
- ok = mnesia_db_euicc:param_set(EidValue, signAlgo, SignAlgo),
+ ok = mnesia_db_euicc:state_set(EidValue, signPubKey, utils:binary_to_hex(SignPubKey)),
+ ok = mnesia_db_euicc:state_set(EidValue, signAlgo, SignAlgo),
ok;
_ ->
error
end.
store_euicc_pubkey_from_authenticateResponseOk(AuthRespOk, EidValue) ->
- case mnesia_db_euicc:param_get(EidValue, signPubKey) of
+ case mnesia_db_euicc:state_get(EidValue, signPubKey) of
{ok, <<>>} ->
% There is no public key stored yet for this eUICC, use the public
% key provided in the eUICC certificate
@@ -296,7 +296,7 @@
end.
store_euicc_pubkey_from_ipaEuiccDataResponse(IpaEuiccDataResponse, EidValue) ->
- case mnesia_db_euicc:param_get(EidValue, signPubKey) of
+ case mnesia_db_euicc:state_get(EidValue, signPubKey) of
{ok, <<>>} ->
% There is no public key stored yet for this eUICC, use the public
% key provided in the eUICC certificate
diff --git a/src/esipa_asn1_handler_utils.erl b/src/esipa_asn1_handler_utils.erl
index d7927cb..8d6e59a 100644
--- a/src/esipa_asn1_handler_utils.erl
+++ b/src/esipa_asn1_handler_utils.erl
@@ -53,7 +53,7 @@
CheckCounterValue = fun(Map) ->
{EidValue, _, _} = mnesia_db_work:pickup(Pid, EimTransactionId),
CounterValueIpad = maps:get(counterValue, Map),
- {ok, CounterValueEim} = mnesia_db_euicc:param_get(EidValue, counterValue),
+ {ok, CounterValueEim} = mnesia_db_euicc:state_get(EidValue, counterValue),
case CounterValueIpad of
CounterValueEim ->
ok;
diff --git a/src/mnesia_db.erl b/src/mnesia_db.erl
index d8e7afa..63028c4 100644
--- a/src/mnesia_db.erl
+++ b/src/mnesia_db.erl
@@ -61,8 +61,8 @@
logger:notice(" work table created\n")
end,
- % The euicc table will store the eUICC master data, such as the eID and the counterValue that is required for
- % the replay protection.
+ % The euicc table will store eUICC states, such as the eID and the counterValue that is required for the replay
+ % protection.
case
mnesia:create_table(
euicc,
diff --git a/src/mnesia_db_euicc.erl b/src/mnesia_db_euicc.erl
index 013b377..5a5634f 100644
--- a/src/mnesia_db_euicc.erl
+++ b/src/mnesia_db_euicc.erl
@@ -4,13 +4,17 @@
%
% Author: Philipp Maier <pmaier(a)sysmocom.de> / sysmocom - s.f.m.c. GmbH
+% An eUICC procedure in the context of this module has nothing to do with any of the procedures specified in
+% GSMA SGP.22 or SGP.32. In this module an eUICC procedure is a virtual procedure were states in the
+% euicc table are set.
+
-module(mnesia_db_euicc).
-include_lib("stdlib/include/qlc.hrl").
-include("mnesia_db_rest.hrl").
-include("mnesia_db_euicc.hrl").
% euicc functions, to be called by the eIM code (from inside)
--export([counter_tick/1, param_get/2, param_set/3, create_if_not_exist/1]).
+-export([counter_tick/1, state_get/2, state_set/3, create_if_not_exist/1]).
% debugging
-export([dump/0]).
@@ -38,7 +42,7 @@
present
end.
-% Create a new eUICC master data entry
+% Create a new eUICC state data entry
create_if_not_exist(EidValue) ->
Trans = fun() ->
trans_create_if_not_exist(EidValue)
@@ -46,14 +50,16 @@
{atomic, Result} = mnesia:transaction(Trans),
case Result of
ok ->
- logger:info("eUICC: creating new master data entry,~neID=~p~n", [EidValue]),
+ logger:info("eUICC: creating new eUICC state data entry,~neID=~p~n", [EidValue]),
ok;
present ->
ok;
_ ->
- logger:error("eUICC: cannot create master data entry, database error,~neID=~p~n", [
- EidValue
- ]),
+ logger:error(
+ "eUICC: cannot create new eUICC state data entry, database error,~neID=~p~n", [
+ EidValue
+ ]
+ ),
error
end.
@@ -88,8 +94,8 @@
error
end.
-%update one specific parameter in the euicc table
-trans_update_param(EidValue, Name, Value) ->
+trans_state_set(EidValue, Name, Value) ->
+ trans_create_if_not_exist(EidValue),
Q = qlc:q([X || X <- mnesia:table(euicc), X#euicc.eidValue == EidValue]),
Rows = qlc:e(Q),
case Rows of
@@ -106,121 +112,100 @@
signAlgo ->
mnesia:write(Row#euicc{signAlgo = Value});
_ ->
- error
+ throw(badState)
end;
[] ->
- error;
+ throw(eidUnknown);
_ ->
- error
+ throw(undefinedError)
end.
-% Get an eUICC parameter by its name (atom)
-param_get(EidValue, Name) ->
- Trans = fun() ->
- Q = qlc:q([X || X <- mnesia:table(euicc), X#euicc.eidValue == EidValue]),
- Rows = qlc:e(Q),
- case Rows of
- [Row | _] ->
- case Name of
- counterValue ->
- {ok, Row#euicc.counterValue};
- consumerEuicc ->
- {ok, Row#euicc.consumerEuicc};
- associationToken ->
- {ok, Row#euicc.associationToken};
- signPubKey ->
- {ok, Row#euicc.signPubKey};
- signAlgo ->
- {ok, Row#euicc.signAlgo};
- _ ->
- error
- end;
- [] ->
- error;
- _ ->
- error
- end
- end,
+trans_state_get(EidValue, Name) ->
+ Q = qlc:q([X || X <- mnesia:table(euicc), X#euicc.eidValue == EidValue]),
+ Rows = qlc:e(Q),
+ case Rows of
+ [Row | _] ->
+ case Name of
+ counterValue ->
+ Row#euicc.counterValue;
+ consumerEuicc ->
+ Row#euicc.consumerEuicc;
+ associationToken ->
+ Row#euicc.associationToken;
+ signPubKey ->
+ Row#euicc.signPubKey;
+ signAlgo ->
+ Row#euicc.signAlgo;
+ _ ->
+ throw(badState)
+ end;
+ [] ->
+ throw(eidUnknown);
+ _ ->
+ throw(undefinedError)
+ end.
- {atomic, Result} = mnesia:transaction(Trans),
+% Get an eUICC state by its name (atom)
+state_get(EidValue, Name) ->
+ Trans = fun() ->
+ trans_state_get(EidValue, Name)
+ end,
+ Result = mnesia:transaction(Trans),
case Result of
- {ok, Value} ->
- logger:info("eUICC: reading eUICC parameter,~neID=~p, name=~p, value=~p~n", [
+ {atomic, Value} ->
+ logger:info("eUICC: reading eUICC state,~neID=~p, name=~p, value=~p~n", [
EidValue, Name, Value
]),
{ok, Value};
+ {aborted, {throw, ErrorCode}} ->
+ logger:error(
+ "eUICC: reading of eUICC state failed with error code ~p,~neID=~p, name=~p~n", [
+ ErrorCode, EidValue, Name
+ ]
+ ),
+ error;
_ ->
- logger:error("eUICC: cannot read eUICC parameter,~neID=~p, name=~p~n", [EidValue, Name]),
+ logger:error("eUICC: reading of eUICC state failed,~neID=~p, name=~p~n", [
+ EidValue, Name
+ ]),
error
end.
-% Update an eUICC parameter by its name (atom)
-param_set(EidValue, Name, Value) ->
+% Update an eUICC state by its name (atom)
+state_set(EidValue, Name, Value) ->
Trans = fun() ->
- trans_update_param(EidValue, Name, Value)
+ trans_state_set(EidValue, Name, Value)
end,
- {atomic, Result} = mnesia:transaction(Trans),
+ Result = mnesia:transaction(Trans),
case Result of
- ok ->
- logger:info("eUICC: writing eUICC parameter,~neID=~p, name=~p, value=~p~n", [
+ {atomic, ok} ->
+ logger:info("eUICC: writing eUICC state,~neID=~p, name=~p, value=~p~n", [
EidValue, Name, Value
]),
ok;
+ {aborted, {throw, ErrorCode}} ->
+ logger:error(
+ "eUICC: writing of eUICC state failed with error code ~p,~neID=~p, name=~p~n", [
+ ErrorCode, EidValue, Name
+ ]
+ ),
+ error;
_ ->
- logger:error("eUICC: cannot write eUICC parameter,~neID=~p, name=~p~n", [EidValue, Name]),
+ logger:error("eUICC: writing of eUICC state failed,~neID=~p, name=~p~n", [
+ EidValue, Name
+ ]),
error
end.
-% Handle REST requests in regular intervals
-timer_rest() ->
- % An eUICC procedure in the context of this module has nothing to do with any of the procedures specified in
- % GSMA SGP.22 or SGP.32. In this module an eUICC procedure is a virtual procedure were parameters in the
- % euicc table are set.
+timer_rest_fetch() ->
+ % Fetch the rest resources with incoming orders from the rest table and set their status (wihich is initially set
+ % to "new" to "work", so that the REST API user knows that the order has been accepted for processing. Finally, we
+ % will return the fetched rest resources back to the caller, so that he can proceed with the processing of the
+ % orders.
- HandleParam = fun(ResourceId, EidValue, Param) ->
- case Param of
- {[{Name, Value}]} ->
- case trans_update_param(EidValue, binary_to_atom(Name), Value) of
- ok ->
- mnesia_db_rest:trans_set_status(
- ResourceId,
- done,
- [{[{euiccUpdateResult, ok}]}],
- none
- );
- _ ->
- mnesia_db_rest:trans_set_status(
- ResourceId,
- done,
- [{[{euiccUpdateResult, badParam}]}],
- none
- )
- end;
- _ ->
- mnesia_db_rest:trans_set_status(
- ResourceId,
- done,
- [{[{euiccUpdateResult, badParamFormat}]}],
- none
- )
- end
+ SetStatus = fun({ResourceId, _EidValue, _Order}) ->
+ mnesia_db_rest:trans_set_status(ResourceId, work, [], none)
end,
-
- % Parse order and process each parameter individually
- HandleResource = fun({ResourceId, EidValue, Order}) ->
- trans_create_if_not_exist(EidValue),
- case Order of
- {[{<<"euicc">>, ParameterList}]} ->
- [HandleParam(ResourceId, EidValue, Param) || Param <- ParameterList],
- ok;
- _ ->
- mnesia_db_rest:trans_set_status(
- ResourceId, done, [{[{procedureError, badOrder}]}], none
- )
- end
- end,
-
- % Look into facility euicc and find the first entry that is in status "new".
Trans = fun() ->
Q = qlc:q([
{X#rest.resourceId, X#rest.eidValue, X#rest.order}
@@ -229,27 +214,99 @@
Rows = qlc:e(Q),
case Rows of
[] ->
- ok;
+ [];
Rows ->
- [HandleResource(Row) || Row <- Rows],
- ok
+ [SetStatus(Row) || Row <- Rows],
+ Rows
end
end,
- {atomic, Result} = mnesia:transaction(Trans),
+ Result = mnesia:transaction(Trans),
case Result of
- ok ->
- ok;
+ {atomic, Resources} ->
+ {ok, Resources};
_ ->
- logger:error("eUICC: euicc procedure failed, database error~n"),
+ logger:error("eUICC: order fetch failed, database error~n"),
error
+ end.
+
+timer_rest_process(RestResource) ->
+ % Process the order contained in the given REST resource and return the outcome when done.
+
+ HandleStateSet = fun(EidValue, State) ->
+ case State of
+ {[{Name, Value}]} ->
+ ok = trans_state_set(EidValue, binary_to_atom(Name), Value),
+ {[{Name, Value}]};
+ _ ->
+ throw(badOrder)
+ end
+ end,
+ HandleStateGet = fun(EidValue, Name) ->
+ {[{Name, trans_state_get(EidValue, binary_to_atom(Name))}]}
+ end,
+ Trans = fun() ->
+ {_ResourceId, EidValue, Order} = RestResource,
+ case Order of
+ {[{<<"euicc">>, {[{<<"set">>, States}]}}]} ->
+ [{[{euiccStateResult, [HandleStateSet(EidValue, State) || State <- States]}]}];
+ {[{<<"euicc">>, {[{<<"get">>, States}]}}]} ->
+ [{[{euiccStateResult, [HandleStateGet(EidValue, State) || State <- States]}]}];
+ _ ->
+ throw(badOrder)
+ end
end,
- % Next euicc procedure in 10 secs.
- {ok, _} = timer:apply_after(10000, mnesia_db_euicc, timer_rest, []),
+ Result = mnesia:transaction(Trans),
+ case Result of
+ {atomic, Outcome} ->
+ Outcome;
+ {aborted, {throw, ErrorCode}} ->
+ logger:error("eUICC: order process failed with error code ~p~n", [ErrorCode]),
+ [{[{procedureError, ErrorCode}]}];
+ _ ->
+ logger:error("eUICC: order process failed, database error~n"),
+ [{[{procedureError, undefinedError}]}]
+ end.
+
+timer_rest_finish(RestResource, Outcome) ->
+ % Write the given outcome to the given REST resource and set the status to "done" to notify the REST API user that
+ % the order processing has finished.
+
+ Trans = fun() ->
+ {ResourceId, _EidValue, _Order} = RestResource,
+ mnesia_db_rest:trans_set_status(ResourceId, done, Outcome, none)
+ end,
+
+ Result = mnesia:transaction(Trans),
+ case Result of
+ {atomic, ok} ->
+ ok;
+ {aborted, {throw, ErrorCode}} ->
+ logger:error("eUICC: order finish failed with error code ~p~n", [ErrorCode]),
+ error;
+ _ ->
+ logger:error("eUICC: order finish failed, database error~n"),
+ error
+ end.
+
+% Handle euicc table related REST resources in regular intervals.
+timer_rest() ->
+ % Fetch REST resources with new orders from rest table and mark them as work in progress
+ {ok, RestResources} = timer_rest_fetch(),
+
+ % Process each REST resource individually
+ ProcessRestResource = fun(RestResource) ->
+ Outcome = timer_rest_process(RestResource),
+ timer_rest_finish(RestResource, Outcome)
+ end,
+ [ProcessRestResource(RestResource) || RestResource <- RestResources],
+
+ % Next euicc procedure in 1 sec.
+ {ok, _} = timer:apply_after(1000, mnesia_db_euicc, timer_rest, []),
ok.
-% Dump all eUICCs we are aware of
+% Dump all eUICC states we are aware of
dump() ->
Trans = fun() ->
Q = qlc:q([X || X <- mnesia:table(euicc)]),
--
To view, visit https://gerrit.osmocom.org/c/onomondo-eim/+/42993?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: merged
Gerrit-Project: onomondo-eim
Gerrit-Branch: master
Gerrit-Change-Id: I74c602555b194a28d6eb9dd67ec4c6a8865fcb77
Gerrit-Change-Number: 42993
Gerrit-PatchSet: 11
Gerrit-Owner: dexter <pmaier(a)sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: dexter <pmaier(a)sysmocom.de>
Gerrit-Reviewer: laforge <laforge(a)osmocom.org>