diff --git a/args.go b/args.go index ff56b1cea..ab5c34b57 100644 --- a/args.go +++ b/args.go @@ -406,6 +406,8 @@ const ( ArgDatabasePrivateConnectionBool = "private" // ArgDatabaseUserKafkaACLs will specify permissions on topics in kafka clsuter ArgDatabaseUserKafkaACLs = "acl" + // ArgDatabaseUserOpenSearchACLs will specify permissions on indexes in opensearch clsuter + ArgDatabaseUserOpenSearchACLs = "opensearch-acl" // ArgDatabaseTopicReplicationFactor is the replication factor of a kafka topic ArgDatabaseTopicReplicationFactor = "replication-factor" diff --git a/commands/databases.go b/commands/databases.go index 18ca9a586..4550de6c7 100644 --- a/commands/databases.go +++ b/commands/databases.go @@ -667,6 +667,7 @@ Database user accounts are scoped to one database cluster, to which they have fu }, } databaseKafkaACLsTxt := `A comma-separated list of kafka ACL rules, in ` + "`" + `topic:permission` + "`" + ` format.` + databaseOpenSearchACLsTxt := `A comma-separated list of OpenSearch ACL rules, in ` + "`" + `index:permission` + "`" + ` format.` userDetailsDesc := ` - The username for the user @@ -695,6 +696,7 @@ To retrieve a list of your databases and their IDs, call `+"`"+`doctl databases AddStringFlag(cmdDatabaseUserCreate, doctl.ArgDatabaseUserMySQLAuthPlugin, "", "", "Sets authorization plugin for a MySQL user. Possible values: `caching_sha2_password` or `mysql_native_password`") AddStringSliceFlag(cmdDatabaseUserCreate, doctl.ArgDatabaseUserKafkaACLs, "", []string{}, databaseKafkaACLsTxt) + AddStringSliceFlag(cmdDatabaseUserCreate, doctl.ArgDatabaseUserOpenSearchACLs, "", []string{}, databaseOpenSearchACLsTxt) cmdDatabaseUserCreate.Example = `The following example creates a new user with the username ` + "`" + `example-user` + "`" + ` for a database cluster with the ID ` + "`" + `ca9f591d-f38h-5555-a0ef-1c02d1d1e35` + "`" + `: doctl databases user create ca9f591d-f38h-5555-a0ef-1c02d1d1e35 example-user` cmdDatabaseUserResetAuth := CmdBuilder(cmd, RunDatabaseUserResetAuth, "reset ", @@ -781,6 +783,17 @@ func RunDatabaseUserCreate(c *CmdConfig) error { } } + openSearchACLs, err := buildDatabaseCreateOpenSearchUserACLs(c) + if err != nil { + return err + } + + if len(openSearchACLs) != 0 { + req.Settings = &godo.DatabaseUserSettings{ + OpenSearchACL: openSearchACLs, + } + } + user, err := c.Databases().CreateUser(databaseID, req) if err != nil { return err @@ -809,6 +822,26 @@ func buildDatabaseCreateKafkaUserACls(c *CmdConfig) (kafkaACls []*godo.KafkaACL, return kafkaACls, nil } +func buildDatabaseCreateOpenSearchUserACLs(c *CmdConfig) (openSearchACLs []*godo.OpenSearchACL, err error) { + acls, err := c.Doit.GetStringSlice(c.NS, doctl.ArgDatabaseUserOpenSearchACLs) + if err != nil { + return nil, err + } + for _, acl := range acls { + pair := strings.SplitN(acl, ":", 2) + if len(pair) != 2 { + return nil, fmt.Errorf("unexpected input value [%v], must be a index:permission pair", pair) + } + + openSearchACL := new(godo.OpenSearchACL) + openSearchACL.Index = pair[0] + openSearchACL.Permission = pair[1] + + openSearchACLs = append(openSearchACLs, openSearchACL) + } + return openSearchACLs, nil +} + func RunDatabaseUserResetAuth(c *CmdConfig) error { if len(c.Args) < 2 { return doctl.NewMissingArgsErr(c.NS) diff --git a/commands/databases_test.go b/commands/databases_test.go index 5a2c92afe..f2f94a35f 100644 --- a/commands/databases_test.go +++ b/commands/databases_test.go @@ -962,6 +962,29 @@ func TestDatabaseUserCreate(t *testing.T) { assert.NoError(t, err) }) + // Successful call with kafka acl set + withTestClient(t, func(config *CmdConfig, tm *tcMocks) { + r := &godo.DatabaseCreateUserRequest{ + Name: testDBUser.Name, + Settings: &godo.DatabaseUserSettings{ + OpenSearchACL: []*godo.OpenSearchACL{ + { + Permission: "admin", + Index: "test", + }, + }, + }, + } + + tm.databases.EXPECT().CreateUser(testDBCluster.ID, r).Return(&testDBUser, nil) + + config.Args = append(config.Args, testDBCluster.ID, testDBUser.Name) + config.Doit.Set(config.NS, doctl.ArgDatabaseUserOpenSearchACLs, "test:admin") + + err := RunDatabaseUserCreate(config) + assert.NoError(t, err) + }) + // Error withTestClient(t, func(config *CmdConfig, tm *tcMocks) { tm.databases.EXPECT().CreateUser( diff --git a/integration/database_user_create_test.go b/integration/database_user_create_test.go index f983f8bb4..8b703dc46 100644 --- a/integration/database_user_create_test.go +++ b/integration/database_user_create_test.go @@ -105,6 +105,25 @@ var _ = suite("database/user/create", func(t *testing.T, when spec.G, it spec.S) expect.Equal(strings.TrimSpace(databaseUserCreateOutput), strings.TrimSpace(string(output))) }) }) + + when("the opensearch acl flag is present", func() { + it("creates the database user", func() { + cmd := exec.Command(builtBinaryPath, + "-t", "some-magic-token", + "-u", server.URL, + "database", + "user", + "create", + "some-database-id", + "some-user-name", + "--opensearch-acl", "log-*:read", + ) + + output, err := cmd.CombinedOutput() + expect.NoError(err, fmt.Sprintf("received error output: %s", output)) + expect.Equal(strings.TrimSpace(databaseUserCreateOutput), strings.TrimSpace(string(output))) + }) + }) }) const ( @@ -118,7 +137,15 @@ some-user-name normal jge5lfxtzhx42iff "name": "{{.Name}}", "role": "normal", "password": "jge5lfxtzhx42iff", - "mysql_settings": { "auth_plugin": "mysql_native_password" } + "mysql_settings": { "auth_plugin": "mysql_native_password" }, + "settings": { + "opensearch_acl": [ + { + "permission": "read", + "index": "log-*" + } + ] + } } } `