(Solved) Controlling access to RPC call/transport combinations in code

I need to disable some RPC calls from certain transports (specifically trying to restrict access over network, while still allowing full access over the console).

the ACLs seems to be for restricting by user.

I remember asking about this last year and receiving a response indicating that there was a way to do this in some form of callback, but I cannot find a forum posting in this forum or the old one.

is this possible?

That is a configuration parameter, see mos config-get rpc and various .enable options.

I do not want to enable or disable entire channels. I want to look at the combination of channel and service and enable/disable those combinations individually. Unless I missed something, .enable would only give me the ability to turn all shadow RPC access off and on, not give me the ability to do the fine grained control?

I want to have the OTA and CONFIG service accessible from the console, but only OTA accessible via WS (no CONFIG allowed via WS).

That’s an elaborate fine-grained control you’re looking for - Mongoose OS does not provide such out of the box.
One way of doing that is to make a wrapper RPC functions that check channel type before calling “mother” functions.
Another way is to tinker with the core library.

I dug into the source code of mg_rpc.c.

I think I can do it with a prehandler, need to research how to set one up.

Prehandler works! It needs to send an error response if it’s denying the request, else the client hangs. I don’t have access to client information from the prehandler, but there you can see the channel, method, and args.

Example that let’s you call the ‘sum’ method from websocket, but not the ‘sum2’ method.

static bool my_rpc_prehandler (struct mg_rpc_request_info *ri, void *cb_arg,
      struct mg_rpc_frame_info *fi, struct mg_str args) {

  LOG(LL_INFO, ("prehandler hit: %.*s via %s, args %.*s",
    (int) ri->method.len,
    ri->method.p,
    fi->channel_type,
    (int) args.len,
    args.p
  ));

  if (mg_vcmp(&ri->method, "sum2") == 0 && strcmp(fi->channel_type, "WS_in") == 0) {
    LOG (LL_WARN, ("no, you can't call %.*s from %s", (int) ri->method.len, ri->method.p, fi->channel_type));
    mg_rpc_send_errorf(ri, -1, "no, you can't call %.*s from %s", (int) ri->method.len, ri->method.p, fi->channel_type);
    return false;
  }

  return true;
}

void rpc_filter_setup() {
  struct mg_rpc * global_rpc = mgos_rpc_get_global();
  mg_rpc_set_prehandler (global_rpc, my_rpc_prehandler, NULL);
}

static void sum_rpc(struct mg_rpc_request_info *ri, void *cb_arg,
                   struct mg_rpc_frame_info *fi, struct mg_str args) {
  double a = 0, b = 0;
  if (json_scanf(args.p, args.len, ri->args_fmt, &a, &b) == 2) {
    mg_rpc_send_responsef(ri, "%.2lf", a + b);
  } else {
    mg_rpc_send_errorf(ri, -1, "Bad request. Expected: {\"a\":N1,\"b\":N2}");
  }
  (void) cb_arg;
  (void) fi;
}

// and somewhere
  mg_rpc_add_handler(mgos_rpc_get_global(), "sum", "{a: %lf, b: %lf}", sum_rpc, NULL);
  mg_rpc_add_handler(mgos_rpc_get_global(), "sum2", "{a: %lf, b: %lf}", sum_rpc, NULL);

Client usage looks like this:

$ mos --port ws://sti_B4E62DD60069.attlocal.net/rpc call sum '{"a":1, "b": 2}'
3.00
$ mos --port ws://sti_B4E62DD60069.attlocal.net/rpc call sum2 '{"a":1, "b": 2}'
Error: /build/mos-i_6ltm/mos-2.13.1+2034cc0~bionic0/obj-x86_64-linux-gnu/src/cesanta.com/mos/dev/dev_conn_impl.go:156: remote error -1: no, you can't call sum2 from WS_in
/build/mos-i_6ltm/mos-2.13.1+2034cc0~bionic0/obj-x86_64-linux-gnu/src/cesanta.com/mos/dev/dev_conn_impl.go:165: 
/build/mos-i_6ltm/mos-2.13.1+2034cc0~bionic0/obj-x86_64-linux-gnu/src/cesanta.com/mos/main.go:178: call failed
$ # can work via http though
$ curl -d '{"a":1, "b": 2}' http://sti_B4E62DD60069.attlocal.net/rpc/sum
3.00
$ curl -d '{"a":1, "b": 2}' http://sti_B4E62DD60069.attlocal.net/rpc/sum2
3.00

and the device log:

[Jun  5 16:24:28.261] mgos_http_server.c:176  0x3ffeb14c HTTP connection from 192.168.1.212:55782
[Jun  5 16:24:28.276] rpc_filter.c:19         prehandler hit: sum via WS_in, args {"a":1,"b":2}
[Jun  5 16:24:28.282] mg_rpc.c:293            sum via WS_in 192.168.1.212:55782
[Jun  5 16:24:34.771] mgos_http_server.c:176  0x3ffeb14c HTTP connection from 192.168.1.212:55784
[Jun  5 16:24:34.786] rpc_filter.c:19         prehandler hit: sum2 via WS_in, args {"a":1,"b":2}
[Jun  5 16:24:34.793] rpc_filter.c:23         no, you can't call sum2 from WS_in
[Jun  5 16:33:47.625] mgos_http_server.c:176  0x3ffeb14c HTTP connection from 192.168.1.212:55886
[Jun  5 16:33:47.634] rpc_filter.c:19         prehandler hit: sum via HTTP, args {"a":1, "b": 2}
[Jun  5 16:33:47.641] mg_rpc.c:293            sum via HTTP 192.168.1.212:55886
[Jun  5 16:33:49.746] mgos_http_server.c:176  0x3ffeb14c HTTP connection from 192.168.1.212:55888
[Jun  5 16:33:49.755] rpc_filter.c:19         prehandler hit: sum2 via HTTP, args {"a":1, "b": 2}
[Jun  5 16:33:49.762] mg_rpc.c:293            sum2 via HTTP 192.168.1.212:55888

1 Like