Skip to content

Commit 319e060

Browse files
author
serverpod_cloud
committed
feat(cli): 748b4840ed3e8c33d099f165b2907aba02f76dfe
1 parent 6188384 commit 319e060

6 files changed

Lines changed: 1322 additions & 183 deletions

File tree

serverpod_cloud_cli/lib/command_runner/cloud_cli_command_runner.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import 'package:serverpod_cloud_cli/command_runner/commands/launch_command.dart'
1616
import 'package:serverpod_cloud_cli/command_runner/commands/log_command.dart';
1717
import 'package:serverpod_cloud_cli/command_runner/commands/project_command.dart';
1818
import 'package:serverpod_cloud_cli/command_runner/commands/secret_command.dart';
19+
import 'package:serverpod_cloud_cli/command_runner/commands/password_command.dart';
1920
import 'package:serverpod_cloud_cli/command_runner/commands/deployments_command.dart';
2021
import 'package:serverpod_cloud_cli/command_runner/commands/version_command.dart';
2122
import 'package:serverpod_cloud_cli/command_runner/commands/me_command.dart';
@@ -154,6 +155,7 @@ class CloudCliCommandRunner extends BetterCommandRunner<GlobalOption, void> {
154155
CloudLogCommand(logger: logger),
155156
CloudDeploymentsCommand(logger: logger),
156157
CloudSecretCommand(logger: logger),
158+
CloudPasswordCommand(logger: logger),
157159
CloudDbCommand(logger: logger),
158160
CloudLaunchCommand(logger: logger),
159161
CliUserSettingsCommand(logger: logger),
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
import 'package:config/config.dart';
2+
import 'package:serverpod_cloud_cli/command_runner/cloud_cli_command.dart';
3+
import 'package:serverpod_cloud_cli/shared/exceptions/exit_exceptions.dart';
4+
import 'package:serverpod_cloud_cli/command_runner/helpers/command_options.dart';
5+
import 'package:serverpod_cloud_cli/commands/password/password.dart';
6+
7+
import 'categories.dart';
8+
9+
class CloudPasswordCommand extends CloudCliCommand {
10+
@override
11+
final name = 'password';
12+
13+
@override
14+
final description = '''Manage Serverpod Cloud passwords.
15+
16+
The passwords are automatically prefixed with SERVERPOD_PASSWORD_ and will be injected as environment variables.
17+
Passwords defined by this command can be accessed with the getPassword function.
18+
19+
If you need to set a secret without the SERVERPOD_PASSWORD_ prefix, you can do so by using the secret create command.
20+
''';
21+
22+
@override
23+
String get category => CommandCategories.control;
24+
25+
CloudPasswordCommand({required super.logger}) {
26+
addSubcommand(CloudPasswordListCommand(logger: logger));
27+
addSubcommand(CloudPasswordSetCommand(logger: logger));
28+
addSubcommand(CloudPasswordUnsetCommand(logger: logger));
29+
}
30+
}
31+
32+
abstract final class PasswordCommandConfig {
33+
static const projectId = ProjectIdOption();
34+
static const name = NameOption(
35+
argPos: 0,
36+
helpText:
37+
'The name of the password (without SERVERPOD_PASSWORD_ prefix). '
38+
'Can be passed as the first argument.',
39+
);
40+
static const value = ValueOption(
41+
argPos: 1,
42+
helpText:
43+
'The value of the password. Can be passed as the second argument.',
44+
);
45+
static const valueFile = ValueFileOption(
46+
helpText: 'The name of the file with the password value.',
47+
);
48+
}
49+
50+
enum PasswordListCommandConfig<V> implements OptionDefinition<V> {
51+
projectId(PasswordCommandConfig.projectId);
52+
53+
const PasswordListCommandConfig(this.option);
54+
55+
@override
56+
final ConfigOptionBase<V> option;
57+
}
58+
59+
class CloudPasswordListCommand
60+
extends CloudCliCommand<PasswordListCommandConfig> {
61+
@override
62+
String get description =>
63+
'''List all passwords, both user-set and platform-managed.
64+
65+
Passwords are grouped by category:
66+
- Custom: User-defined passwords that are not part of the platform.
67+
- Services: Passwords for services like databases, insights, etc.
68+
- Auth: Passwords for authentication like JWT, email, for package serverpod_auth_idp_server.
69+
- Legacy Auth: Passwords for the legacy authentication module.
70+
''';
71+
72+
@override
73+
String get name => 'list';
74+
75+
CloudPasswordListCommand({required super.logger})
76+
: super(options: PasswordListCommandConfig.values);
77+
78+
@override
79+
Future<void> runWithConfig(
80+
final Configuration<PasswordListCommandConfig> commandConfig,
81+
) async {
82+
final projectId = commandConfig.value(PasswordListCommandConfig.projectId);
83+
84+
await PasswordCommands.listPasswords(
85+
runner.serviceProvider.cloudApiClient,
86+
logger: logger,
87+
projectId: projectId,
88+
);
89+
}
90+
}
91+
92+
enum PasswordSetCommandConfig<V> implements OptionDefinition<V> {
93+
projectId(PasswordCommandConfig.projectId),
94+
name(PasswordCommandConfig.name),
95+
value(PasswordCommandConfig.value),
96+
valueFile(PasswordCommandConfig.valueFile);
97+
98+
const PasswordSetCommandConfig(this.option);
99+
100+
@override
101+
final ConfigOptionBase<V> option;
102+
}
103+
104+
class CloudPasswordSetCommand
105+
extends CloudCliCommand<PasswordSetCommandConfig> {
106+
@override
107+
String get description => '''Set a password.
108+
109+
Setting a platform-managed password will override the existing password.
110+
The original password will not be lost and can be activated again by unsetting the password.
111+
''';
112+
113+
@override
114+
String get name => 'set';
115+
116+
CloudPasswordSetCommand({required super.logger})
117+
: super(options: PasswordSetCommandConfig.values);
118+
119+
@override
120+
Future<void> runWithConfig(
121+
final Configuration<PasswordSetCommandConfig> commandConfig,
122+
) async {
123+
final projectId = commandConfig.value(PasswordSetCommandConfig.projectId);
124+
final name = commandConfig.value(PasswordSetCommandConfig.name);
125+
final value = commandConfig.optionalValue(PasswordSetCommandConfig.value);
126+
final valueFile = commandConfig.optionalValue(
127+
PasswordSetCommandConfig.valueFile,
128+
);
129+
130+
String valueToSet;
131+
if (value != null) {
132+
valueToSet = value;
133+
} else if (valueFile != null) {
134+
valueToSet = valueFile.readAsStringSync();
135+
} else {
136+
throw ErrorExitException(
137+
'Either a value or --from-file must be provided.',
138+
);
139+
}
140+
141+
await PasswordCommands.setPassword(
142+
runner.serviceProvider.cloudApiClient,
143+
logger: logger,
144+
projectId: projectId,
145+
name: name,
146+
value: valueToSet,
147+
);
148+
}
149+
}
150+
151+
enum PasswordUnsetCommandConfig<V> implements OptionDefinition<V> {
152+
projectId(PasswordCommandConfig.projectId),
153+
name(PasswordCommandConfig.name);
154+
155+
const PasswordUnsetCommandConfig(this.option);
156+
157+
@override
158+
final ConfigOptionBase<V> option;
159+
}
160+
161+
class CloudPasswordUnsetCommand
162+
extends CloudCliCommand<PasswordUnsetCommandConfig> {
163+
@override
164+
String get description =>
165+
'Unset a password, can only unset user-set passwords.';
166+
167+
@override
168+
String get name => 'unset';
169+
170+
CloudPasswordUnsetCommand({required super.logger})
171+
: super(options: PasswordUnsetCommandConfig.values);
172+
173+
@override
174+
Future<void> runWithConfig(
175+
final Configuration<PasswordUnsetCommandConfig> commandConfig,
176+
) async {
177+
final projectId = commandConfig.value(PasswordUnsetCommandConfig.projectId);
178+
final name = commandConfig.value(PasswordUnsetCommandConfig.name);
179+
180+
final shouldDelete = await logger.confirm(
181+
'Are you sure you want to unset the password "$name"?',
182+
defaultValue: false,
183+
);
184+
185+
if (!shouldDelete) {
186+
throw UserAbortException();
187+
}
188+
189+
await PasswordCommands.unsetPassword(
190+
runner.serviceProvider.cloudApiClient,
191+
logger: logger,
192+
projectId: projectId,
193+
name: name,
194+
);
195+
}
196+
}

serverpod_cloud_cli/lib/command_runner/completion/completion_script_carapace.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,28 @@ commands:
238238
-p, --project=!: "The ID of the project.\nCan be omitted for existing projects that are linked. See `scloud project link --help`."
239239
--name=!: "The name of the secret. Can be passed as the first argument."
240240
241+
- name: password
242+
243+
commands:
244+
- name: list
245+
flags:
246+
-p, --project=!: "The ID of the project.\nCan be omitted for existing projects that are linked. See `scloud project link --help`."
247+
248+
- name: set
249+
flags:
250+
-p, --project=!: "The ID of the project.\nCan be omitted for existing projects that are linked. See `scloud project link --help`."
251+
--name=!: "The name of the password (without SERVERPOD_PASSWORD_ prefix). Can be passed as the first argument."
252+
--value=: "The value of the password. Can be passed as the second argument."
253+
--from-file=: "The name of the file with the password value."
254+
completion:
255+
flag:
256+
from-file: ["$files"]
257+
258+
- name: unset
259+
flags:
260+
-p, --project=!: "The ID of the project.\nCan be omitted for existing projects that are linked. See `scloud project link --help`."
261+
--name=!: "The name of the password (without SERVERPOD_PASSWORD_ prefix). Can be passed as the first argument."
262+
241263
- name: db
242264
243265
commands:

0 commit comments

Comments
 (0)