...

Source file src/github.com/redis/go-redis/v9/acl_commands_test.go

Documentation: github.com/redis/go-redis/v9

     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  		// cleanup after creating the user
    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  		// Call ACL LOG RESET
    63  		resetCmd := client.ACLLogReset(ctx)
    64  		Expect(resetCmd.Err()).NotTo(HaveOccurred())
    65  		Expect(resetCmd.Val()).To(Equal("OK"))
    66  
    67  		// Verify that the log is empty after the reset
    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  		// can write
   183  		v, err := connection.Set(ctx, "anykey", "anyvalue", 0).Result()
   184  		Expect(err).ToNot(HaveOccurred())
   185  		Expect(v).To(Equal("OK"))
   186  
   187  		// but can't read
   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  		// can read
   211  		value, err := connection.Get(ctx, "anykey").Result()
   212  		Expect(err).ToNot(HaveOccurred())
   213  		Expect(value).To(Equal("anyvalue"))
   214  
   215  		// but can't delete
   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  		// can read
   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  		// has perm for search
   282  		Expect(c.FTSearch(ctx, "txt", "foo ~bar").Err()).NotTo(HaveOccurred())
   283  
   284  		// no perm for dropindex
   285  		err = c.FTDropIndex(ctx, "txt").Err()
   286  		Expect(err).ToNot(BeEmpty())
   287  		Expect(err.Error()).To(ContainSubstring("NOPERM"))
   288  
   289  		// json set and get have perm
   290  		Expect(c.JSONSet(ctx, "foo", "$", "\"bar\"").Err()).NotTo(HaveOccurred())
   291  		Expect(c.JSONGet(ctx, "foo", "$").Val()).To(BeEquivalentTo("[\"bar\"]"))
   292  
   293  		// no perm for json clear
   294  		err = c.JSONClear(ctx, "foo", "$").Err()
   295  		Expect(err).ToNot(BeEmpty())
   296  		Expect(err.Error()).To(ContainSubstring("NOPERM"))
   297  
   298  		// perm for reserve
   299  		Expect(c.BFReserve(ctx, "bloom", 0.01, 100).Err()).NotTo(HaveOccurred())
   300  
   301  		// no perm for info
   302  		err = c.BFInfo(ctx, "bloom").Err()
   303  		Expect(err).ToNot(BeEmpty())
   304  		Expect(err.Error()).To(ContainSubstring("NOPERM"))
   305  
   306  		// perm for cf.reserve
   307  		Expect(c.CFReserve(ctx, "cfres", 100).Err()).NotTo(HaveOccurred())
   308  		// perm for cms.initbydim
   309  		Expect(c.CMSInitByDim(ctx, "cmsdim", 100, 5).Err()).NotTo(HaveOccurred())
   310  		// perm for topk.reserve
   311  		Expect(c.TopKReserve(ctx, "topk", 10).Err()).NotTo(HaveOccurred())
   312  		// perm for tdigest.create
   313  		Expect(c.TDigestCreate(ctx, "tdc").Err()).NotTo(HaveOccurred())
   314  		// perm for ts.create
   315  		Expect(c.TSCreate(ctx, "tsts").Err()).NotTo(HaveOccurred())
   316  		// noperm for ts.info
   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  		// has perm for search
   357  		Expect(c.FTSearch(ctx, "txt", "foo ~bar").Err()).NotTo(HaveOccurred())
   358  		// perm for dropindex
   359  		Expect(c.FTDropIndex(ctx, "txt").Err()).NotTo(HaveOccurred())
   360  		// json set and get have perm
   361  		Expect(c.JSONSet(ctx, "foo", "$", "\"bar\"").Err()).NotTo(HaveOccurred())
   362  		Expect(c.JSONGet(ctx, "foo", "$").Val()).To(BeEquivalentTo("[\"bar\"]"))
   363  		// perm for json clear
   364  		Expect(c.JSONClear(ctx, "foo", "$").Err()).NotTo(HaveOccurred())
   365  		// perm for reserve
   366  		Expect(c.BFReserve(ctx, "bloom", 0.01, 100).Err()).NotTo(HaveOccurred())
   367  		// perm for info
   368  		Expect(c.BFInfo(ctx, "bloom").Err()).NotTo(HaveOccurred())
   369  		// perm for cf.reserve
   370  		Expect(c.CFReserve(ctx, "cfres", 100).Err()).NotTo(HaveOccurred())
   371  		// perm for cms.initbydim
   372  		Expect(c.CMSInitByDim(ctx, "cmsdim", 100, 5).Err()).NotTo(HaveOccurred())
   373  		// perm for topk.reserve
   374  		Expect(c.TopKReserve(ctx, "topk", 10).Err()).NotTo(HaveOccurred())
   375  		// perm for tdigest.create
   376  		Expect(c.TDigestCreate(ctx, "tdc").Err()).NotTo(HaveOccurred())
   377  		// perm for ts.create
   378  		Expect(c.TSCreate(ctx, "tsts").Err()).NotTo(HaveOccurred())
   379  		// perm for ts.info
   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