From 1536b6a38b147f778ca50a4516661d92ccd8a417 Mon Sep 17 00:00:00 2001 From: cpq Date: Wed, 20 May 2026 06:56:55 +0100 Subject: [PATCH] Dash levels --- mongoose.c | 52 +++-- src/dash.c | 52 +++-- test/unit_test.c | 187 +++++++++++++++++- tutorials/device-dashboard/full/dashboard.c | 10 +- .../device-dashboard/full/dashboard.html | 4 +- 5 files changed, 254 insertions(+), 51 deletions(-) diff --git a/mongoose.c b/mongoose.c index 32e16615..61c6aed2 100644 --- a/mongoose.c +++ b/mongoose.c @@ -150,19 +150,22 @@ static struct mg_str trimq(struct mg_str s) { // Trim double quotes return s; } -static void mg_dash_broadcast(struct mg_mgr *mgr, const char *fmt, ...) { +static void mg_dash_broadcast(struct mg_mgr *mgr, int level, const char *fmt, + ...) { struct mg_connection *c; va_list ap; for (c = mgr->conns; c != NULL; c = c->next) { + int user_level = *(int *) c->data; if (!c->is_websocket) continue; + if (level > 0 && user_level < level) continue; if (c->send.len > MG_DASH_MAX_SEND_BUF_SIZE) { - MG_ERROR(("%lu buffered data %lu > MG_DASH_MAX_SEND_BUF_SIZE, throttled", - c->id, c->send.len)); - continue; + mg_error(c, "%lu buffered data %lu > MG_DASH_MAX_SEND_BUF_SIZE", c->id, + c->send.len); + } else { + va_start(ap, fmt); + mg_ws_vprintf(c, WEBSOCKET_OP_TEXT, fmt, &ap); + va_end(ap); } - va_start(ap, fmt); - mg_ws_vprintf(c, WEBSOCKET_OP_TEXT, fmt, &ap); - va_end(ap); } } @@ -246,6 +249,7 @@ static size_t mg_print_field_set(mg_pfn_t fn, void *arg, va_list *ap) { static size_t mg_dash_print_name(mg_pfn_t fn, void *arg, va_list *ap) { struct mg_dash *dash = va_arg(*ap, struct mg_dash *); struct mg_str *name = va_arg(*ap, struct mg_str *); + int level = va_arg(*ap, int); struct mg_field_set *set = mg_dash_find_field_set(dash, *name); size_t n = 0; if (name->len == 0) { @@ -253,6 +257,7 @@ static size_t mg_dash_print_name(mg_pfn_t fn, void *arg, va_list *ap) { const char *comma = ""; n += mg_xprintf(fn, arg, "{"); for (fs = dash->sets; fs != NULL; fs = fs->next) { + if (fs->read_level > 0 && level < fs->read_level) continue; if (fs->reader) fs->reader(); n += mg_xprintf(fn, arg, comma); n += mg_xprintf(fn, arg, "%m:", MG_ESC(fs->name)); @@ -260,7 +265,8 @@ static size_t mg_dash_print_name(mg_pfn_t fn, void *arg, va_list *ap) { comma = ","; } n += mg_xprintf(fn, arg, "}"); - } else if (set != NULL) { + } else if (set != NULL && + (set->read_level <= 0 || level >= set->read_level)) { if (set->reader) set->reader(); n += mg_xprintf(fn, arg, "%M", mg_print_field_set, set); } else { @@ -272,15 +278,16 @@ static size_t mg_dash_print_name(mg_pfn_t fn, void *arg, va_list *ap) { static size_t mg_dash_print(mg_pfn_t fn, void *arg, va_list *ap) { struct mg_str *req = va_arg(*ap, struct mg_str *); struct mg_dash *dash = va_arg(*ap, struct mg_dash *); + int level = va_arg(*ap, int); struct mg_str name = trimq(mg_json_get_tok(*req, "$.params")); - return mg_xprintf(fn, arg, "%M", mg_dash_print_name, dash, &name); + return mg_xprintf(fn, arg, "%M", mg_dash_print_name, dash, &name, level); } void mg_dash_send_change(struct mg_mgr *mgr, struct mg_field_set *set) { if (set->reader) set->reader(); - mg_dash_broadcast(mgr, "{%m:%m,%m:{%m:%M}}", MG_ESC("method"), - MG_ESC("change"), MG_ESC("params"), MG_ESC(set->name), - mg_print_field_set, set); + mg_dash_broadcast(mgr, set->read_level, "{%m:%m,%m:{%m:%M}}", + MG_ESC("method"), MG_ESC("change"), MG_ESC("params"), + MG_ESC(set->name), mg_print_field_set, set); } static int mg_dash_parse_field(struct mg_str json, struct mg_field *f) { @@ -310,14 +317,14 @@ static int mg_dash_parse_field(struct mg_str json, struct mg_field *f) { } static int mg_dash_apply(struct mg_connection *c, struct mg_dash *dash, - struct mg_str json) { + struct mg_str json, int level) { struct mg_str key, val; size_t ofs = 0; int total_count = 0; while ((ofs = mg_json_next(json, ofs, &key, &val)) > 0) { struct mg_field_set *set = mg_dash_find_field_set(dash, trimq(key)); int count = 0; - if (set != NULL) { + if (set != NULL && (set->write_level <= 0 || level >= set->write_level)) { size_t i; for (i = 0; set->fields[i].name != NULL; i++) { if (mg_dash_parse_field(val, &set->fields[i])) count++; @@ -327,7 +334,7 @@ static int mg_dash_apply(struct mg_connection *c, struct mg_dash *dash, mg_dash_send_change(c->mgr, set); total_count += count; } - } else { + } else if (set == NULL) { MG_ERROR(("UNKNOWN SET: [%.*s]", key.len, key.buf)); } } @@ -339,11 +346,12 @@ static void mg_dash_process_msg(struct mg_connection *c, struct mg_dash *dash) { struct mg_str req = wm->data; struct mg_str method = trimq(mg_json_get_tok(req, "$.method")); + int level = *(int *) c->data; if (mg_match(method, mg_str("get"), NULL)) { - mg_dash_success(c, req, "%M", mg_dash_print, &req, dash); + mg_dash_success(c, req, "%M", mg_dash_print, &req, dash, level); } else if (mg_match(method, mg_str("set"), NULL)) { struct mg_str params = trimq(mg_json_get_tok(req, "$.params")); - int count = mg_dash_apply(c, dash, params); + int count = mg_dash_apply(c, dash, params, level); mg_dash_success(c, req, "%d", count); } else { mg_dash_error(c, req, "%s", "unknown method"); @@ -632,7 +640,8 @@ void mg_dash_ev_handler(struct mg_connection *c, int ev, void *ev_data) { c->is_resp = 0; } else if (ev == MG_EV_HTTP_MSG && c->data[0] == '\0') { struct mg_http_message *hm = (struct mg_http_message *) ev_data; - // struct mg_dash_user *u = mg_dash_authenticate(hm, dash); + struct mg_dash_user *u = mg_dash_authenticate(hm, dash); + int level = u == NULL ? 0 : u->level; struct mg_str parts[5]; memset(parts, 0, sizeof(parts)); @@ -643,6 +652,7 @@ void mg_dash_ev_handler(struct mg_connection *c, int ev, void *ev_data) { mg_ws_printf(c, WEBSOCKET_OP_TEXT, "{%m:%m}", MG_ESC("method"), MG_ESC("logout")); } else if (mg_match(hm->uri, mg_str("/api/websocket"), NULL)) { + *(int *) c->data = level; mg_ws_upgrade(c, hm, NULL); } else if (mg_match(hm->uri, mg_str("/fs/#"), NULL)) { struct mg_str name = mg_dash_file_name(hm); @@ -660,9 +670,9 @@ void mg_dash_ev_handler(struct mg_connection *c, int ev, void *ev_data) { } else if (mg_match(hm->uri, mg_str("/api/get/#"), parts) || mg_match(hm->uri, mg_str("/api/get"), NULL)) { mg_http_reply(c, 200, MG_JSON_HEADERS, "%M\n", mg_dash_print_name, dash, - &parts[0]); + &parts[0], level); } else if (mg_match(hm->uri, mg_str("/api/set"), NULL)) { - int count = mg_dash_apply(c, dash, hm->body); + int count = mg_dash_apply(c, dash, hm->body, level); mg_http_reply(c, 200, MG_JSON_HEADERS, "%d\n", count); } else if (mg_match(hm->uri, mg_str("/"), NULL)) { struct mg_http_serve_opts opts; @@ -686,7 +696,7 @@ void mg_dash_ev_handler(struct mg_connection *c, int ev, void *ev_data) { for (fs = dash->sets; fs != NULL; fs = fs->next) { mg_dash_send_change(c->mgr, fs); } - mg_dash_broadcast(c->mgr, "{%m:%m}", MG_ESC("method"), MG_ESC("ready")); + mg_dash_broadcast(c->mgr, 0, "{%m:%m}", MG_ESC("method"), MG_ESC("ready")); } else if (ev == MG_EV_WS_MSG) { // Add this to automatically handle "get" and "set" JSON-RPC calls struct mg_ws_message *wm = (struct mg_ws_message *) ev_data; diff --git a/src/dash.c b/src/dash.c index 7538a717..cbcb3594 100644 --- a/src/dash.c +++ b/src/dash.c @@ -29,19 +29,22 @@ static struct mg_str trimq(struct mg_str s) { // Trim double quotes return s; } -static void mg_dash_broadcast(struct mg_mgr *mgr, const char *fmt, ...) { +static void mg_dash_broadcast(struct mg_mgr *mgr, int level, const char *fmt, + ...) { struct mg_connection *c; va_list ap; for (c = mgr->conns; c != NULL; c = c->next) { + int user_level = *(int *) c->data; if (!c->is_websocket) continue; + if (level > 0 && user_level < level) continue; if (c->send.len > MG_DASH_MAX_SEND_BUF_SIZE) { - MG_ERROR(("%lu buffered data %lu > MG_DASH_MAX_SEND_BUF_SIZE, throttled", - c->id, c->send.len)); - continue; + mg_error(c, "%lu buffered data %lu > MG_DASH_MAX_SEND_BUF_SIZE", c->id, + c->send.len); + } else { + va_start(ap, fmt); + mg_ws_vprintf(c, WEBSOCKET_OP_TEXT, fmt, &ap); + va_end(ap); } - va_start(ap, fmt); - mg_ws_vprintf(c, WEBSOCKET_OP_TEXT, fmt, &ap); - va_end(ap); } } @@ -125,6 +128,7 @@ static size_t mg_print_field_set(mg_pfn_t fn, void *arg, va_list *ap) { static size_t mg_dash_print_name(mg_pfn_t fn, void *arg, va_list *ap) { struct mg_dash *dash = va_arg(*ap, struct mg_dash *); struct mg_str *name = va_arg(*ap, struct mg_str *); + int level = va_arg(*ap, int); struct mg_field_set *set = mg_dash_find_field_set(dash, *name); size_t n = 0; if (name->len == 0) { @@ -132,6 +136,7 @@ static size_t mg_dash_print_name(mg_pfn_t fn, void *arg, va_list *ap) { const char *comma = ""; n += mg_xprintf(fn, arg, "{"); for (fs = dash->sets; fs != NULL; fs = fs->next) { + if (fs->read_level > 0 && level < fs->read_level) continue; if (fs->reader) fs->reader(); n += mg_xprintf(fn, arg, comma); n += mg_xprintf(fn, arg, "%m:", MG_ESC(fs->name)); @@ -139,7 +144,8 @@ static size_t mg_dash_print_name(mg_pfn_t fn, void *arg, va_list *ap) { comma = ","; } n += mg_xprintf(fn, arg, "}"); - } else if (set != NULL) { + } else if (set != NULL && + (set->read_level <= 0 || level >= set->read_level)) { if (set->reader) set->reader(); n += mg_xprintf(fn, arg, "%M", mg_print_field_set, set); } else { @@ -151,15 +157,16 @@ static size_t mg_dash_print_name(mg_pfn_t fn, void *arg, va_list *ap) { static size_t mg_dash_print(mg_pfn_t fn, void *arg, va_list *ap) { struct mg_str *req = va_arg(*ap, struct mg_str *); struct mg_dash *dash = va_arg(*ap, struct mg_dash *); + int level = va_arg(*ap, int); struct mg_str name = trimq(mg_json_get_tok(*req, "$.params")); - return mg_xprintf(fn, arg, "%M", mg_dash_print_name, dash, &name); + return mg_xprintf(fn, arg, "%M", mg_dash_print_name, dash, &name, level); } void mg_dash_send_change(struct mg_mgr *mgr, struct mg_field_set *set) { if (set->reader) set->reader(); - mg_dash_broadcast(mgr, "{%m:%m,%m:{%m:%M}}", MG_ESC("method"), - MG_ESC("change"), MG_ESC("params"), MG_ESC(set->name), - mg_print_field_set, set); + mg_dash_broadcast(mgr, set->read_level, "{%m:%m,%m:{%m:%M}}", + MG_ESC("method"), MG_ESC("change"), MG_ESC("params"), + MG_ESC(set->name), mg_print_field_set, set); } static int mg_dash_parse_field(struct mg_str json, struct mg_field *f) { @@ -189,14 +196,14 @@ static int mg_dash_parse_field(struct mg_str json, struct mg_field *f) { } static int mg_dash_apply(struct mg_connection *c, struct mg_dash *dash, - struct mg_str json) { + struct mg_str json, int level) { struct mg_str key, val; size_t ofs = 0; int total_count = 0; while ((ofs = mg_json_next(json, ofs, &key, &val)) > 0) { struct mg_field_set *set = mg_dash_find_field_set(dash, trimq(key)); int count = 0; - if (set != NULL) { + if (set != NULL && (set->write_level <= 0 || level >= set->write_level)) { size_t i; for (i = 0; set->fields[i].name != NULL; i++) { if (mg_dash_parse_field(val, &set->fields[i])) count++; @@ -206,7 +213,7 @@ static int mg_dash_apply(struct mg_connection *c, struct mg_dash *dash, mg_dash_send_change(c->mgr, set); total_count += count; } - } else { + } else if (set == NULL) { MG_ERROR(("UNKNOWN SET: [%.*s]", key.len, key.buf)); } } @@ -218,11 +225,12 @@ static void mg_dash_process_msg(struct mg_connection *c, struct mg_dash *dash) { struct mg_str req = wm->data; struct mg_str method = trimq(mg_json_get_tok(req, "$.method")); + int level = *(int *) c->data; if (mg_match(method, mg_str("get"), NULL)) { - mg_dash_success(c, req, "%M", mg_dash_print, &req, dash); + mg_dash_success(c, req, "%M", mg_dash_print, &req, dash, level); } else if (mg_match(method, mg_str("set"), NULL)) { struct mg_str params = trimq(mg_json_get_tok(req, "$.params")); - int count = mg_dash_apply(c, dash, params); + int count = mg_dash_apply(c, dash, params, level); mg_dash_success(c, req, "%d", count); } else { mg_dash_error(c, req, "%s", "unknown method"); @@ -511,7 +519,8 @@ void mg_dash_ev_handler(struct mg_connection *c, int ev, void *ev_data) { c->is_resp = 0; } else if (ev == MG_EV_HTTP_MSG && c->data[0] == '\0') { struct mg_http_message *hm = (struct mg_http_message *) ev_data; - // struct mg_dash_user *u = mg_dash_authenticate(hm, dash); + struct mg_dash_user *u = mg_dash_authenticate(hm, dash); + int level = u == NULL ? 0 : u->level; struct mg_str parts[5]; memset(parts, 0, sizeof(parts)); @@ -522,6 +531,7 @@ void mg_dash_ev_handler(struct mg_connection *c, int ev, void *ev_data) { mg_ws_printf(c, WEBSOCKET_OP_TEXT, "{%m:%m}", MG_ESC("method"), MG_ESC("logout")); } else if (mg_match(hm->uri, mg_str("/api/websocket"), NULL)) { + *(int *) c->data = level; mg_ws_upgrade(c, hm, NULL); } else if (mg_match(hm->uri, mg_str("/fs/#"), NULL)) { struct mg_str name = mg_dash_file_name(hm); @@ -539,9 +549,9 @@ void mg_dash_ev_handler(struct mg_connection *c, int ev, void *ev_data) { } else if (mg_match(hm->uri, mg_str("/api/get/#"), parts) || mg_match(hm->uri, mg_str("/api/get"), NULL)) { mg_http_reply(c, 200, MG_JSON_HEADERS, "%M\n", mg_dash_print_name, dash, - &parts[0]); + &parts[0], level); } else if (mg_match(hm->uri, mg_str("/api/set"), NULL)) { - int count = mg_dash_apply(c, dash, hm->body); + int count = mg_dash_apply(c, dash, hm->body, level); mg_http_reply(c, 200, MG_JSON_HEADERS, "%d\n", count); } else if (mg_match(hm->uri, mg_str("/"), NULL)) { struct mg_http_serve_opts opts; @@ -565,7 +575,7 @@ void mg_dash_ev_handler(struct mg_connection *c, int ev, void *ev_data) { for (fs = dash->sets; fs != NULL; fs = fs->next) { mg_dash_send_change(c->mgr, fs); } - mg_dash_broadcast(c->mgr, "{%m:%m}", MG_ESC("method"), MG_ESC("ready")); + mg_dash_broadcast(c->mgr, 0, "{%m:%m}", MG_ESC("method"), MG_ESC("ready")); } else if (ev == MG_EV_WS_MSG) { // Add this to automatically handle "get" and "set" JSON-RPC calls struct mg_ws_message *wm = (struct mg_ws_message *) ev_data; diff --git a/test/unit_test.c b/test/unit_test.c index d38464a9..7a28b763 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -4134,6 +4134,52 @@ static void dash_custom(struct mg_connection *c, int ev, void *ev_data) { (void) c, (void) ev, (void) ev_data; } +static int dash_authenticate(const char *user, const char *pass) { + int level = 0; + if (strcmp(pass, "admin") == 0) { + level = 7; + } else if (strcmp(pass, "user") == 0) { + level = 3; + } + (void) user; + return level; +} + +struct dash_ws_test { + const char *req; + int done; + int result; + int saw_set3; +}; + +static void dash_ws_cb(struct mg_connection *c, int ev, void *ev_data) { + struct dash_ws_test *d = (struct dash_ws_test *) c->fn_data; + if (ev == MG_EV_WS_OPEN) { + if (d->req != NULL) { + mg_ws_send(c, d->req, strlen(d->req), WEBSOCKET_OP_TEXT); + } + } else if (ev == MG_EV_WS_MSG) { + struct mg_ws_message *wm = (struct mg_ws_message *) ev_data; + struct mg_str method = mg_json_get_tok(wm->data, "$.method"); + struct mg_str set3 = mg_json_get_tok(wm->data, "$.params.set3"); + double result = 0; + if (mg_strcmp(method, mg_str("\"change\"")) == 0 && set3.buf != NULL) { + d->saw_set3 = 1; + } + if (mg_json_get_num(wm->data, "$.result", &result)) { + d->result = (int) result; + d->done = 1; + c->is_closing = 1; + } else if (d->req == NULL && + mg_strcmp(method, mg_str("\"ready\"")) == 0) { + d->done = 1; + c->is_closing = 1; + } + } else if (ev == MG_EV_CLOSE && d->done == 0) { + d->done = -1; + } +} + char three[10] = "hi"; int one = 1; static void rfields1(void) { @@ -4143,9 +4189,13 @@ static void rfields1(void) { static void test_dash(void) { char buf[FETCH_BUF_SIZE]; const char *url = "http://localhost:26352"; + const char *ws_url = "ws://localhost:26352/api/websocket"; struct mg_mgr mgr; struct mg_dash dash; + struct dash_ws_test ws_user, ws_admin, ws_user_notify; bool two = false; + int admin = 7; + int i; struct mg_field fields1[] = { {"one", MG_VAL_INT, &one, sizeof(one)}, {"three", MG_VAL_STR, three, sizeof(three)}, @@ -4155,14 +4205,44 @@ static void test_dash(void) { {"two", MG_VAL_BOOL, &two, 0}, // Size set to 0 - readonly {NULL, MG_VAL_INT, NULL, 0}, }; - struct mg_field_set set1 = {"set1", fields1, rfields1, NULL, 0, 0, NULL}; + struct mg_field fields3[] = { + {"admin", MG_VAL_INT, &admin, sizeof(admin)}, + {NULL, MG_VAL_INT, NULL, 0}, + }; + struct mg_field_set set1 = {"set1", fields1, rfields1, NULL, 3, 7, NULL}; struct mg_field_set set2 = {"set2", fields2, NULL, NULL, 0, 0, NULL}; + struct mg_field_set set3 = {"set3", fields3, NULL, NULL, 7, 7, NULL}; const char *get_all_expected = - "{\"files\":{\"data\":[]},\"set2\":{\"two\":false}," + "{\"files\":{\"data\":[]},\"set3\":{\"admin\":7}," + "\"set2\":{\"two\":false}," "\"set1\":{\"one\":1,\"three\":\"t: 1\"}}\n"; const char *set1_req = "POST /api/set HTTP/1.0\nContent-Length: 18\n\n" "{\"set1\":{\"one\":2}}"; + const char *set1_set3_req = "POST /api/set HTTP/1.0\n" + "Authorization: Basic dXNlcjp1c2Vy\n" + "Content-Length: 18\n\n" + "{\"set1\":{\"one\":3}}"; + const char *set1_admin_set3_req = "POST /api/set HTTP/1.0\n" + "Authorization: Basic YWRtaW46YWRtaW4=\n" + "Content-Length: 18\n\n" + "{\"set1\":{\"one\":3}}"; + const char *set3_user_req = "POST /api/set HTTP/1.0\n" + "Authorization: Basic dXNlcjp1c2Vy\n" + "Content-Length: 20\n\n" + "{\"set3\":{\"admin\":8}}"; + const char *set3_admin_req = "POST /api/set HTTP/1.0\n" + "Authorization: Basic YWRtaW46YWRtaW4=\n" + "Content-Length: 20\n\n" + "{\"set3\":{\"admin\":8}}"; + const char *set1_ws_set4_req = + "{\"id\":1,\"method\":\"set\",\"params\":{\"set1\":{\"one\":4}}}"; const char *set1_expected = "{\"one\":2,\"three\":\"t: 2\"}\n"; + const char *set1_expected3 = "{\"one\":3,\"three\":\"t: 3\"}\n"; + const char *set1_expected4 = "{\"one\":4,\"three\":\"t: 4\"}\n"; + const char *set3_expected = "{\"admin\":7}\n"; + const char *set3_expected8 = "{\"admin\":8}\n"; + const char *login_user_expected = "{\"user\":\"user\",\"level\":3}\n"; + const char *login_admin_expected = "{\"user\":\"admin\",\"level\":7}\n"; const char *set2_req = "POST /api/set HTTP/1.0\nContent-Length: 21\n\n" "{\"set2\":{\"two\":true}}"; const char *set2_expected = "{\"two\":false}\n"; @@ -4178,6 +4258,10 @@ static void test_dash(void) { ASSERT(dash.sets == &set2); ASSERT(set2.next == &set1); + MG_DASH_ADD_FIELD_SET(&dash, &set3); + ASSERT(dash.sets == &set3); + ASSERT(set3.next == &set2); + MG_DASH_REGISTER_CUSTOM_HANDLER(&dash, "/api/custom", dash_custom, &marker); ASSERT(dash.custom_handlers != NULL); ASSERT(mg_strcmp(dash.custom_handlers->uri_pattern, mg_str("/api/custom")) == @@ -4209,6 +4293,7 @@ static void test_dash(void) { mg_mem_files = mg_packed_files; ASSERT(mg_http_listen(&mgr, url, mg_dash_ev_handler, &dash) != NULL); + // Test general get / set functionality ASSERT(fetch(&mgr, buf, url, "GET /api/get HTTP/1.0\n\n") == 200); ASSERT(cmpbody(buf, get_all_expected) == 0); @@ -4217,11 +4302,109 @@ static void test_dash(void) { ASSERT(fetch(&mgr, buf, url, "GET /api/get/set1 HTTP/1.0\n\n") == 200); ASSERT(cmpbody(buf, set1_expected) == 0); + // Setting a variable with zero size should fail ASSERT(fetch(&mgr, buf, url, set2_req) == 200); ASSERT(cmpbody(buf, "0\n") == 0); ASSERT(two == false); ASSERT(fetch(&mgr, buf, url, "GET /api/get/set2 HTTP/1.0\n\n") == 200); ASSERT(cmpbody(buf, set2_expected) == 0); + + // Install auth function with two users - user, level 3 and admin, level 7 + dash.authenticate = dash_authenticate; + ASSERT(fetch(&mgr, buf, url, "GET /api/get HTTP/1.0\n\n") == 403); + ASSERT(cmpbody(buf, "Not Authorised\n") == 0); + ASSERT(fetch(&mgr, buf, url, + "GET /api/login HTTP/1.0\n" + "Authorization: Basic dXNlcjpiYWQ=\n\n") == 403); + ASSERT(fetch(&mgr, buf, url, + "GET /api/login HTTP/1.0\n" + "Authorization: Basic dXNlcjp1c2Vy\n\n") == 200); + ASSERT(cmpbody(buf, login_user_expected) == 0); + ASSERT(fetch(&mgr, buf, url, + "GET /api/login HTTP/1.0\n" + "Authorization: Basic YWRtaW46YWRtaW4=\n\n") == 200); + ASSERT(cmpbody(buf, login_admin_expected) == 0); + + // set3 is admin-only + ASSERT(fetch(&mgr, buf, url, + "GET /api/get/set3 HTTP/1.0\n" + "Authorization: Basic dXNlcjp1c2Vy\n\n") == 200); + ASSERT(cmpbody(buf, "null\n") == 0); + ASSERT(fetch(&mgr, buf, url, + "GET /api/get/set3 HTTP/1.0\n" + "Authorization: Basic YWRtaW46YWRtaW4=\n\n") == 200); + ASSERT(cmpbody(buf, set3_expected) == 0); + ASSERT(fetch(&mgr, buf, url, set3_user_req) == 200); + ASSERT(cmpbody(buf, "0\n") == 0); + ASSERT(fetch(&mgr, buf, url, + "GET /api/get/set3 HTTP/1.0\n" + "Authorization: Basic YWRtaW46YWRtaW4=\n\n") == 200); + ASSERT(cmpbody(buf, set3_expected) == 0); + ASSERT(fetch(&mgr, buf, url, set3_admin_req) == 200); + ASSERT(cmpbody(buf, "1\n") == 0); + ASSERT(fetch(&mgr, buf, url, + "GET /api/get/set3 HTTP/1.0\n" + "Authorization: Basic YWRtaW46YWRtaW4=\n\n") == 200); + ASSERT(cmpbody(buf, set3_expected8) == 0); + + // set1 read level should be 3, write level 7 + ASSERT(fetch(&mgr, buf, url, + "GET /api/get/set1 HTTP/1.0\n" + "Authorization: Basic dXNlcjp1c2Vy\n\n") == 200); + ASSERT(cmpbody(buf, set1_expected) == 0); + ASSERT(fetch(&mgr, buf, url, set1_set3_req) == 200); + ASSERT(cmpbody(buf, "0\n") == 0); + ASSERT(fetch(&mgr, buf, url, + "GET /api/get/set1 HTTP/1.0\n" + "Authorization: Basic dXNlcjp1c2Vy\n\n") == 200); + ASSERT(cmpbody(buf, set1_expected) == 0); + ASSERT(fetch(&mgr, buf, url, set1_admin_set3_req) == 200); + ASSERT(cmpbody(buf, "1\n") == 0); + ASSERT(fetch(&mgr, buf, url, + "GET /api/get/set1 HTTP/1.0\n" + "Authorization: Basic YWRtaW46YWRtaW4=\n\n") == 200); + ASSERT(cmpbody(buf, set1_expected3) == 0); + + ws_user_notify.req = NULL; + ws_user_notify.done = 0; + ws_user_notify.result = -1; + ws_user_notify.saw_set3 = 0; + mg_ws_connect(&mgr, ws_url, dash_ws_cb, &ws_user_notify, "%s", + "Authorization: Basic dXNlcjp1c2Vy\r\n"); + for (i = 0; i < 200 && ws_user_notify.done == 0; i++) mg_mgr_poll(&mgr, 1); + ASSERT(ws_user_notify.done == 1); + ASSERT(ws_user_notify.saw_set3 == 0); + + ws_user.req = set1_ws_set4_req; + ws_user.done = 0; + ws_user.result = -1; + ws_user.saw_set3 = 0; + mg_ws_connect(&mgr, ws_url, dash_ws_cb, &ws_user, "%s", + "Authorization: Basic dXNlcjp1c2Vy\r\n"); + for (i = 0; i < 200 && ws_user.done == 0; i++) mg_mgr_poll(&mgr, 1); + ASSERT(ws_user.done == 1); + ASSERT(ws_user.result == 0); + ASSERT(ws_user.saw_set3 == 0); + ASSERT(fetch(&mgr, buf, url, + "GET /api/get/set1 HTTP/1.0\n" + "Authorization: Basic dXNlcjp1c2Vy\n\n") == 200); + ASSERT(cmpbody(buf, set1_expected3) == 0); + + ws_admin.req = set1_ws_set4_req; + ws_admin.done = 0; + ws_admin.result = -1; + ws_admin.saw_set3 = 0; + mg_ws_connect(&mgr, ws_url, dash_ws_cb, &ws_admin, "%s", + "Authorization: Basic YWRtaW46YWRtaW4=\r\n"); + for (i = 0; i < 200 && ws_admin.done == 0; i++) mg_mgr_poll(&mgr, 1); + ASSERT(ws_admin.done == 1); + ASSERT(ws_admin.result == 1); + ASSERT(ws_admin.saw_set3 == 1); + ASSERT(fetch(&mgr, buf, url, + "GET /api/get/set1 HTTP/1.0\n" + "Authorization: Basic YWRtaW46YWRtaW4=\n\n") == 200); + ASSERT(cmpbody(buf, set1_expected4) == 0); + mg_mgr_free(&mgr); } diff --git a/tutorials/device-dashboard/full/dashboard.c b/tutorials/device-dashboard/full/dashboard.c index eda37a8c..b39bbdc5 100644 --- a/tutorials/device-dashboard/full/dashboard.c +++ b/tutorials/device-dashboard/full/dashboard.c @@ -100,7 +100,7 @@ static struct mg_field fields_settings[] = { }; static struct mg_field_set set_settings = { - "settings", fields_settings, read_settings, write_settings, 0, 0, NULL, + "settings", fields_settings, read_settings, write_settings, 3, 7, NULL, }; #define NUM_POINTS_GRAPH1 7 // How many graph1 data points to send @@ -126,8 +126,8 @@ void read_graph1(void) { for (i = 0; i < NUM_POINTS_GRAPH1; i++) { struct point p; p.x = (uint16_t) i; - p.y1 = 85 - (uint16_t) ((49 * i) / NUM_POINTS_GRAPH1) + (rand() % 9); - p.y2 = 20 + (uint16_t) ((69 * i) / NUM_POINTS_GRAPH1) + (rand() % 5); + p.y1 = (uint16_t) (85 - ((49 * i) / NUM_POINTS_GRAPH1) + (rand() % 9)); + p.y2 = (uint16_t) (20 + ((69 * i) / NUM_POINTS_GRAPH1) + (rand() % 5)); len += mg_snprintf(s_graph1_data + len, sizeof(s_graph1_data) - len, "%s[%hu,%hu,%hu]", i > 0 ? "," : "", p.x, p.y1, p.y2); } @@ -156,8 +156,8 @@ void read_graph2(void) { for (i = 0; i < NUM_POINTS_GRAPH2; i++) { uint16_t x = (uint16_t) (i * 7 + t); uint16_t y = (uint16_t) (i * 3 - t / 2); - uint8_t a = (x ^ (x >> 3)) & 255; - uint8_t b = (y ^ (y >> 4)) & 255; + uint8_t a = (uint8_t) (x ^ (x >> 3)) & 255; + uint8_t b = (uint8_t) (y ^ (y >> 4)) & 255; int v = 70 + ((a < 128 ? a : 255 - a) >> 1) + ((b < 128 ? b : 255 - b) >> 2); len += mg_snprintf(s_graph2_data + len, sizeof(s_graph2_data) - len, diff --git a/tutorials/device-dashboard/full/dashboard.html b/tutorials/device-dashboard/full/dashboard.html index b3a0ff8d..08606eb7 100644 --- a/tutorials/device-dashboard/full/dashboard.html +++ b/tutorials/device-dashboard/full/dashboard.html @@ -327,7 +327,7 @@ a, a:visited, a:link { text-decoration: none; } Overview - + @@ -363,7 +363,7 @@ a, a:visited, a:link { text-decoration: none; } - ${__status.username || 'admin'} + ${__status.userlevel == 3 ? 'user' : 'admin'}