cdk-rds-sql 9.1.0 → 9.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.jsii +2 -2
- package/AGENTS.md +66 -0
- package/CLAUDE.md +66 -0
- package/lib/database.js +1 -1
- package/lib/handler/index.js +179 -60
- package/lib/iam-grant.js +1 -1
- package/lib/provider.js +1 -1
- package/lib/role.js +1 -1
- package/lib/schema.js +1 -1
- package/lib/sql.js +1 -1
- package/package.json +1 -1
package/.jsii
CHANGED
|
@@ -11289,6 +11289,6 @@
|
|
|
11289
11289
|
"symbolId": "src/sql:SqlProps"
|
|
11290
11290
|
}
|
|
11291
11291
|
},
|
|
11292
|
-
"version": "9.1.
|
|
11293
|
-
"fingerprint": "
|
|
11292
|
+
"version": "9.1.2",
|
|
11293
|
+
"fingerprint": "DOvwHf5DkaTFsOXeSwFamf8PiOcAMMtjOeFmJ2ALNG4="
|
|
11294
11294
|
}
|
package/AGENTS.md
CHANGED
|
@@ -105,6 +105,72 @@ Projen tasks:
|
|
|
105
105
|
4. `biome`: Runs formatting and linting
|
|
106
106
|
5. `typecheck`: Runs typecheck
|
|
107
107
|
|
|
108
|
+
## GitHub Actions checks
|
|
109
|
+
|
|
110
|
+
`hk.pkl` lints workflows, Dependabot config, and composite actions with
|
|
111
|
+
jactionlint. `mise.toml` pins `jactionlint` 2.3.0 and the `shellcheck` 0.11.0
|
|
112
|
+
that jactionlint shells out to. Run that pin through mise. A `jactionlint`
|
|
113
|
+
already on `PATH` can be another version.
|
|
114
|
+
|
|
115
|
+
The hk step is `jactionlint --baseline --sarif-hide-baselined --format sarif`.
|
|
116
|
+
`.github/jactionlint.yaml` sets `baseline: auto`, so findings recorded in
|
|
117
|
+
`.github/jactionlint-baseline.json` are accepted and any other finding fails.
|
|
118
|
+
`mise run check` includes the step. To run only it:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
mise exec -- hk check --all --step jactionlint
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Refreshing the jactionlint baseline
|
|
125
|
+
|
|
126
|
+
The baseline matches by fingerprint and context hash, which include the finding
|
|
127
|
+
text. After a projen upgrade, a Dependabot action bump, or another
|
|
128
|
+
generated-workflow change, an already accepted finding can fail again because
|
|
129
|
+
the old entry still names the previous text. The old entry then matches
|
|
130
|
+
nothing. Changing `actions/setup-node@v7.0.0` to `v7.0.1` in
|
|
131
|
+
`.github/workflows/build.yml` does this for the three baselined `unpinned-uses`
|
|
132
|
+
findings in that file.
|
|
133
|
+
|
|
134
|
+
Re-baseline only for that drift, and only after confirming each new finding is
|
|
135
|
+
that stale-baseline effect and not a real regression. Commit the refreshed
|
|
136
|
+
`.github/jactionlint-baseline.json` in the same commit or pull request as the
|
|
137
|
+
workflow change. Do not refresh the baseline to hide a finding you introduced,
|
|
138
|
+
and do not edit the JSON by hand.
|
|
139
|
+
|
|
140
|
+
1. List findings the baseline does not hide, and compare them with the workflow
|
|
141
|
+
diff. Expected drift is the same rule on the same step, with the message now
|
|
142
|
+
naming the new generated text:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
mise exec -- jactionlint --baseline
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
2. List entries that no longer match anything. They are reported as
|
|
149
|
+
`unused-baseline-entry`:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
mise exec -- jactionlint --baseline-check
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
3. Rewrite the baseline. Do not pass workflow paths, and do not combine this
|
|
156
|
+
with `--baseline`. File arguments keep every other file's entries.
|
|
157
|
+
`--baseline-write` records every current finding and exits 0 even when new
|
|
158
|
+
findings exist, so review the baseline diff before committing:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
mise exec -- jactionlint --baseline-write
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
4. Confirm the hk step is clean:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
mise exec -- hk check --all --step jactionlint
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
`.github/jactionlint.yaml` mentions the same `--baseline-write` flag. Use the
|
|
171
|
+
mise form so the `mise.toml` pin is the binary that runs. Install tools with
|
|
172
|
+
`mise install` if `mise exec` cannot find `jactionlint`.
|
|
173
|
+
|
|
108
174
|
## SSL/TLS Configuration
|
|
109
175
|
|
|
110
176
|
- SSL is enabled by default
|
package/CLAUDE.md
CHANGED
|
@@ -105,6 +105,72 @@ Projen tasks:
|
|
|
105
105
|
4. `biome`: Runs formatting and linting
|
|
106
106
|
5. `typecheck`: Runs typecheck
|
|
107
107
|
|
|
108
|
+
## GitHub Actions checks
|
|
109
|
+
|
|
110
|
+
`hk.pkl` lints workflows, Dependabot config, and composite actions with
|
|
111
|
+
jactionlint. `mise.toml` pins `jactionlint` 2.3.0 and the `shellcheck` 0.11.0
|
|
112
|
+
that jactionlint shells out to. Run that pin through mise. A `jactionlint`
|
|
113
|
+
already on `PATH` can be another version.
|
|
114
|
+
|
|
115
|
+
The hk step is `jactionlint --baseline --sarif-hide-baselined --format sarif`.
|
|
116
|
+
`.github/jactionlint.yaml` sets `baseline: auto`, so findings recorded in
|
|
117
|
+
`.github/jactionlint-baseline.json` are accepted and any other finding fails.
|
|
118
|
+
`mise run check` includes the step. To run only it:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
mise exec -- hk check --all --step jactionlint
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Refreshing the jactionlint baseline
|
|
125
|
+
|
|
126
|
+
The baseline matches by fingerprint and context hash, which include the finding
|
|
127
|
+
text. After a projen upgrade, a Dependabot action bump, or another
|
|
128
|
+
generated-workflow change, an already accepted finding can fail again because
|
|
129
|
+
the old entry still names the previous text. The old entry then matches
|
|
130
|
+
nothing. Changing `actions/setup-node@v7.0.0` to `v7.0.1` in
|
|
131
|
+
`.github/workflows/build.yml` does this for the three baselined `unpinned-uses`
|
|
132
|
+
findings in that file.
|
|
133
|
+
|
|
134
|
+
Re-baseline only for that drift, and only after confirming each new finding is
|
|
135
|
+
that stale-baseline effect and not a real regression. Commit the refreshed
|
|
136
|
+
`.github/jactionlint-baseline.json` in the same commit or pull request as the
|
|
137
|
+
workflow change. Do not refresh the baseline to hide a finding you introduced,
|
|
138
|
+
and do not edit the JSON by hand.
|
|
139
|
+
|
|
140
|
+
1. List findings the baseline does not hide, and compare them with the workflow
|
|
141
|
+
diff. Expected drift is the same rule on the same step, with the message now
|
|
142
|
+
naming the new generated text:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
mise exec -- jactionlint --baseline
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
2. List entries that no longer match anything. They are reported as
|
|
149
|
+
`unused-baseline-entry`:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
mise exec -- jactionlint --baseline-check
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
3. Rewrite the baseline. Do not pass workflow paths, and do not combine this
|
|
156
|
+
with `--baseline`. File arguments keep every other file's entries.
|
|
157
|
+
`--baseline-write` records every current finding and exits 0 even when new
|
|
158
|
+
findings exist, so review the baseline diff before committing:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
mise exec -- jactionlint --baseline-write
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
4. Confirm the hk step is clean:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
mise exec -- hk check --all --step jactionlint
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
`.github/jactionlint.yaml` mentions the same `--baseline-write` flag. Use the
|
|
171
|
+
mise form so the `mise.toml` pin is the binary that runs. Install tools with
|
|
172
|
+
`mise install` if `mise exec` cannot find `jactionlint`.
|
|
173
|
+
|
|
108
174
|
## SSL/TLS Configuration
|
|
109
175
|
|
|
110
176
|
- SSL is enabled by default
|
package/lib/database.js
CHANGED
|
@@ -14,7 +14,7 @@ class ImportedDatabase extends constructs_1.Construct {
|
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
class Database extends aws_cdk_lib_1.CustomResource {
|
|
17
|
-
static [JSII_RTTI_SYMBOL_1] = { fqn: "cdk-rds-sql.Database", version: "9.1.
|
|
17
|
+
static [JSII_RTTI_SYMBOL_1] = { fqn: "cdk-rds-sql.Database", version: "9.1.2" };
|
|
18
18
|
/**
|
|
19
19
|
* Return a Database based upon name only. Use for importing existing databases.
|
|
20
20
|
*/
|
package/lib/handler/index.js
CHANGED
|
@@ -27144,6 +27144,13 @@ var AbstractEngine = class {
|
|
|
27144
27144
|
this.log = process.env.LOGGER === "true" ? console.debug : (_message, ..._optionalParams) => {
|
|
27145
27145
|
};
|
|
27146
27146
|
}
|
|
27147
|
+
/**
|
|
27148
|
+
* Load connection state required before SQL is rendered.
|
|
27149
|
+
* MySQL literal escaping depends on the session SQL mode, so the handler
|
|
27150
|
+
* calls this before create/update/delete SQL is built.
|
|
27151
|
+
*/
|
|
27152
|
+
async prepare(_config) {
|
|
27153
|
+
}
|
|
27147
27154
|
/**
|
|
27148
27155
|
* Parse password field from secret. Returns void on error or if no password field exists.
|
|
27149
27156
|
*/
|
|
@@ -27414,11 +27421,78 @@ var DsqlEngine = class extends AbstractEngine {
|
|
|
27414
27421
|
|
|
27415
27422
|
// lambda/engine.mysql.ts
|
|
27416
27423
|
var fs = __toESM(require("fs"));
|
|
27424
|
+
|
|
27425
|
+
// lambda/mysql-quote.ts
|
|
27426
|
+
var NO_BACKSLASH_ESCAPES = "NO_BACKSLASH_ESCAPES";
|
|
27427
|
+
function quoteMysqlIdentifier(identifier) {
|
|
27428
|
+
return `\`${identifier.replaceAll("`", "``")}\``;
|
|
27429
|
+
}
|
|
27430
|
+
function quoteMysqlString(value, sqlMode) {
|
|
27431
|
+
const noBackslashEscapes = sqlMode.split(",").some((mode) => mode.trim() === NO_BACKSLASH_ESCAPES);
|
|
27432
|
+
let quoted = "";
|
|
27433
|
+
for (const char of value) {
|
|
27434
|
+
if (char === "'") {
|
|
27435
|
+
quoted += "''";
|
|
27436
|
+
continue;
|
|
27437
|
+
}
|
|
27438
|
+
if (!noBackslashEscapes) {
|
|
27439
|
+
const escaped = backslashEscape(char);
|
|
27440
|
+
if (escaped !== void 0) {
|
|
27441
|
+
quoted += escaped;
|
|
27442
|
+
continue;
|
|
27443
|
+
}
|
|
27444
|
+
}
|
|
27445
|
+
quoted += char;
|
|
27446
|
+
}
|
|
27447
|
+
return `'${quoted}'`;
|
|
27448
|
+
}
|
|
27449
|
+
function backslashEscape(char) {
|
|
27450
|
+
switch (char) {
|
|
27451
|
+
case "\\":
|
|
27452
|
+
return "\\\\";
|
|
27453
|
+
case "\0":
|
|
27454
|
+
return "\\0";
|
|
27455
|
+
case "\b":
|
|
27456
|
+
return "\\b";
|
|
27457
|
+
case "\n":
|
|
27458
|
+
return "\\n";
|
|
27459
|
+
case "\r":
|
|
27460
|
+
return "\\r";
|
|
27461
|
+
case " ":
|
|
27462
|
+
return "\\t";
|
|
27463
|
+
case "":
|
|
27464
|
+
return "\\Z";
|
|
27465
|
+
default:
|
|
27466
|
+
return void 0;
|
|
27467
|
+
}
|
|
27468
|
+
}
|
|
27469
|
+
|
|
27470
|
+
// lambda/engine.mysql.ts
|
|
27417
27471
|
var MysqlEngine = class extends AbstractEngine {
|
|
27472
|
+
/**
|
|
27473
|
+
* Session SQL mode used to quote literals. Empty means MySQL's default
|
|
27474
|
+
* (backslash escapes enabled) so unit tests can render SQL without a
|
|
27475
|
+
* connection. Production replaces this from @@SESSION.sql_mode.
|
|
27476
|
+
*/
|
|
27477
|
+
sqlMode = "";
|
|
27478
|
+
/** Visible to tests. Production sets this from the live session in prepare(). */
|
|
27479
|
+
setSqlMode(sqlMode) {
|
|
27480
|
+
this.sqlMode = sqlMode;
|
|
27481
|
+
}
|
|
27482
|
+
async prepare(config) {
|
|
27483
|
+
const connection = await this.openConnection(config);
|
|
27484
|
+
try {
|
|
27485
|
+
const [rows] = await connection.query("SELECT @@SESSION.sql_mode AS sqlMode");
|
|
27486
|
+
this.sqlMode = readSqlMode(rows);
|
|
27487
|
+
this.log(`MySQL session sql_mode is ${this.sqlMode === "" ? "(empty)" : this.sqlMode}`);
|
|
27488
|
+
} finally {
|
|
27489
|
+
await connection.end();
|
|
27490
|
+
}
|
|
27491
|
+
}
|
|
27418
27492
|
createDatabase(resourceId, props) {
|
|
27419
|
-
const sql = [`CREATE DATABASE IF NOT EXISTS
|
|
27493
|
+
const sql = [`CREATE DATABASE IF NOT EXISTS ${quoteMysqlIdentifier(resourceId)}`];
|
|
27420
27494
|
if (props.Owner) {
|
|
27421
|
-
sql.push(
|
|
27495
|
+
sql.push(this.grantDatabase(resourceId, props.Owner));
|
|
27422
27496
|
sql.push("FLUSH PRIVILEGES");
|
|
27423
27497
|
}
|
|
27424
27498
|
return sql;
|
|
@@ -27427,24 +27501,22 @@ var MysqlEngine = class extends AbstractEngine {
|
|
|
27427
27501
|
throw new Error("Renaming database is not supported in MySQL.");
|
|
27428
27502
|
}
|
|
27429
27503
|
deleteDatabase(resourceId, _masterUser) {
|
|
27430
|
-
return [`DROP DATABASE IF EXISTS
|
|
27504
|
+
return [`DROP DATABASE IF EXISTS ${quoteMysqlIdentifier(resourceId)}`];
|
|
27431
27505
|
}
|
|
27432
27506
|
async createRole(resourceId, props) {
|
|
27433
27507
|
const sql = [];
|
|
27434
27508
|
if (props.EnableIamAuth) {
|
|
27435
|
-
sql.push(
|
|
27436
|
-
`CREATE USER IF NOT EXISTS '${resourceId}'@'%' IDENTIFIED WITH AWSAuthenticationPlugin as 'RDS'`
|
|
27437
|
-
);
|
|
27509
|
+
sql.push(this.iamUser(resourceId, true));
|
|
27438
27510
|
} else {
|
|
27439
27511
|
if (!props.PasswordArn) throw new Error("No PasswordArn provided");
|
|
27440
27512
|
const password = await this.getPassword(props.PasswordArn);
|
|
27441
27513
|
if (!password) throw `Cannot parse password from ${props.PasswordArn}`;
|
|
27442
|
-
sql.push(
|
|
27514
|
+
sql.push(this.passwordUser(resourceId, password, true));
|
|
27443
27515
|
}
|
|
27444
27516
|
if (props.DatabaseName) {
|
|
27445
|
-
sql.push(
|
|
27517
|
+
sql.push(this.grantDatabase(props.DatabaseName, resourceId));
|
|
27446
27518
|
}
|
|
27447
|
-
appendMaxUserConnections(sql, resourceId, props.MaxUserConnections);
|
|
27519
|
+
appendMaxUserConnections(sql, this.account(resourceId), props.MaxUserConnections);
|
|
27448
27520
|
sql.push("FLUSH PRIVILEGES");
|
|
27449
27521
|
return sql;
|
|
27450
27522
|
}
|
|
@@ -27452,42 +27524,40 @@ var MysqlEngine = class extends AbstractEngine {
|
|
|
27452
27524
|
const sql = [];
|
|
27453
27525
|
if (oldResourceId !== resourceId) {
|
|
27454
27526
|
if (props.EnableIamAuth) {
|
|
27455
|
-
sql.push(
|
|
27456
|
-
`CREATE USER IF NOT EXISTS '${resourceId}'@'%' IDENTIFIED WITH AWSAuthenticationPlugin as 'RDS'`
|
|
27457
|
-
);
|
|
27527
|
+
sql.push(this.iamUser(resourceId, true));
|
|
27458
27528
|
} else if (props?.PasswordArn) {
|
|
27459
27529
|
const password = await this.getPassword(props.PasswordArn);
|
|
27460
27530
|
if (!password) throw `Cannot parse password from ${props.PasswordArn}`;
|
|
27461
|
-
sql.push(
|
|
27531
|
+
sql.push(this.passwordUser(resourceId, password, true));
|
|
27462
27532
|
} else {
|
|
27463
|
-
sql.push(`CREATE USER IF NOT EXISTS
|
|
27464
|
-
sql.push(`ALTER USER
|
|
27533
|
+
sql.push(`CREATE USER IF NOT EXISTS ${this.account(resourceId)} IDENTIFIED BY UUID()`);
|
|
27534
|
+
sql.push(`ALTER USER ${this.account(resourceId)} PASSWORD EXPIRE`);
|
|
27465
27535
|
}
|
|
27466
|
-
sql.push(`DROP USER IF EXISTS
|
|
27536
|
+
sql.push(`DROP USER IF EXISTS ${this.account(oldResourceId)}`);
|
|
27467
27537
|
} else {
|
|
27468
27538
|
if (props?.EnableIamAuth && !oldProps?.EnableIamAuth) {
|
|
27469
|
-
sql.push(`DROP USER IF EXISTS
|
|
27470
|
-
sql.push(
|
|
27539
|
+
sql.push(`DROP USER IF EXISTS ${this.account(resourceId)}`);
|
|
27540
|
+
sql.push(this.iamUser(resourceId, false));
|
|
27471
27541
|
} else if (!props?.EnableIamAuth && oldProps?.EnableIamAuth) {
|
|
27472
|
-
sql.push(`DROP USER IF EXISTS
|
|
27542
|
+
sql.push(`DROP USER IF EXISTS ${this.account(resourceId)}`);
|
|
27473
27543
|
if (props?.PasswordArn) {
|
|
27474
27544
|
const password = await this.getPassword(props.PasswordArn);
|
|
27475
27545
|
if (!password) throw `Cannot parse password from ${props.PasswordArn}`;
|
|
27476
|
-
sql.push(
|
|
27546
|
+
sql.push(this.passwordUser(resourceId, password, false));
|
|
27477
27547
|
}
|
|
27478
27548
|
} else if (!props?.EnableIamAuth && props?.PasswordArn) {
|
|
27479
27549
|
const password = await this.getPassword(props.PasswordArn);
|
|
27480
27550
|
if (!password) throw `Cannot parse password from ${props.PasswordArn}`;
|
|
27481
|
-
sql.push(`ALTER USER
|
|
27551
|
+
sql.push(`ALTER USER ${this.account(resourceId)} IDENTIFIED BY ${this.literal(password)}`);
|
|
27482
27552
|
}
|
|
27483
27553
|
}
|
|
27484
27554
|
if (oldProps?.DatabaseName && props?.DatabaseName && oldProps.DatabaseName !== props.DatabaseName) {
|
|
27485
|
-
sql.push(
|
|
27555
|
+
sql.push(this.revokeDatabase(oldProps.DatabaseName, resourceId));
|
|
27486
27556
|
}
|
|
27487
27557
|
if (props?.DatabaseName) {
|
|
27488
|
-
sql.push(
|
|
27558
|
+
sql.push(this.grantDatabase(props.DatabaseName, resourceId));
|
|
27489
27559
|
}
|
|
27490
|
-
appendMaxUserConnections(sql, resourceId, props.MaxUserConnections);
|
|
27560
|
+
appendMaxUserConnections(sql, this.account(resourceId), props.MaxUserConnections);
|
|
27491
27561
|
if (sql.length > 0) {
|
|
27492
27562
|
sql.push("FLUSH PRIVILEGES");
|
|
27493
27563
|
}
|
|
@@ -27496,9 +27566,9 @@ var MysqlEngine = class extends AbstractEngine {
|
|
|
27496
27566
|
async deleteRole(resourceId, props) {
|
|
27497
27567
|
const sql = [];
|
|
27498
27568
|
if (props?.DatabaseName) {
|
|
27499
|
-
sql.push(
|
|
27569
|
+
sql.push(this.revokeDatabase(props.DatabaseName, resourceId));
|
|
27500
27570
|
}
|
|
27501
|
-
sql.push(`DROP USER IF EXISTS
|
|
27571
|
+
sql.push(`DROP USER IF EXISTS ${this.account(resourceId)}`);
|
|
27502
27572
|
sql.push("FLUSH PRIVILEGES");
|
|
27503
27573
|
return sql;
|
|
27504
27574
|
}
|
|
@@ -27530,6 +27600,48 @@ var MysqlEngine = class extends AbstractEngine {
|
|
|
27530
27600
|
throw new Error("IAM grants are only supported with DSQL clusters");
|
|
27531
27601
|
}
|
|
27532
27602
|
async executeSQL(sql, config) {
|
|
27603
|
+
const isSslEnabled = process.env.SSL ? JSON.parse(process.env.SSL) : true;
|
|
27604
|
+
const connection = await this.openConnection(config);
|
|
27605
|
+
this.log(
|
|
27606
|
+
`Connecting to MySQL/MariaDB host ${config.host}:${config.port}${isSslEnabled ? " using a secure connection" : ""}, database ${config.database} as ${config.user}`
|
|
27607
|
+
);
|
|
27608
|
+
this.log("Executing SQL", sql);
|
|
27609
|
+
try {
|
|
27610
|
+
if (typeof sql === "string") {
|
|
27611
|
+
return await connection.query(sql);
|
|
27612
|
+
} else if (sql) {
|
|
27613
|
+
return await Promise.all(sql.map((statement) => connection.query(statement)));
|
|
27614
|
+
}
|
|
27615
|
+
} finally {
|
|
27616
|
+
await connection.end();
|
|
27617
|
+
}
|
|
27618
|
+
}
|
|
27619
|
+
literal(value) {
|
|
27620
|
+
return quoteMysqlString(value, this.sqlMode);
|
|
27621
|
+
}
|
|
27622
|
+
/** Account name as a string literal. The host is the fixed VPC wildcard. */
|
|
27623
|
+
account(user) {
|
|
27624
|
+
return `${this.literal(user)}@'%'`;
|
|
27625
|
+
}
|
|
27626
|
+
iamUser(user, ifNotExists) {
|
|
27627
|
+
const exists = ifNotExists ? "IF NOT EXISTS " : "";
|
|
27628
|
+
const account = this.account(user);
|
|
27629
|
+
return `CREATE USER ${exists}${account} IDENTIFIED WITH AWSAuthenticationPlugin as 'RDS'`;
|
|
27630
|
+
}
|
|
27631
|
+
passwordUser(user, password, ifNotExists) {
|
|
27632
|
+
const exists = ifNotExists ? "IF NOT EXISTS " : "";
|
|
27633
|
+
const account = this.account(user);
|
|
27634
|
+
return `CREATE USER ${exists}${account} IDENTIFIED BY ${this.literal(password)}`;
|
|
27635
|
+
}
|
|
27636
|
+
grantDatabase(databaseName, user) {
|
|
27637
|
+
const database = quoteMysqlIdentifier(databaseName);
|
|
27638
|
+
return `GRANT ALL PRIVILEGES ON ${database}.* TO ${this.account(user)}`;
|
|
27639
|
+
}
|
|
27640
|
+
revokeDatabase(databaseName, user) {
|
|
27641
|
+
const database = quoteMysqlIdentifier(databaseName);
|
|
27642
|
+
return `REVOKE ALL PRIVILEGES ON ${database}.* FROM ${this.account(user)}`;
|
|
27643
|
+
}
|
|
27644
|
+
async openConnection(config) {
|
|
27533
27645
|
const { createConnection } = await Promise.resolve().then(() => __toESM(require_promise()));
|
|
27534
27646
|
const isSslEnabled = process.env.SSL ? JSON.parse(process.env.SSL) : true;
|
|
27535
27647
|
const sslOptions = isSslEnabled ? {
|
|
@@ -27538,7 +27650,7 @@ var MysqlEngine = class extends AbstractEngine {
|
|
|
27538
27650
|
rejectUnauthorized: true
|
|
27539
27651
|
}
|
|
27540
27652
|
} : {};
|
|
27541
|
-
|
|
27653
|
+
return createConnection({
|
|
27542
27654
|
host: config.host,
|
|
27543
27655
|
port: config.port,
|
|
27544
27656
|
user: config.user,
|
|
@@ -27547,31 +27659,30 @@ var MysqlEngine = class extends AbstractEngine {
|
|
|
27547
27659
|
connectTimeout: 3e4,
|
|
27548
27660
|
multipleStatements: true,
|
|
27549
27661
|
...sslOptions
|
|
27550
|
-
};
|
|
27551
|
-
this.log(
|
|
27552
|
-
`Connecting to MySQL/MariaDB host ${connectionConfig.host}:${connectionConfig.port}${isSslEnabled ? " using a secure connection" : ""}, database ${connectionConfig.database} as ${connectionConfig.user}`
|
|
27553
|
-
);
|
|
27554
|
-
this.log("Executing SQL", sql);
|
|
27555
|
-
const connection = await createConnection(connectionConfig);
|
|
27556
|
-
try {
|
|
27557
|
-
if (typeof sql === "string") {
|
|
27558
|
-
return await connection.query(sql);
|
|
27559
|
-
} else if (sql) {
|
|
27560
|
-
return await Promise.all(sql.map((statement) => connection.query(statement)));
|
|
27561
|
-
}
|
|
27562
|
-
} finally {
|
|
27563
|
-
await connection.end();
|
|
27564
|
-
}
|
|
27662
|
+
});
|
|
27565
27663
|
}
|
|
27566
27664
|
};
|
|
27567
|
-
function appendMaxUserConnections(sql,
|
|
27665
|
+
function appendMaxUserConnections(sql, account, maxUserConnections) {
|
|
27568
27666
|
if (maxUserConnections === void 0) {
|
|
27569
27667
|
return;
|
|
27570
27668
|
}
|
|
27571
27669
|
if (!Number.isInteger(maxUserConnections) || maxUserConnections < 0) {
|
|
27572
27670
|
throw new Error(`MaxUserConnections must be a non-negative integer, got ${maxUserConnections}`);
|
|
27573
27671
|
}
|
|
27574
|
-
sql.push(`ALTER USER
|
|
27672
|
+
sql.push(`ALTER USER ${account} WITH MAX_USER_CONNECTIONS ${maxUserConnections}`);
|
|
27673
|
+
}
|
|
27674
|
+
function readSqlMode(rows) {
|
|
27675
|
+
if (!Array.isArray(rows) || rows.length !== 1) {
|
|
27676
|
+
throw new Error("MySQL did not return a session sql_mode");
|
|
27677
|
+
}
|
|
27678
|
+
const row = rows[0];
|
|
27679
|
+
if (!isSqlModeRow(row)) {
|
|
27680
|
+
throw new Error("MySQL did not return a session sql_mode");
|
|
27681
|
+
}
|
|
27682
|
+
return row.sqlMode;
|
|
27683
|
+
}
|
|
27684
|
+
function isSqlModeRow(row) {
|
|
27685
|
+
return typeof row === "object" && row !== null && "sqlMode" in row && typeof row.sqlMode === "string";
|
|
27575
27686
|
}
|
|
27576
27687
|
|
|
27577
27688
|
// lambda/engine.postgresql.ts
|
|
@@ -27942,6 +28053,30 @@ var handler = async (event) => {
|
|
|
27942
28053
|
engine = secretValues.engine || "postgresql";
|
|
27943
28054
|
}
|
|
27944
28055
|
const dbEngine = EngineFactory.createEngine(engine);
|
|
28056
|
+
let database;
|
|
28057
|
+
if (engine === "dsql") {
|
|
28058
|
+
database = "postgres";
|
|
28059
|
+
} else if (resource === "role" /* ROLE */) {
|
|
28060
|
+
database = secretValues.dbname;
|
|
28061
|
+
} else {
|
|
28062
|
+
database = databaseName ?? secretValues.dbname;
|
|
28063
|
+
}
|
|
28064
|
+
const config = {
|
|
28065
|
+
host: secretValues.host,
|
|
28066
|
+
port: secretValues.port,
|
|
28067
|
+
user: secretValues.username,
|
|
28068
|
+
password: secretValues.password || "",
|
|
28069
|
+
// DSQL will generate token in engine
|
|
28070
|
+
database
|
|
28071
|
+
};
|
|
28072
|
+
if (resource !== "parameter_password" /* PARAMETER_PASSWORD */) {
|
|
28073
|
+
try {
|
|
28074
|
+
await dbEngine.prepare(config);
|
|
28075
|
+
} catch (error) {
|
|
28076
|
+
log("Error preparing database connection: %o", error);
|
|
28077
|
+
throw error;
|
|
28078
|
+
}
|
|
28079
|
+
}
|
|
27945
28080
|
let sql;
|
|
27946
28081
|
switch (requestType) {
|
|
27947
28082
|
case "Create": {
|
|
@@ -28050,22 +28185,6 @@ var handler = async (event) => {
|
|
|
28050
28185
|
}
|
|
28051
28186
|
}
|
|
28052
28187
|
if (sql) {
|
|
28053
|
-
let database;
|
|
28054
|
-
if (engine === "dsql") {
|
|
28055
|
-
database = "postgres";
|
|
28056
|
-
} else if (resource === "role" /* ROLE */) {
|
|
28057
|
-
database = secretValues.dbname;
|
|
28058
|
-
} else {
|
|
28059
|
-
database = databaseName ?? secretValues.dbname;
|
|
28060
|
-
}
|
|
28061
|
-
const config = {
|
|
28062
|
-
host: secretValues.host,
|
|
28063
|
-
port: secretValues.port,
|
|
28064
|
-
user: secretValues.username,
|
|
28065
|
-
password: secretValues.password || "",
|
|
28066
|
-
// DSQL will generate token in engine
|
|
28067
|
-
database
|
|
28068
|
-
};
|
|
28069
28188
|
try {
|
|
28070
28189
|
await (0, import_exponential_backoff.backOff)(
|
|
28071
28190
|
async () => {
|
package/lib/iam-grant.js
CHANGED
|
@@ -6,7 +6,7 @@ const aws_cdk_lib_1 = require("aws-cdk-lib");
|
|
|
6
6
|
const enum_1 = require("./enum");
|
|
7
7
|
const provider_1 = require("./provider");
|
|
8
8
|
class IamGrant extends aws_cdk_lib_1.CustomResource {
|
|
9
|
-
static [JSII_RTTI_SYMBOL_1] = { fqn: "cdk-rds-sql.IamGrant", version: "9.1.
|
|
9
|
+
static [JSII_RTTI_SYMBOL_1] = { fqn: "cdk-rds-sql.IamGrant", version: "9.1.2" };
|
|
10
10
|
constructor(scope, id, props) {
|
|
11
11
|
// IAM grants are only supported on DSQL
|
|
12
12
|
if (props.provider.engine !== provider_1.DatabaseEngine.DSQL) {
|
package/lib/provider.js
CHANGED
|
@@ -60,7 +60,7 @@ var DatabaseEngine;
|
|
|
60
60
|
DatabaseEngine["DSQL"] = "dsql";
|
|
61
61
|
})(DatabaseEngine || (exports.DatabaseEngine = DatabaseEngine = {}));
|
|
62
62
|
class Provider extends constructs_1.Construct {
|
|
63
|
-
static [JSII_RTTI_SYMBOL_1] = { fqn: "cdk-rds-sql.Provider", version: "9.1.
|
|
63
|
+
static [JSII_RTTI_SYMBOL_1] = { fqn: "cdk-rds-sql.Provider", version: "9.1.2" };
|
|
64
64
|
/**
|
|
65
65
|
* Import an existing provider Lambda function
|
|
66
66
|
*/
|
package/lib/role.js
CHANGED
|
@@ -83,7 +83,7 @@ class Parameters extends constructs_1.Construct {
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
class Role extends constructs_1.Construct {
|
|
86
|
-
static [JSII_RTTI_SYMBOL_1] = { fqn: "cdk-rds-sql.Role", version: "9.1.
|
|
86
|
+
static [JSII_RTTI_SYMBOL_1] = { fqn: "cdk-rds-sql.Role", version: "9.1.2" };
|
|
87
87
|
/**
|
|
88
88
|
* The role name.
|
|
89
89
|
*/
|
package/lib/schema.js
CHANGED
|
@@ -5,7 +5,7 @@ const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
|
|
|
5
5
|
const aws_cdk_lib_1 = require("aws-cdk-lib");
|
|
6
6
|
const enum_1 = require("./enum");
|
|
7
7
|
class Schema extends aws_cdk_lib_1.CustomResource {
|
|
8
|
-
static [JSII_RTTI_SYMBOL_1] = { fqn: "cdk-rds-sql.Schema", version: "9.1.
|
|
8
|
+
static [JSII_RTTI_SYMBOL_1] = { fqn: "cdk-rds-sql.Schema", version: "9.1.2" };
|
|
9
9
|
schemaName;
|
|
10
10
|
constructor(scope, id, props) {
|
|
11
11
|
super(scope, id, {
|
package/lib/sql.js
CHANGED
|
@@ -5,7 +5,7 @@ const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
|
|
|
5
5
|
const aws_cdk_lib_1 = require("aws-cdk-lib");
|
|
6
6
|
const enum_1 = require("./enum");
|
|
7
7
|
class Sql extends aws_cdk_lib_1.CustomResource {
|
|
8
|
-
static [JSII_RTTI_SYMBOL_1] = { fqn: "cdk-rds-sql.Sql", version: "9.1.
|
|
8
|
+
static [JSII_RTTI_SYMBOL_1] = { fqn: "cdk-rds-sql.Sql", version: "9.1.2" };
|
|
9
9
|
constructor(scope, id, props) {
|
|
10
10
|
super(scope, id, {
|
|
11
11
|
serviceToken: props.provider.serviceToken,
|