diff --git a/command.ts b/command.ts index 84c74277..72a37991 100644 --- a/command.ts +++ b/command.ts @@ -11,6 +11,7 @@ export type ConditionalArray = Raw[]; export type RedisCommands = { // Connection auth(password: string): Promise; + auth(username: string, password: string): Promise; echo(message: string): Promise; ping(): Promise; ping(message: string): Promise; @@ -396,6 +397,18 @@ export type RedisCommands = { // Cluster // cluster // // Server + acl_cat(parameter?: string): Promise; + acl_deluser(parameter: string): Promise; + acl_genpass(parameter?: number): Promise; + acl_getuser(parameter: string): Promise; + acl_help(): Promise; + acl_list(): Promise; + acl_load(): Promise; + acl_log(parameter: string | number): Promise; + acl_save(): Promise; + acl_setuser(username: string, rule: string): Promise; + acl_users(): Promise; + acl_whoami(): Promise; bgrewriteaof(): Promise; bgsave(): Promise; // client // @@ -437,6 +450,9 @@ export type RedisCommands = { samples?: number; }, ): Promise; + module_list(): Promise; + module_load(path: string, args: string): Promise; + module_unload(name: string): Promise; monitor(): void; role(): Promise< | ["master", Integer, BulkString[][]] diff --git a/redis.ts b/redis.ts index 3dccd918..dde93477 100644 --- a/redis.ts +++ b/redis.ts @@ -89,12 +89,74 @@ class RedisImpl implements RedisCommands { return reply as Status | BulkNil; } + acl_cat(categoryname?: string) { + if (categoryname) { + return this.execArrayReply("ACL", "CAT", categoryname); + } else { + return this.execArrayReply("ACL", "CAT"); + } + } + + acl_deluser(username: string) { + return this.execIntegerReply("ACL", "DELUSER", username); + } + + acl_genpass(bits?: Integer) { + if (bits) { + return this.execStatusReply("ACL", "GENPASS", bits); + } else { + return this.execStatusReply("ACL", "GENPASS"); + } + } + + acl_getuser(username: string) { + return this.execArrayReply("ACL", "GETUSER", username); + } + + acl_help() { + return this.execArrayReply("ACL", "HELP"); + } + + acl_list() { + return this.execArrayReply("ACL", "LIST"); + } + + acl_load() { + return this.execStatusReply("ACL", "LOAD"); + } + + acl_log(param: string|number) { + if (param === "RESET" || param === "reset") { + return this.execStatusReply("ACL", "LOG", "RESET"); + } + return this.execArrayReply("ACL", "LOG", param); + } + + acl_save() { + return this.execStatusReply("ACL", "SAVE"); + } + + acl_setuser(username: string, rule: string) { + return this.execStatusReply("ACL", "SETUSER", username, rule); + } + + acl_users() { + return this.execArrayReply("ACL", "USERS"); + } + + acl_whoami() { + return this.execStatusReply("ACL", "WHOAMI"); + } + append(key: string, value: string | number) { return this.execIntegerReply("APPEND", key, value); } - auth(password: string) { - return this.execStatusReply("AUTH", password); + auth(param1: string, param2?: string) { + if (typeof param2 === "string") { + return this.execStatusReply("AUTH", param1, param2); + } + return this.execStatusReply("AUTH", param1); } bgrewriteaof() { @@ -662,6 +724,18 @@ class RedisImpl implements RedisCommands { return this.execStatusReply("MIGRATE", ...args); } + module_list() { + return this.execArrayReply("MODULE", "LIST"); + } + + module_load(path: string, args: string) { + return this.execStatusReply("MODULE", "LOAD", path, args); + } + + module_unload(name: string) { + return this.execStatusReply("MODULE", "UNLOAD", name); + } + monitor() { throw new Error("not supported yet"); } diff --git a/redis_test.ts b/redis_test.ts index 47af3785..dcfd4d0e 100644 --- a/redis_test.ts +++ b/redis_test.ts @@ -8,3 +8,4 @@ import "./tests/set_test.ts"; import "./tests/sorted_set_test.ts"; import "./tests/string_test.ts"; import "./tests/key_test.ts"; +import "./tests/acl_cmd_test.ts"; diff --git a/tests/acl_cmd_test.ts b/tests/acl_cmd_test.ts new file mode 100644 index 00000000..96f7c8e1 --- /dev/null +++ b/tests/acl_cmd_test.ts @@ -0,0 +1,120 @@ +import { makeTest } from "./test_util.ts"; +import { + assertEquals, +} from "../vendor/https/deno.land/std/testing/asserts.ts"; + +const { test, client } = await makeTest("acl_cmd"); + +test("whoami", async () => { + assertEquals(await client.acl_whoami(), "default"); +}); + +test("list", async () => { + assertEquals(await client.acl_list(), ["user default on nopass ~* +@all"]); +}); + +test("getuser", async () => { + assertEquals(await client.acl_getuser("default"), + [ "flags",[ "on", "allkeys", "allcommands", "nopass" ], + "passwords", [], "commands", "+@all", "keys", [ "*" ] + ]); +}); + +test("cat", async () => { + assertEquals((await client.acl_cat()).sort(), + [ + "keyspace", + "read", + "write", + "set", + "sortedset", + "list", + "hash", + "string", + "bitmap", + "hyperloglog", + "geo", + "stream", + "pubsub", + "admin", + "fast", + "slow", + "blocking", + "dangerous", + "connection", + "transaction", + "scripting" + ].sort()); + assertEquals((await client.acl_cat("dangerous")).sort(), + [ + "lastsave", + "shutdown", + "module", + "monitor", + "role", + "client", + "replconf", + "config", + "pfselftest", + "save", + "replicaof", + "restore-asking", + "restore", + "latency", + "swapdb", + "slaveof", + "bgsave", + "debug", + "bgrewriteaof", + "sync", + "flushdb", + "keys", + "psync", + "pfdebug", + "flushall", + "cluster", + "info", + "migrate", + "acl", + "sort", + "slowlog" + ].sort()); +}); + +test("users", async () => { + assertEquals(await client.acl_users(), ["default"]) +}); + +test("acl_setuser", async () => { + assertEquals(await client.acl_setuser("alan", "+get"), "OK") + assertEquals(await client.acl_deluser("alan"), 1); +}); + +test("deluser", async () => { + assertEquals(await client.acl_deluser("alan"), 0); +}); + +test("genpass", async () => { + assertEquals((await client.acl_genpass()).length, 64); + let testlen = 32 + assertEquals((await client.acl_genpass(testlen)).length, testlen / 4); +}); + +test("aclauth", async () => { + assertEquals(await client.auth("default", ""), "OK") +}); + +test("log", async () => { + let randString = "balh" + try { + await client.auth(randString, randString) + } catch (error) { + // skip invalid username-password pair error + } + assertEquals((await client.acl_log(1))[0][9], randString); + assertEquals((await client.acl_log("RESET")), "OK"); +}); + +test("module_list", async () => { + assertEquals(await client.module_list(), []); +});