1 package redis_test
2
3 import (
4 "context"
5
6 "github.com/redis/go-redis/v9"
7
8 . "github.com/bsm/ginkgo/v2"
9 . "github.com/bsm/gomega"
10 )
11
12 var TestUserName string = "goredis"
13 var _ = Describe("ACL", func() {
14 var client *redis.Client
15 var ctx context.Context
16
17 BeforeEach(func() {
18 ctx = context.Background()
19 opt := redisOptions()
20 client = redis.NewClient(opt)
21 })
22
23 It("should ACL LOG", Label("NonRedisEnterprise"), func() {
24 Expect(client.ACLLogReset(ctx).Err()).NotTo(HaveOccurred())
25 err := client.Do(ctx, "acl", "setuser", "test", ">test", "on", "allkeys", "+get").Err()
26 Expect(err).NotTo(HaveOccurred())
27
28 clientAcl := redis.NewClient(redisOptions())
29 clientAcl.Options().Username = "test"
30 clientAcl.Options().Password = "test"
31 clientAcl.Options().DB = 0
32 _ = clientAcl.Set(ctx, "mystring", "foo", 0).Err()
33 _ = clientAcl.HSet(ctx, "myhash", "foo", "bar").Err()
34 _ = clientAcl.SAdd(ctx, "myset", "foo", "bar").Err()
35
36 logEntries, err := client.ACLLog(ctx, 10).Result()
37 Expect(err).NotTo(HaveOccurred())
38 Expect(len(logEntries)).To(Equal(4))
39
40 for _, entry := range logEntries {
41 Expect(entry.Reason).To(Equal("command"))
42 Expect(entry.Context).To(Equal("toplevel"))
43 Expect(entry.Object).NotTo(BeEmpty())
44 Expect(entry.Username).To(Equal("test"))
45 Expect(entry.AgeSeconds).To(BeNumerically(">=", 0))
46 Expect(entry.ClientInfo).NotTo(BeNil())
47 Expect(entry.EntryID).To(BeNumerically(">=", 0))
48 Expect(entry.TimestampCreated).To(BeNumerically(">=", 0))
49 Expect(entry.TimestampLastUpdated).To(BeNumerically(">=", 0))
50 }
51
52 limitedLogEntries, err := client.ACLLog(ctx, 2).Result()
53 Expect(err).NotTo(HaveOccurred())
54 Expect(len(limitedLogEntries)).To(Equal(2))
55
56
57 err = client.Do(ctx, "acl", "deluser", "test").Err()
58 Expect(err).NotTo(HaveOccurred())
59 })
60
61 It("should ACL LOG RESET", Label("NonRedisEnterprise"), func() {
62
63 resetCmd := client.ACLLogReset(ctx)
64 Expect(resetCmd.Err()).NotTo(HaveOccurred())
65 Expect(resetCmd.Val()).To(Equal("OK"))
66
67
68 logEntries, err := client.ACLLog(ctx, 10).Result()
69 Expect(err).NotTo(HaveOccurred())
70 Expect(len(logEntries)).To(Equal(0))
71 })
72
73 })
74 var _ = Describe("ACL user commands", Label("NonRedisEnterprise"), func() {
75 var client *redis.Client
76 var ctx context.Context
77
78 BeforeEach(func() {
79 ctx = context.Background()
80 opt := redisOptions()
81 client = redis.NewClient(opt)
82 })
83
84 AfterEach(func() {
85 _, err := client.ACLDelUser(context.Background(), TestUserName).Result()
86 Expect(err).NotTo(HaveOccurred())
87 Expect(client.Close()).NotTo(HaveOccurred())
88 })
89
90 It("list only default user", func() {
91 res, err := client.ACLList(ctx).Result()
92 Expect(err).NotTo(HaveOccurred())
93 Expect(res).To(HaveLen(1))
94 Expect(res[0]).To(ContainSubstring("default"))
95 })
96
97 It("setuser and deluser", func() {
98 res, err := client.ACLList(ctx).Result()
99 Expect(err).NotTo(HaveOccurred())
100 Expect(res).To(HaveLen(1))
101 Expect(res[0]).To(ContainSubstring("default"))
102
103 add, err := client.ACLSetUser(ctx, TestUserName, "nopass", "on", "allkeys", "+set", "+get").Result()
104 Expect(err).NotTo(HaveOccurred())
105 Expect(add).To(Equal("OK"))
106
107 resAfter, err := client.ACLList(ctx).Result()
108 Expect(err).NotTo(HaveOccurred())
109 Expect(resAfter).To(HaveLen(2))
110 Expect(resAfter[1]).To(ContainSubstring(TestUserName))
111
112 deletedN, err := client.ACLDelUser(ctx, TestUserName).Result()
113 Expect(err).NotTo(HaveOccurred())
114 Expect(deletedN).To(BeNumerically("==", 1))
115
116 resAfterDeletion, err := client.ACLList(ctx).Result()
117 Expect(err).NotTo(HaveOccurred())
118 Expect(resAfterDeletion).To(HaveLen(1))
119 Expect(resAfterDeletion[0]).To(BeEquivalentTo(res[0]))
120 })
121
122 It("should acl dryrun", func() {
123 dryRun := client.ACLDryRun(ctx, "default", "get", "randomKey")
124 Expect(dryRun.Err()).NotTo(HaveOccurred())
125 Expect(dryRun.Val()).To(Equal("OK"))
126 })
127 })
128
129 var _ = Describe("ACL permissions", Label("NonRedisEnterprise"), func() {
130 var client *redis.Client
131 var ctx context.Context
132
133 BeforeEach(func() {
134 ctx = context.Background()
135 opt := redisOptions()
136 opt.UnstableResp3 = true
137 client = redis.NewClient(opt)
138 })
139
140 AfterEach(func() {
141 _, err := client.ACLDelUser(context.Background(), TestUserName).Result()
142 Expect(err).NotTo(HaveOccurred())
143 Expect(client.Close()).NotTo(HaveOccurred())
144 })
145
146 It("reset permissions", func() {
147 add, err := client.ACLSetUser(ctx,
148 TestUserName,
149 "reset",
150 "nopass",
151 "on",
152 ).Result()
153 Expect(err).NotTo(HaveOccurred())
154 Expect(add).To(Equal("OK"))
155
156 connection := client.Conn()
157 authed, err := connection.AuthACL(ctx, TestUserName, "").Result()
158 Expect(err).NotTo(HaveOccurred())
159 Expect(authed).To(Equal("OK"))
160
161 _, err = connection.Get(ctx, "anykey").Result()
162 Expect(err).To(HaveOccurred())
163 })
164
165 It("add write permissions", func() {
166 add, err := client.ACLSetUser(ctx,
167 TestUserName,
168 "reset",
169 "nopass",
170 "on",
171 "~*",
172 "+SET",
173 ).Result()
174 Expect(err).NotTo(HaveOccurred())
175 Expect(add).To(Equal("OK"))
176
177 connection := client.Conn()
178 authed, err := connection.AuthACL(ctx, TestUserName, "").Result()
179 Expect(err).NotTo(HaveOccurred())
180 Expect(authed).To(Equal("OK"))
181
182
183 v, err := connection.Set(ctx, "anykey", "anyvalue", 0).Result()
184 Expect(err).ToNot(HaveOccurred())
185 Expect(v).To(Equal("OK"))
186
187
188 value, err := connection.Get(ctx, "anykey").Result()
189 Expect(err).To(HaveOccurred())
190 Expect(value).To(BeEmpty())
191 })
192
193 It("add read permissions", func() {
194 add, err := client.ACLSetUser(ctx,
195 TestUserName,
196 "reset",
197 "nopass",
198 "on",
199 "~*",
200 "+GET",
201 ).Result()
202 Expect(err).NotTo(HaveOccurred())
203 Expect(add).To(Equal("OK"))
204
205 connection := client.Conn()
206 authed, err := connection.AuthACL(ctx, TestUserName, "").Result()
207 Expect(err).NotTo(HaveOccurred())
208 Expect(authed).To(Equal("OK"))
209
210
211 value, err := connection.Get(ctx, "anykey").Result()
212 Expect(err).ToNot(HaveOccurred())
213 Expect(value).To(Equal("anyvalue"))
214
215
216 del, err := connection.Del(ctx, "anykey").Result()
217 Expect(err).To(HaveOccurred())
218 Expect(del).ToNot(Equal(1))
219 })
220
221 It("add del permissions", func() {
222 add, err := client.ACLSetUser(ctx,
223 TestUserName,
224 "reset",
225 "nopass",
226 "on",
227 "~*",
228 "+DEL",
229 ).Result()
230 Expect(err).NotTo(HaveOccurred())
231 Expect(add).To(Equal("OK"))
232
233 connection := client.Conn()
234 authed, err := connection.AuthACL(ctx, TestUserName, "").Result()
235 Expect(err).NotTo(HaveOccurred())
236 Expect(authed).To(Equal("OK"))
237
238
239 del, err := connection.Del(ctx, "anykey").Result()
240 Expect(err).ToNot(HaveOccurred())
241 Expect(del).To(BeEquivalentTo(1))
242 })
243
244 It("set permissions for module commands", func() {
245 SkipBeforeRedisVersion(8, "permissions for modules are supported for Redis Version >=8")
246 Expect(client.FlushDB(ctx).Err()).NotTo(HaveOccurred())
247 val, err := client.FTCreate(ctx, "txt", &redis.FTCreateOptions{}, &redis.FieldSchema{FieldName: "txt", FieldType: redis.SearchFieldTypeText}).Result()
248 Expect(err).NotTo(HaveOccurred())
249 Expect(val).To(BeEquivalentTo("OK"))
250 WaitForIndexing(client, "txt")
251 client.HSet(ctx, "doc1", "txt", "foo baz")
252 client.HSet(ctx, "doc2", "txt", "foo bar")
253 add, err := client.ACLSetUser(ctx,
254 TestUserName,
255 "reset",
256 "nopass",
257 "on",
258 "~*",
259 "+FT.SEARCH",
260 "-FT.DROPINDEX",
261 "+json.set",
262 "+json.get",
263 "-json.clear",
264 "+bf.reserve",
265 "-bf.info",
266 "+cf.reserve",
267 "+cms.initbydim",
268 "+topk.reserve",
269 "+tdigest.create",
270 "+ts.create",
271 "-ts.info",
272 ).Result()
273 Expect(err).NotTo(HaveOccurred())
274 Expect(add).To(Equal("OK"))
275
276 c := client.Conn()
277 authed, err := c.AuthACL(ctx, TestUserName, "").Result()
278 Expect(err).NotTo(HaveOccurred())
279 Expect(authed).To(Equal("OK"))
280
281
282 Expect(c.FTSearch(ctx, "txt", "foo ~bar").Err()).NotTo(HaveOccurred())
283
284
285 err = c.FTDropIndex(ctx, "txt").Err()
286 Expect(err).ToNot(BeEmpty())
287 Expect(err.Error()).To(ContainSubstring("NOPERM"))
288
289
290 Expect(c.JSONSet(ctx, "foo", "$", "\"bar\"").Err()).NotTo(HaveOccurred())
291 Expect(c.JSONGet(ctx, "foo", "$").Val()).To(BeEquivalentTo("[\"bar\"]"))
292
293
294 err = c.JSONClear(ctx, "foo", "$").Err()
295 Expect(err).ToNot(BeEmpty())
296 Expect(err.Error()).To(ContainSubstring("NOPERM"))
297
298
299 Expect(c.BFReserve(ctx, "bloom", 0.01, 100).Err()).NotTo(HaveOccurred())
300
301
302 err = c.BFInfo(ctx, "bloom").Err()
303 Expect(err).ToNot(BeEmpty())
304 Expect(err.Error()).To(ContainSubstring("NOPERM"))
305
306
307 Expect(c.CFReserve(ctx, "cfres", 100).Err()).NotTo(HaveOccurred())
308
309 Expect(c.CMSInitByDim(ctx, "cmsdim", 100, 5).Err()).NotTo(HaveOccurred())
310
311 Expect(c.TopKReserve(ctx, "topk", 10).Err()).NotTo(HaveOccurred())
312
313 Expect(c.TDigestCreate(ctx, "tdc").Err()).NotTo(HaveOccurred())
314
315 Expect(c.TSCreate(ctx, "tsts").Err()).NotTo(HaveOccurred())
316
317 err = c.TSInfo(ctx, "tsts").Err()
318 Expect(err).ToNot(BeEmpty())
319 Expect(err.Error()).To(ContainSubstring("NOPERM"))
320
321 Expect(client.FlushDB(ctx).Err()).NotTo(HaveOccurred())
322 })
323
324 It("set permissions for module categories", func() {
325 SkipBeforeRedisVersion(8, "permissions for modules are supported for Redis Version >=8")
326 Expect(client.FlushDB(ctx).Err()).NotTo(HaveOccurred())
327 val, err := client.FTCreate(ctx, "txt", &redis.FTCreateOptions{}, &redis.FieldSchema{FieldName: "txt", FieldType: redis.SearchFieldTypeText}).Result()
328 Expect(err).NotTo(HaveOccurred())
329 Expect(val).To(BeEquivalentTo("OK"))
330 WaitForIndexing(client, "txt")
331 client.HSet(ctx, "doc1", "txt", "foo baz")
332 client.HSet(ctx, "doc2", "txt", "foo bar")
333 add, err := client.ACLSetUser(ctx,
334 TestUserName,
335 "reset",
336 "nopass",
337 "on",
338 "~*",
339 "+@search",
340 "+@json",
341 "+@bloom",
342 "+@cuckoo",
343 "+@topk",
344 "+@cms",
345 "+@timeseries",
346 "+@tdigest",
347 ).Result()
348 Expect(err).NotTo(HaveOccurred())
349 Expect(add).To(Equal("OK"))
350
351 c := client.Conn()
352 authed, err := c.AuthACL(ctx, TestUserName, "").Result()
353 Expect(err).NotTo(HaveOccurred())
354 Expect(authed).To(Equal("OK"))
355
356
357 Expect(c.FTSearch(ctx, "txt", "foo ~bar").Err()).NotTo(HaveOccurred())
358
359 Expect(c.FTDropIndex(ctx, "txt").Err()).NotTo(HaveOccurred())
360
361 Expect(c.JSONSet(ctx, "foo", "$", "\"bar\"").Err()).NotTo(HaveOccurred())
362 Expect(c.JSONGet(ctx, "foo", "$").Val()).To(BeEquivalentTo("[\"bar\"]"))
363
364 Expect(c.JSONClear(ctx, "foo", "$").Err()).NotTo(HaveOccurred())
365
366 Expect(c.BFReserve(ctx, "bloom", 0.01, 100).Err()).NotTo(HaveOccurred())
367
368 Expect(c.BFInfo(ctx, "bloom").Err()).NotTo(HaveOccurred())
369
370 Expect(c.CFReserve(ctx, "cfres", 100).Err()).NotTo(HaveOccurred())
371
372 Expect(c.CMSInitByDim(ctx, "cmsdim", 100, 5).Err()).NotTo(HaveOccurred())
373
374 Expect(c.TopKReserve(ctx, "topk", 10).Err()).NotTo(HaveOccurred())
375
376 Expect(c.TDigestCreate(ctx, "tdc").Err()).NotTo(HaveOccurred())
377
378 Expect(c.TSCreate(ctx, "tsts").Err()).NotTo(HaveOccurred())
379
380 Expect(c.TSInfo(ctx, "tsts").Err()).NotTo(HaveOccurred())
381
382 Expect(client.FlushDB(ctx).Err()).NotTo(HaveOccurred())
383 })
384 })
385
386 var _ = Describe("ACL Categories", func() {
387 var client *redis.Client
388 var ctx context.Context
389
390 BeforeEach(func() {
391 ctx = context.Background()
392 opt := redisOptions()
393 client = redis.NewClient(opt)
394 })
395
396 AfterEach(func() {
397 Expect(client.Close()).NotTo(HaveOccurred())
398 })
399
400 It("lists acl categories and subcategories", func() {
401 res, err := client.ACLCat(ctx).Result()
402 Expect(err).NotTo(HaveOccurred())
403 Expect(len(res)).To(BeNumerically(">", 20))
404 Expect(res).To(ContainElements(
405 "read",
406 "write",
407 "keyspace",
408 "dangerous",
409 "slow",
410 "set",
411 "sortedset",
412 "list",
413 "hash",
414 ))
415
416 res, err = client.ACLCatArgs(ctx, &redis.ACLCatArgs{Category: "read"}).Result()
417 Expect(err).NotTo(HaveOccurred())
418 Expect(res).To(ContainElement("get"))
419 })
420
421 It("lists acl categories and subcategories with Modules", func() {
422 SkipBeforeRedisVersion(8, "modules are included in acl for redis version >= 8")
423 aclTestCase := map[string]string{
424 "search": "FT.CREATE",
425 "bloom": "bf.add",
426 "json": "json.get",
427 "cuckoo": "cf.insert",
428 "cms": "cms.query",
429 "topk": "topk.list",
430 "tdigest": "tdigest.rank",
431 "timeseries": "ts.range",
432 }
433 var cats []interface{}
434
435 for cat, subitem := range aclTestCase {
436 cats = append(cats, cat)
437
438 res, err := client.ACLCatArgs(ctx, &redis.ACLCatArgs{
439 Category: cat,
440 }).Result()
441 Expect(err).NotTo(HaveOccurred())
442 Expect(res).To(ContainElement(subitem))
443 }
444
445 res, err := client.ACLCat(ctx).Result()
446 Expect(err).NotTo(HaveOccurred())
447 Expect(res).To(ContainElements(cats...))
448 })
449 })
450
View as plain text