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
-
+
- ${__status.username || 'admin'}
+ ${__status.userlevel == 3 ? 'user' : 'admin'}