Skip to content

Commit

Permalink
Databases: Add OpenSearch acl option to database user
Browse files Browse the repository at this point in the history
  • Loading branch information
Rahul Bhardwaj committed Sep 9, 2024
1 parent cba40bb commit f4f5430
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 1 deletion.
2 changes: 2 additions & 0 deletions args.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
33 changes: 33 additions & 0 deletions commands/databases.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 <database-cluster-id> <user-name> <new-auth-mode>",
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
23 changes: 23 additions & 0 deletions commands/databases_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
29 changes: 28 additions & 1 deletion integration/database_user_create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -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-*"
}
]
}
}
}
`
Expand Down

0 comments on commit f4f5430

Please sign in to comment.