From 7e1f124ee948068edbfb1cc0b0f36aa3fb173f06 Mon Sep 17 00:00:00 2001 From: aloima Date: Tue, 5 Nov 2024 12:32:20 +0300 Subject: [PATCH] feat(commands): add PWD EDIT command fix(auth): remove zero bytes allocation problem for `passwords` fix(ci): add missing `uses` field feat(commands): add permissions field to CLIENT INFO command --- .github/workflows/c.yml | 5 +- docs/COMMANDS.md | 26 +++++++- headers/server.h | 1 + src/commands/generic/client.c | 26 +++++++- src/commands/generic/pwd.c | 122 ++++++++++++++++++++++------------ src/server/auth.c | 70 ++++++++++++------- 6 files changed, 177 insertions(+), 73 deletions(-) diff --git a/.github/workflows/c.yml b/.github/workflows/c.yml index b3a691f..14ee4f1 100644 --- a/.github/workflows/c.yml +++ b/.github/workflows/c.yml @@ -11,10 +11,11 @@ jobs: steps: - uses: actions/checkout@v4 - - name: make + - name: Build run: make - name: On release - uses: startsWith(github.ref, 'refs/tags/') + uses: softprops/action-gh-release@v2 + if: startsWith(github.ref, 'refs/tags/') with: files: telly diff --git a/docs/COMMANDS.md b/docs/COMMANDS.md index 827a379..7a519b7 100644 --- a/docs/COMMANDS.md +++ b/docs/COMMANDS.md @@ -211,7 +211,29 @@ This document provides a detailed description of all the available commands. Eac **Behavior**: * If current using password by client do not have a permissions in `permissions`, throws an error. * If there is an invalid permission in `permissions`, throws an error. -* If `password` is already exist, throws an error. +* If `password` already exists, throws an error. + +**Arguments**: +- **password**: Password value +- **permissions**: For permissions, look at [AUTH.md](./AUTH.md). Each character represents a permissions: + + `r` => `P_READ` + + `w` => `P_WRITE` + + `c` => `P_CLIENT` + + `o` => `P_CONFIG` + + `a` => `P_AUTH` + + `s` => `P_SERVER` + +#### EDIT +**Syntax**: `PWD EDIT password permissions` +**Description**: Edits a password permissions. +**Since**: `0.1.7` +**Time complexity**: `O(N) where N is permissions length` +**Permissions**: None +**Returns**: `OK` +**Behavior**: +* If current using password by client do not have a permissions in `permissions`, throws an error. +* If there is an invalid permission in `permissions`, throws an error. +* If `password` does not exist, throws an error. **Arguments**: - **password**: Password value @@ -561,7 +583,7 @@ INCR user_age * If the key is exist, new value will be overwritten. **Arguments**: -- **NX**: Only set if the key does not already exist. +- **NX**: Only set if the key does not exist. - **XX**: Only set if the key exists. - **GET**: Returns the old value if it existed. diff --git a/headers/server.h b/headers/server.h index 29fc943..f19be75 100644 --- a/headers/server.h +++ b/headers/server.h @@ -80,6 +80,7 @@ void free_passwords(); void add_password(struct Client *client, const string_t data, const uint8_t permissions); bool remove_password(struct Client *executor, const char *value); int32_t where_password(const char *value); +struct Password *get_password(const char *value); bool edit_password(const char *value, const uint32_t permissions); /* /AUTH */ diff --git a/src/commands/generic/client.c b/src/commands/generic/client.c index 36afed5..6ffee3b 100644 --- a/src/commands/generic/client.c +++ b/src/commands/generic/client.c @@ -6,6 +6,7 @@ #include #include #include +#include #include static void run(struct Client *client, commanddata_t *command, __attribute__((unused)) struct Password *password) { @@ -34,6 +35,28 @@ static void run(struct Client *client, commanddata_t *command, __attribute__((un break; } + char permissions[44]; + permissions[0] = '\0'; + + { + const uint8_t value = client->password->permissions; + uint32_t length = 0; + + if (value == 0) { + memcpy(permissions, "None", 5); + } else { + if (value & P_READ) strcat(permissions, "read, "), length += 6; + if (value & P_WRITE) strcat(permissions, "write, "), length += 7; + if (value & P_CLIENT) strcat(permissions, "client, "), length += 8; + if (value & P_CONFIG) strcat(permissions, "config, "), length += 8; + if (value & P_AUTH) strcat(permissions, "auth, "), length += 6; + if (value & P_SERVER) strcat(permissions, "server, "), length += 8; + + permissions[0] = toupper(permissions[0]); + permissions[length - 2] = '\0'; + } + } + char buf[512]; const size_t buf_len = sprintf(buf, ( "ID: %d\r\n" @@ -43,7 +66,8 @@ static void run(struct Client *client, commanddata_t *command, __attribute__((un "Library name: %s\r\n" "Library version: %s\r\n" "Protocol: %s\r\n" - ), client->id, client->connfd, ctime(&client->connected_at), client->command->name, lib_name, lib_ver, protocol); + "Permissions: %s\r\n" + ), client->id, client->connfd, ctime(&client->connected_at), client->command->name, lib_name, lib_ver, protocol, permissions); char res[1024]; const size_t nbytes = sprintf(res, "$%ld\r\n%s\r\n", buf_len, buf); diff --git a/src/commands/generic/pwd.c b/src/commands/generic/pwd.c index 20eb903..e687cc6 100644 --- a/src/commands/generic/pwd.c +++ b/src/commands/generic/pwd.c @@ -18,6 +18,51 @@ static void generate_random(char *dest, size_t length) { *dest = '\0'; } +static uint8_t read_permissions_value(struct Client *client, char *permissions_value) { + uint8_t permissions = 0; + char c; + + while ((c = *permissions_value) != '\0') { + switch (c) { + case 'r': + permissions |= P_READ; + break; + + case 'w': + permissions |= P_WRITE; + break; + + case 'c': + permissions |= P_CLIENT; + break; + + case 'o': + permissions |= P_CONFIG; + break; + + case 'a': + permissions |= P_AUTH; + break; + + case 's': + permissions |= P_SERVER; + break; + + default: { + char buf[27]; + sprintf(buf, "-Invalid permission: '%c'\r\n", c); + + _write(client, buf, 26); + return 0; + } + } + + permissions_value += 1; + } + + return permissions; +} + static void run(struct Client *client, commanddata_t *command, struct Password *password) { if (command->arg_count == 0) { WRONG_ARGUMENT_ERROR(client, "AUTH", 4); @@ -37,48 +82,7 @@ static void run(struct Client *client, commanddata_t *command, struct Password * const string_t data = command->args[1]; char *permissions_value = command->args[2].value; - uint8_t permissions = 0; - - char c; - - while ((c = *permissions_value) != '\0') { - switch (c) { - case 'r': - permissions |= P_READ; - break; - - case 'w': - permissions |= P_WRITE; - break; - - case 'c': - permissions |= P_CLIENT; - break; - - case 'o': - permissions |= P_CONFIG; - break; - - case 'a': - permissions |= P_AUTH; - break; - - case 's': - permissions |= P_SERVER; - break; - - default: { - char buf[27]; - sprintf(buf, "-Invalid permission: '%c'\r\n", c); - - _write(client, buf, 26); - return; - } - } - - permissions_value += 1; - } - + const uint8_t permissions = read_permissions_value(client, permissions_value); const uint8_t not_has = ~password->permissions & permissions; if (not_has) { @@ -90,7 +94,32 @@ static void run(struct Client *client, commanddata_t *command, struct Password * add_password(client, data, permissions); WRITE_OK(client); } else { - _write(client, "-This password is already exist\r\n", 33); + _write(client, "-This password already exists\r\n", 31); + } + } if (streq(subcommand, "EDIT")) { + if (command->arg_count != 3) { + WRONG_ARGUMENT_ERROR(client, "AUTH EDIT", 9); + return; + } + + const char *value = command->args[1].value; + char *permissions_value = command->args[2].value; + + struct Password *target = get_password(value); + + if (target) { + const uint8_t permissions = read_permissions_value(client, permissions_value); + const uint8_t not_has = ~password->permissions & permissions; + + if (not_has) { + _write(client, "-Tried to give permissions your password do not have\r\n", 54); + return; + } + + target->permissions = permissions; + WRITE_OK(client); + } else { + _write(client, "-This password does not exist\r\n", 31); } } else if (streq(subcommand, "REMOVE")) { if (command->arg_count != 2) { @@ -99,6 +128,7 @@ static void run(struct Client *client, commanddata_t *command, struct Password * } const char *value = command->args[1].value; + if (remove_password(client, value)) WRITE_OK(client); else { _write(client, "-This password cannot be found\r\n", 32); @@ -124,6 +154,12 @@ static struct Subcommand subcommands[] = { .since = "0.1.7", .complexity = "O(N) where N is permissions length" }, + (struct Subcommand) { + .name = "EDIT", + .summary = "Edits a password permissions.", + .since = "0.1.7", + .complexity = "O(N) where N is permissions length" + }, (struct Subcommand) { .name = "REMOVE", .summary = "Removes a password.", diff --git a/src/server/auth.c b/src/server/auth.c index 2a1cae1..3c28062 100644 --- a/src/server/auth.c +++ b/src/server/auth.c @@ -16,6 +16,29 @@ uint32_t get_password_count() { return password_count; } +static void get_password_from_file(const int fd, const uint32_t i) { + struct Password *password = (passwords[i] = malloc(sizeof(struct Password))); + string_t *data = &password->data; + + // String length specifier + { + uint8_t first; + read(fd, &first, 1); + + const uint8_t byte_count = first >> 6; + data->len = 0; + read(fd, &data->len, byte_count); + + data->len = (data->len << 6) | (first & 0b111111); + data->value = malloc(data->len + 1); + } + + read(fd, data->value, data->len); + data->value[data->len] = '\0'; + + read(fd, &password->permissions, 1); +} + off_t get_authorization_from_file(const int fd) { lseek(fd, 10, SEEK_SET); @@ -23,29 +46,13 @@ off_t get_authorization_from_file(const int fd) { read(fd, &password_count_byte_count, 1); read(fd, &password_count, password_count_byte_count); - passwords = malloc(password_count * sizeof(struct Password *)); - - for (uint32_t i = 0; i < password_count; ++i) { - struct Password *password = (passwords[i] = malloc(sizeof(struct Password))); - string_t *data = &password->data; - - // String length specifier - { - uint8_t first; - read(fd, &first, 1); - - const uint8_t byte_count = first >> 6; - data->len = 0; - read(fd, &data->len, byte_count); + if (password_count != 0) { + passwords = malloc(password_count * sizeof(struct Password *)); + get_password_from_file(fd, 0); - data->len = (data->len << 6) | (first & 0b111111); - data->value = malloc(data->len + 1); + for (uint32_t i = 1; i < password_count; ++i) { + get_password_from_file(fd, i); } - - read(fd, data->value, data->len); - data->value[data->len] = '\0'; - - read(fd, &password->permissions, 1); } return lseek(fd, 0, SEEK_CUR); @@ -59,6 +66,15 @@ int32_t where_password(const char *value) { return -1; } +struct Password *get_password(const char *value) { + for (uint32_t i = 0; i < password_count; ++i) { + struct Password *password = passwords[i]; + if (streq(value, password->data.value)) return password; + } + + return NULL; +} + bool edit_password(const char *value, const uint32_t permissions) { for (uint32_t i = 0; i < password_count; ++i) { struct Password *password = passwords[i]; @@ -99,11 +115,15 @@ void free_password(struct Password *password) { } void free_passwords() { - for (uint32_t i = 0; i < password_count; ++i) { - free_password(passwords[i]); - } + if (password_count != 0) { + free_password(passwords[0]); - free(passwords); + for (uint32_t i = 1; i < password_count; ++i) { + free_password(passwords[i]); + } + + free(passwords); + } } bool remove_password(struct Client *executor, const char *value) {