dexter has uploaded this change for review.

View Change

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.

Related: SYS#8100

The original purpose of the euicc table was to hold static params
for an eUICC (e.g. the public key or the flag that tells if it is
a consumer eUICC or not). Now that we plan to extend the scope
of the euicc table, it makes more sense to talk about states rather
than parameters.

Change-Id: I74c602555b194a28d6eb9dd67ec4c6a8865fcb77
---
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, 292 insertions(+), 173 deletions(-)

git pull ssh://gerrit.osmocom.org:29418/onomondo-eim refs/changes/93/42993/1
diff --git a/contrib/rest_api_resource_schema.json b/contrib/rest_api_resource_schema.json
index ce1ae7d..6519dfa 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-9,A-F]{2,32}$"
+ },
+ "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 7ec2735..f2a5165 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-9,A-F]{2,32}$"
+ },
+ "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..1ca8b9f
--- /dev/null
+++ b/contrib/tryme_euicc_set_state.sh
@@ -0,0 +1,15 @@
+#!/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 0d77ba7..ead177f 100644
--- a/src/esipa_asn1_handler_utils.erl
+++ b/src/esipa_asn1_handler_utils.erl
@@ -51,7 +51,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..9f27ed3 100644
--- a/src/mnesia_db_euicc.erl
+++ b/src/mnesia_db_euicc.erl
@@ -4,13 +4,17 @@
%
% Author: Philipp Maier <pmaier@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,82 @@
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};
_ ->
- logger:error("eUICC: cannot read eUICC parameter,~neID=~p, name=~p~n", [EidValue, Name]),
+ logger:error("eUICC: cannot read eUICC state,~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;
_ ->
- logger:error("eUICC: cannot write eUICC parameter,~neID=~p, name=~p~n", [EidValue, Name]),
+ logger:error("eUICC: cannot write eUICC state,~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 +196,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,
+
+ 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 10 secs.
{ok, _} = timer:apply_after(10000, 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 change 42993. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-MessageType: newchange
Gerrit-Project: onomondo-eim
Gerrit-Branch: master
Gerrit-Change-Id: I74c602555b194a28d6eb9dd67ec4c6a8865fcb77
Gerrit-Change-Number: 42993
Gerrit-PatchSet: 1
Gerrit-Owner: dexter <pmaier@sysmocom.de>