Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement mysql user creation and update #698

Merged
merged 37 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
a3a1cb5
implement mysql user creation and update
niristius Aug 13, 2024
93deea9
clean up mysql operations and flag-names
niristius Aug 15, 2024
42bdbfd
fix update step since the updateMysqlUser function uses a PUT route
niristius Aug 21, 2024
9eafaf5
Merge branch 'master' into feature/mysql-user-create-update
martin-helmich Aug 22, 2024
de8ba94
chore: re-generate README
martin-helmich Aug 22, 2024
52e3f8c
fix create mysql user payload type
niristius Aug 26, 2024
9d56b61
Merge remote-tracking branch 'origin' into feature/mysql-user-create-…
niristius Aug 26, 2024
7349ed4
Merge remote-tracking branch 'origin/feature/mysql-user-create-update…
niristius Aug 26, 2024
2f7f926
chore: re-generate README
niristius Aug 26, 2024
397be27
resolve update mysql user discussions
niristius Aug 26, 2024
83e6234
Merge remote-tracking branch 'origin/feature/mysql-user-create-update…
niristius Aug 26, 2024
94569c5
breaking: external access is not a property of the inferred updateMys…
niristius Aug 26, 2024
4663de7
chore: re-generate README
niristius Aug 26, 2024
a1729bc
fix wrong case inclusion and typos
niristius Aug 26, 2024
90bed1d
chore: re-generate README
niristius Aug 26, 2024
82e6ec9
reduce duplication by defining cronjob flags centrally. currently bro…
niristius Aug 27, 2024
40601c6
change mysql user id to arg
niristius Aug 28, 2024
32c0acd
chore: re-generate README
niristius Aug 28, 2024
b5a6e91
keep up with master
niristius Aug 29, 2024
e0f9240
chore: re-generate README
niristius Aug 29, 2024
5f81e55
handle main mysql user deletion
niristius Aug 29, 2024
775a3bc
edit mysql user list command to display uuid
niristius Aug 29, 2024
d9c3c50
centralize flags
niristius Aug 29, 2024
d5c0a1a
chore: re-generate README
niristius Aug 29, 2024
99ebced
add useful flag descriptions and summary and handle update without flags
niristius Aug 29, 2024
9f1ad6c
Merge remote-tracking branch 'origin/feature/mysql-user-create-update…
niristius Aug 29, 2024
782b130
chore: re-generate README
niristius Aug 29, 2024
f97fb48
fix external access flags and remove them from flags.ts due to weird …
niristius Aug 29, 2024
1758b6d
chore: re-generate README
niristius Aug 29, 2024
6cc7b88
fix external access flags and remove them from flags.ts due to weird …
niristius Aug 29, 2024
2c95f7f
fix typo
niristius Aug 29, 2024
31a2133
Merge remote-tracking branch 'origin/feature/mysql-user-create-update…
niristius Aug 29, 2024
19e373d
chore: re-generate README
niristius Aug 29, 2024
230d74c
Merge branch 'master' into feature/mysql-user-create-update
niristius Aug 29, 2024
c579bb4
Update src/commands/database/mysql/user/create.tsx
martin-helmich Aug 29, 2024
2a099f0
chore: re-generate README
martin-helmich Aug 29, 2024
5e24bb5
Fix eslint issues
martin-helmich Aug 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 118 additions & 0 deletions docs/database.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ Manage databases (like MySQL and Redis) in your projects
* [`mw database mysql phpmyadmin DATABASE-ID`](#mw-database-mysql-phpmyadmin-database-id)
* [`mw database mysql port-forward DATABASE-ID`](#mw-database-mysql-port-forward-database-id)
* [`mw database mysql shell DATABASE-ID`](#mw-database-mysql-shell-database-id)
* [`mw database mysql user create`](#mw-database-mysql-user-create)
* [`mw database mysql user delete USER-ID`](#mw-database-mysql-user-delete-user-id)
* [`mw database mysql user get ID`](#mw-database-mysql-user-get-id)
* [`mw database mysql user list`](#mw-database-mysql-user-list)
* [`mw database mysql user update USER-ID`](#mw-database-mysql-user-update-user-id)
* [`mw database mysql versions`](#mw-database-mysql-versions)
* [`mw database redis create`](#mw-database-redis-create)
* [`mw database redis get ID`](#mw-database-redis-get-id)
Expand Down Expand Up @@ -457,6 +459,63 @@ FLAG DESCRIPTIONS
You can also set this value by setting the MITTWALD_SSH_USER environment variable.
```

## `mw database mysql user create`

Create a new MySQL user

```
USAGE
$ mw database mysql user create --database-id <value> --access-level readonly|full --description <value> --password <value>
[-q] [--access-ip-mask <value> --enable-external-access]

FLAGS
-q, --quiet suppress process output and only display a machine-readable summary.
--access-ip-mask=<value> IP to restrict external access to.
--access-level=<option> (required) Set the access level permissions for the SFTP user.
<options: readonly|full>
--database-id=<value> (required) MySQL database ID to create a user for.
--description=<value> (required) Set the description for the MySQL user.
--enable-external-access Enable external access for this MySQL user.
--password=<value> (required) Password used for authentication

DESCRIPTION
Create a new MySQL user

FLAG DESCRIPTIONS
-q, --quiet suppress process output and only display a machine-readable summary.

This flag controls if you want to see the process output or only a summary. When using mw non-interactively (e.g. in
scripts), you can use this flag to easily get the IDs of created resources for further processing.

--access-ip-mask=<value> IP to restrict external access to.

If specified as IPv4, external access will be restricted to only the specified IP addresses when external access is
enabled.

--access-level=readonly|full Set the access level permissions for the SFTP user.

Must be specified as either readonly or full. Grant the user either read-only or full file read and write access to
files.

--database-id=<value> MySQL database ID to create a user for.

Can be specified as UUID or shortId. The user will be created for the specified database.

--description=<value> Set the description for the MySQL user.

Set the description for the specified MySQL user to be displayed in mStudio and with the list command.

--enable-external-access Enable external access for this MySQL user.

By default, external access is disabled for newly created MySQL users. Using this flag will enable external access
for this user on creation. External access can be restricted to specific IP addresses using the 'access-ip-mask'
flag.

--password=<value> Password used for authentication

Specify a password to use for authentication when connecting to the database with this user.
```

## `mw database mysql user delete USER-ID`

Delete a MySQL user
Expand Down Expand Up @@ -525,6 +584,65 @@ DESCRIPTION
List MySQL users belonging to a database.
```

## `mw database mysql user update USER-ID`

Update an existing MySQL user

```
USAGE
$ mw database mysql user update USER-ID [-q] [--access-level readonly|full] [--description <value>] [--password <value>]
[--access-ip-mask <value>] [--enable-external-access | --disable-external-access]

ARGUMENTS
USER-ID ID of the MySQL user to update.

FLAGS
-q, --quiet suppress process output and only display a machine-readable summary.
--access-ip-mask=<value> IP to restrict external access to.
--access-level=<option> Set the access level permissions for the SFTP user.
<options: readonly|full>
--description=<value> Set the description for the MySQL user.
--disable-external-access Disable external access.
--enable-external-access Enable external access.
--password=<value> Password used for authentication

DESCRIPTION
Update an existing MySQL user

FLAG DESCRIPTIONS
-q, --quiet suppress process output and only display a machine-readable summary.

This flag controls if you want to see the process output or only a summary. When using mw non-interactively (e.g. in
scripts), you can use this flag to easily get the IDs of created resources for further processing.

--access-ip-mask=<value> IP to restrict external access to.

If specified as IPv4, external access will be restricted to only the specified IP addresses when external access is
enabled.

--access-level=readonly|full Set the access level permissions for the SFTP user.

Must be specified as either readonly or full. Grant the user either read-only or full file read and write access to
files.

--description=<value> Set the description for the MySQL user.

Set the description for the specified MySQL user to be displayed in mStudio and with the list command.

--disable-external-access Disable external access.

Set external access for this MySQL user to disabled. External access will not be possible for this user.

--enable-external-access Enable external access.

Set external access for this MySQL user to enabled. External access by this user will be possible. External access
can be restricted to certain IP addresses using the 'access-ip-mask' flag.

--password=<value> Password used for authentication

Specify a password to use for authentication when connecting to the database with this user.
```

## `mw database mysql versions`

List available MySQL versions.
Expand Down
112 changes: 112 additions & 0 deletions src/commands/database/mysql/user/create.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { ExecRenderBaseCommand } from "../../../../lib/basecommands/ExecRenderBaseCommand.js";
import {
makeProcessRenderer,
processFlags,
} from "../../../../rendering/process/process_flags.js";
import { ReactNode } from "react";
import { assertStatus } from "@mittwald/api-client-commons";
import { Success } from "../../../../rendering/react/components/Success.js";
import { Value } from "../../../../rendering/react/components/Value.js";
import type { MittwaldAPIV2Client } from "@mittwald/api-client";
import { mysqlUserFlagDefinitions } from "../../../../lib/resources/database/mysql/user/flags.js";
import { Flags } from "@oclif/core";

type Result = {
mysqlUserId: string;
};

type MyQSLUserCreationData = Parameters<
MittwaldAPIV2Client["database"]["createMysqlUser"]
>[0]["data"];

export class Create extends ExecRenderBaseCommand<typeof Create, Result> {
static description = "Create a new MySQL user";
static flags = {
...processFlags,
"database-id": mysqlUserFlagDefinitions["database-id"]({ required: true }),
"access-level": mysqlUserFlagDefinitions["access-level"]({
required: true,
}),
description: mysqlUserFlagDefinitions.description({ required: true }),
password: mysqlUserFlagDefinitions.password({ required: true }),
"access-ip-mask": mysqlUserFlagDefinitions["access-ip-mask"]({
dependsOn: ["enable-external-access"],
}),
"enable-external-access": Flags.boolean({
summary: "Enable external access for this MySQL user.",
description:
"By default, external access is disabled for newly created MySQL users. " +
"Using this flag will enable external access for this user on creation. " +
"External access can be restricted to specific IP addresses using the 'access-ip-mask' flag.",
}),
};

protected async exec(): Promise<Result> {
const process = makeProcessRenderer(
this.flags,
"Creating a new MySQL User",
);

const {
"database-id": mysqlDatabaseId,
"access-level": accessLevel,
description,
password,
"access-ip-mask": accessIpMask,
"enable-external-access": enableExternalAccess,
} = this.flags;

const createMysqlUserPayload: MyQSLUserCreationData = {
accessLevel: accessLevel == "full" ? "full" : "readonly",
databaseId: mysqlDatabaseId,
description,
password,
};

if (accessIpMask) {
createMysqlUserPayload.accessIpMask = accessIpMask;
}

if (enableExternalAccess) {
createMysqlUserPayload.externalAccess = true;
}

const { id: mysqlUserId } = await process.runStep(
"creating MySQL user",
async () => {
const r = await this.apiClient.database.createMysqlUser({
mysqlDatabaseId,
data: createMysqlUserPayload,
});
assertStatus(r, 201);
return r.data;
},
);

const mysqlUser = await process.runStep(
"checking newly created MySQL user",
async () => {
const r = await this.apiClient.database.getMysqlUser({
mysqlUserId,
});
assertStatus(r, 200);
return r.data;
},
);

await process.complete(
<Success>
The mysql user "<Value>{mysqlUser.description}</Value>" was successfully
created.
</Success>,
);

return { mysqlUserId };
}

protected render({ mysqlUserId }: Result): ReactNode {
if (this.flags.quiet) {
return mysqlUserId;
}
}
}
17 changes: 13 additions & 4 deletions src/commands/database/mysql/user/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,26 @@ export default class Delete extends DeleteBaseCommand<typeof Delete> {
static flags = { ...DeleteBaseCommand.baseFlags };
static args = {
"user-id": Args.string({
description: "ID of the MySQL user to delete.",
required: true,
description: "ID of the MySQL user to delete.",
}),
};

protected async deleteResource(): Promise<void> {
const mysqlUserId = this.args["user-id"];
const response = await this.apiClient.database.deleteMysqlUser({
const currentMysqlUserData = await this.apiClient.database.getMysqlUser({
mysqlUserId,
});

assertSuccess(response);
assertSuccess(currentMysqlUserData);
if (currentMysqlUserData.data.mainUser) {
throw new Error(
"The main MySQL user can not be deleted manually. It's deleted only if its database is deleted.",
);
} else {
const response = await this.apiClient.database.deleteMysqlUser({
mysqlUserId,
});
assertSuccess(response);
}
}
}
12 changes: 9 additions & 3 deletions src/commands/database/mysql/user/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,18 @@ export class List extends ListBaseCommand<typeof List, ResponseItem, Response> {
}

protected getColumns(data: ResponseItem[]): ListColumns<ResponseItem> {
const { id, name, createdAt } = super.getColumns(data, {
const { createdAt } = super.getColumns(data, {
shortIdKey: "name",
});
return {
id,
name,
id: {
header: "ID",
get: (i) => i.id,
},
name: {
header: "Username",
get: (i) => i.name,
},
description: {},
mainUser: {
header: "Main user",
Expand Down
Loading