prelude-cli-beta 1446__py3-none-any.whl

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.
@@ -0,0 +1,368 @@
1
+ import click
2
+
3
+ from prelude_cli_beta.views.shared import Spinner, pretty_print
4
+ from prelude_sdk_beta.controllers.iam_controller import (
5
+ IAMAccountController,
6
+ IAMUserController,
7
+ )
8
+ from prelude_sdk_beta.models.codes import Mode, Permission
9
+
10
+
11
+ @click.group()
12
+ @click.pass_context
13
+ def iam(ctx):
14
+ """Prelude account management"""
15
+ ctx.obj = IAMAccountController(account=ctx.obj)
16
+
17
+
18
+ @iam.command("account")
19
+ @click.pass_obj
20
+ @pretty_print
21
+ def describe_account(controller):
22
+ """Get account details"""
23
+ with Spinner(description="Fetching account details"):
24
+ return controller.get_account()
25
+
26
+
27
+ @iam.command("purge-account")
28
+ @click.confirmation_option(prompt="Are you sure?")
29
+ @click.pass_obj
30
+ @pretty_print
31
+ def purge_account(controller):
32
+ """Delete your account"""
33
+ with Spinner(description="Purging account from existence"):
34
+ return controller.purge_account()
35
+
36
+
37
+ @iam.command("update-account")
38
+ @click.option(
39
+ "-m",
40
+ "--mode",
41
+ help="provide a mode",
42
+ default=None,
43
+ show_default=False,
44
+ type=click.Choice([m.name for m in Mode], case_sensitive=False),
45
+ )
46
+ @click.option(
47
+ "-c",
48
+ "--company",
49
+ help="provide your associated company",
50
+ default=None,
51
+ show_default=False,
52
+ type=str,
53
+ )
54
+ @click.option(
55
+ "-s",
56
+ "--slug",
57
+ help="provide a unique human-readable identifier for your account",
58
+ default=None,
59
+ show_default=False,
60
+ type=str,
61
+ )
62
+ @click.pass_obj
63
+ @pretty_print
64
+ def update_account(controller, mode, company, slug):
65
+ """Update an account"""
66
+ with Spinner(description="Updating account information"):
67
+ return controller.update_account(
68
+ mode=Mode[mode] if mode else None, company=company, slug=slug
69
+ )
70
+
71
+
72
+ @iam.command("attach-oidc")
73
+ @click.argument(
74
+ "issuer", type=click.Choice(["google", "azure", "okta"], case_sensitive=False)
75
+ )
76
+ @click.option("--client_id", required=True, help="Client ID")
77
+ @click.option("--client_secret", required=True, help="Client secret")
78
+ @click.option("--oidc_config_url", required=True, help="Configuration endpoint")
79
+ @click.pass_obj
80
+ @pretty_print
81
+ def attach_oidc(controller, issuer, client_id, client_secret, oidc_config_url):
82
+ """Attach OIDC to an account"""
83
+ with Spinner(description="Attaching OIDC"):
84
+ return controller.attach_oidc(
85
+ client_id=client_id,
86
+ client_secret=client_secret,
87
+ issuer=issuer,
88
+ oidc_url=oidc_config_url,
89
+ )
90
+
91
+
92
+ @iam.command("detach-oidc")
93
+ @click.confirmation_option(prompt="Are you sure?")
94
+ @click.pass_obj
95
+ @pretty_print
96
+ def detach_oidc(controller):
97
+ """Detach OIDC to an account"""
98
+ with Spinner(description="Detaching OIDC"):
99
+ return controller.detach_oidc()
100
+
101
+
102
+ @iam.command("invite-user")
103
+ @click.option(
104
+ "-p",
105
+ "--permission",
106
+ help="user permission level",
107
+ default=Permission.EXECUTIVE.name,
108
+ type=click.Choice(
109
+ [
110
+ p.name
111
+ for p in Permission
112
+ if p not in [Permission.INVALID, Permission.SERVICE, Permission.SUPPORT]
113
+ ],
114
+ case_sensitive=False,
115
+ ),
116
+ show_default=True,
117
+ )
118
+ @click.option(
119
+ "-n", "--name", help="name of user", default=None, show_default=False, type=str
120
+ )
121
+ @click.option(
122
+ "-o",
123
+ "--oidc",
124
+ help="OIDC app name, or 'none' to login via a password",
125
+ required=True,
126
+ type=str,
127
+ )
128
+ @click.argument("email")
129
+ @click.pass_obj
130
+ @pretty_print
131
+ def invite_user(controller, permission, name, oidc, email):
132
+ """Invite a new user to your account"""
133
+ with Spinner(description="Inviting new user"):
134
+ data = controller.invite_user(
135
+ email=email,
136
+ oidc="" if oidc.lower() == "none" else oidc,
137
+ permission=Permission[permission],
138
+ name=name,
139
+ )
140
+ msg = "New non-oidc users must check their email to get their temporary password"
141
+ return data, msg
142
+
143
+
144
+ @iam.command("create-service-user")
145
+ @click.argument("name")
146
+ @click.pass_obj
147
+ @pretty_print
148
+ def create_service_user(controller, name):
149
+ """Create a new service user in your account"""
150
+ with Spinner(description="Creating new service user"):
151
+ return controller.create_service_user(name=name)
152
+
153
+
154
+ @iam.command("delete-service-user")
155
+ @click.argument("handle")
156
+ @click.pass_obj
157
+ @pretty_print
158
+ def delete_service_user(controller, handle):
159
+ """Delete a service user from all accounts"""
160
+ with Spinner(description="Deleting service user"):
161
+ return controller.delete_service_user(handle=handle)
162
+
163
+
164
+ @iam.command("update-account-user")
165
+ @click.option(
166
+ "-p",
167
+ "--permission",
168
+ help="user permission level",
169
+ default=Permission.EXECUTIVE.name,
170
+ type=click.Choice(
171
+ [
172
+ p.name
173
+ for p in Permission
174
+ if p not in [Permission.INVALID, Permission.SERVICE, Permission.SUPPORT]
175
+ ],
176
+ case_sensitive=False,
177
+ ),
178
+ show_default=True,
179
+ )
180
+ @click.option(
181
+ "-o",
182
+ "--oidc",
183
+ help="OIDC app name, or 'none' for non-OIDC users",
184
+ required=True,
185
+ type=str,
186
+ )
187
+ @click.argument("email")
188
+ @click.pass_obj
189
+ @pretty_print
190
+ def update_account_user(controller, permission, oidc, email):
191
+ """Update a user in your account"""
192
+ with Spinner(description="Updating account user"):
193
+ return controller.update_account_user(
194
+ email=email,
195
+ oidc="" if oidc.lower() == "none" else oidc,
196
+ permission=Permission[permission],
197
+ )
198
+
199
+
200
+ @iam.command("remove-user")
201
+ @click.confirmation_option(prompt="Are you sure?")
202
+ @click.option(
203
+ "-o",
204
+ "--oidc",
205
+ help="OIDC app name, or 'none' for non-OIDC users",
206
+ required=True,
207
+ type=str,
208
+ )
209
+ @click.argument("email")
210
+ @click.pass_obj
211
+ @pretty_print
212
+ def remove_user(controller, oidc, email):
213
+ """Remove a user from your account"""
214
+ with Spinner(description="Removing user"):
215
+ return controller.remove_user(
216
+ email=email, oidc="" if oidc.lower() == "none" else oidc
217
+ )
218
+
219
+
220
+ @iam.command("logs")
221
+ @click.option(
222
+ "-d", "--days", help="days back to search from today", default=7, type=int
223
+ )
224
+ @click.option(
225
+ "-l", "--limit", help="limit the number of results", default=1000, type=int
226
+ )
227
+ @click.pass_obj
228
+ @pretty_print
229
+ def logs(controller, days, limit):
230
+ """Get audit logs"""
231
+ with Spinner(description="Fetching logs"):
232
+ return controller.audit_logs(days=days, limit=limit)
233
+
234
+
235
+ @iam.command("sign-up", hidden=True)
236
+ @click.option("-c", "--company", type=str, required=True)
237
+ @click.option("-n", "--name", type=str, required=True)
238
+ @click.argument("email", type=str, required=True)
239
+ @click.pass_obj
240
+ @pretty_print
241
+ def sign_up(controller, company, name, email):
242
+ """(NOT AVAIABLE IN PRODUCTION) Create a new user and account"""
243
+ with Spinner(description="Creating new user and account"):
244
+ return controller.sign_up(company=company, email=email, name=name)
245
+
246
+
247
+ @click.group()
248
+ @click.pass_context
249
+ def user(ctx):
250
+ """Prelude user management"""
251
+ ctx.obj = IAMUserController(account=ctx.obj.account)
252
+
253
+
254
+ @user.command("accounts")
255
+ @click.pass_obj
256
+ @pretty_print
257
+ def list_accounts(controller):
258
+ """List all accounts for your user"""
259
+ with Spinner(description="Fetching all accounts for your user"):
260
+ return controller.list_accounts()
261
+
262
+
263
+ @user.command("purge-user")
264
+ @click.confirmation_option(prompt="Are you sure?")
265
+ @click.pass_obj
266
+ @pretty_print
267
+ def purge_user(controller):
268
+ """Remove your user from all accounts and purge user data"""
269
+ with Spinner(description="Purging user"):
270
+ return controller.purge_user()
271
+
272
+
273
+ @user.command("update-user")
274
+ @click.option("-n", "--name", help="name of user", type=str)
275
+ @click.pass_obj
276
+ @pretty_print
277
+ def update_user(controller, name):
278
+ """Update your user information"""
279
+ with Spinner(description="Updating user"):
280
+ return controller.update_user(name=name)
281
+
282
+
283
+ @user.command("forgot-password")
284
+ @click.pass_obj
285
+ @pretty_print
286
+ def forgot_password(controller):
287
+ """Send a password reset email"""
288
+ with Spinner(description="Sending password reset email"):
289
+ return (
290
+ controller.forgot_password(),
291
+ "Please check your email for a confirmation code",
292
+ )
293
+
294
+
295
+ @user.command("confirm-forgot-password")
296
+ @click.option("-c", "--code", help="confirmation code", required=True)
297
+ @click.option(
298
+ "-n",
299
+ "--new_password",
300
+ help="new password",
301
+ required=True,
302
+ hide_input=True,
303
+ prompt=True,
304
+ )
305
+ @click.option(
306
+ "-r",
307
+ "--confirm_new_password",
308
+ help="confirm new password",
309
+ required=True,
310
+ hide_input=True,
311
+ prompt=True,
312
+ )
313
+ @click.pass_obj
314
+ @pretty_print
315
+ def confirm_forgot_password(controller, code, new_password, confirm_new_password):
316
+ """Change your password using a confirmation code"""
317
+ if new_password != confirm_new_password:
318
+ raise ValueError("New password and confirmation do not match")
319
+ with Spinner(description="Changing password"):
320
+ return (
321
+ controller.confirm_forgot_password(
322
+ confirmation_code=code, new_password=new_password
323
+ ),
324
+ "Password changed successfully",
325
+ )
326
+
327
+
328
+ @user.command("change-password")
329
+ @click.option(
330
+ "-c",
331
+ "--current_password",
332
+ help="current password",
333
+ required=True,
334
+ hide_input=True,
335
+ prompt=True,
336
+ )
337
+ @click.option(
338
+ "-n",
339
+ "--new_password",
340
+ help="new password",
341
+ required=True,
342
+ hide_input=True,
343
+ prompt=True,
344
+ )
345
+ @click.option(
346
+ "-r",
347
+ "--confirm_new_password",
348
+ help="confirm new password",
349
+ required=True,
350
+ hide_input=True,
351
+ prompt=True,
352
+ )
353
+ @click.pass_obj
354
+ @pretty_print
355
+ def change_password(controller, current_password, new_password, confirm_new_password):
356
+ """Change your password"""
357
+ if new_password != confirm_new_password:
358
+ raise ValueError("New password and confirmation do not match")
359
+ with Spinner(description="Changing password"):
360
+ return (
361
+ controller.change_password(
362
+ current_password=current_password, new_password=new_password
363
+ ),
364
+ "Password changed successfully",
365
+ )
366
+
367
+
368
+ iam.add_command(user)
@@ -0,0 +1,50 @@
1
+ import click
2
+
3
+ from prelude_cli_beta.views.shared import Spinner, pretty_print
4
+ from prelude_sdk_beta.controllers.jobs_controller import JobsController
5
+ from prelude_sdk_beta.models.codes import BackgroundJobTypes
6
+
7
+
8
+ @click.group()
9
+ @click.pass_context
10
+ def jobs(ctx):
11
+ """Jobs system commands"""
12
+ ctx.obj = JobsController(account=ctx.obj)
13
+
14
+
15
+ @jobs.command("background-jobs")
16
+ @click.option(
17
+ "-t",
18
+ "--job_type",
19
+ help="filter by job type",
20
+ type=click.Choice(
21
+ [t.name for t in BackgroundJobTypes if t != BackgroundJobTypes.INVALID],
22
+ case_sensitive=False,
23
+ ),
24
+ )
25
+ @click.option(
26
+ "-l", "--limit", help="limit the number of jobs returned (per job type)", type=int
27
+ )
28
+ @click.pass_obj
29
+ @pretty_print
30
+ def background_jobs(controller, job_type, limit):
31
+ """List background jobs"""
32
+ with Spinner(description="Fetching background job statuses"):
33
+ result = controller.job_statuses()
34
+ if job_type:
35
+ result = {job_type: result[job_type]}
36
+ if limit:
37
+ result = {k: v[:limit] for k, v in result.items()}
38
+ return result
39
+
40
+
41
+ @jobs.command("background-job")
42
+ @click.option(
43
+ "-j", "--job_id", required=True, help="background job ID to retrieve status for"
44
+ )
45
+ @click.pass_obj
46
+ @pretty_print
47
+ def job_status(controller, job_id):
48
+ """Get status of a given background job"""
49
+ with Spinner(description="Fetching background job status"):
50
+ return controller.job_status(job_id=job_id)
@@ -0,0 +1,181 @@
1
+ import click
2
+
3
+ from prelude_cli_beta.views.shared import Spinner, pretty_print
4
+ from prelude_sdk_beta.controllers.partner_controller import PartnerController
5
+ from prelude_sdk_beta.models.codes import Control
6
+
7
+
8
+ @click.group()
9
+ @click.pass_context
10
+ def partner(ctx):
11
+ """Partner system commands"""
12
+ ctx.obj = PartnerController(account=ctx.obj)
13
+
14
+
15
+ @partner.command("attach")
16
+ @click.argument(
17
+ "partner",
18
+ type=click.Choice(
19
+ [c.name for c in Control if c != Control.INVALID], case_sensitive=False
20
+ ),
21
+ )
22
+ @click.option("--instance_id", help="instance ID of the partner")
23
+ @click.option("--name", help="Friendly name of the partner instance")
24
+ @click.option("--api", help="API endpoint of the partner")
25
+ @click.option("--user", help="user identifier")
26
+ @click.option("--secret", help="secret for OAUTH use cases")
27
+ @click.pass_obj
28
+ @pretty_print
29
+ def attach_partner(controller, partner, instance_id, name, api, user, secret):
30
+ """Attach an EDR to Detect"""
31
+ with Spinner(description="Attaching partner"):
32
+ return controller.attach(
33
+ partner=Control[partner],
34
+ api=api,
35
+ user=user,
36
+ secret=secret,
37
+ name=name,
38
+ instance_id=instance_id,
39
+ )
40
+
41
+
42
+ @partner.command("detach")
43
+ @click.confirmation_option(prompt="Are you sure?")
44
+ @click.argument(
45
+ "partner",
46
+ type=click.Choice(
47
+ [c.name for c in Control if c != Control.INVALID], case_sensitive=False
48
+ ),
49
+ )
50
+ @click.option("--instance_id", required=True, help="instance ID of the partner")
51
+ @click.pass_obj
52
+ @pretty_print
53
+ def detach_partner(controller, partner, instance_id):
54
+ """Detach an existing partner from your account"""
55
+ with Spinner(description="Detaching partner"):
56
+ return controller.detach(partner=Control[partner], instance_id=instance_id)
57
+
58
+
59
+ @partner.command("block")
60
+ @click.argument(
61
+ "partner",
62
+ type=click.Choice(
63
+ [c.name for c in Control if c != Control.INVALID], case_sensitive=False
64
+ ),
65
+ )
66
+ @click.option("-t", "--test_id", required=True, help="a test to block")
67
+ @click.pass_obj
68
+ @pretty_print
69
+ def partner_block(controller, partner, test_id):
70
+ """Report to a partner to block a test"""
71
+ with Spinner(description="Reporting test to partner"):
72
+ return controller.block(partner=Control[partner], test_id=test_id)
73
+
74
+
75
+ @partner.command("endpoints")
76
+ @click.argument(
77
+ "partner",
78
+ type=click.Choice(
79
+ [c.name for c in Control if c != Control.INVALID], case_sensitive=False
80
+ ),
81
+ )
82
+ @click.option(
83
+ "--platform",
84
+ help='platform name (e.g. "windows")',
85
+ required=True,
86
+ type=click.Choice(["windows", "linux", "darwin"], case_sensitive=False),
87
+ )
88
+ @click.option(
89
+ "--hostname", default="", help='hostname pattern (e.g. "mycompany-c24oi444")'
90
+ )
91
+ @click.option("--offset", default=0, help="API pagination offset", type=int)
92
+ @click.option("--limit", default=100, help="API pagination limit", type=int)
93
+ @click.pass_obj
94
+ @pretty_print
95
+ def partner_endpoints(controller, partner, platform, hostname, offset, limit):
96
+ """Get a list of endpoints from a partner"""
97
+ with Spinner(description="Fetching endpoints from partner"):
98
+ return controller.endpoints(
99
+ partner=Control[partner],
100
+ platform=platform,
101
+ hostname=hostname,
102
+ offset=offset,
103
+ count=limit,
104
+ )
105
+
106
+
107
+ @partner.command("deploy")
108
+ @click.confirmation_option(prompt="Are you sure?")
109
+ @click.argument(
110
+ "partner", type=click.Choice([Control.CROWDSTRIKE.name], case_sensitive=False)
111
+ )
112
+ @click.option(
113
+ "--host_ids",
114
+ required=True,
115
+ help="a list of host IDs to deploy to",
116
+ multiple=True,
117
+ default=[],
118
+ )
119
+ @click.pass_obj
120
+ @pretty_print
121
+ def partner_deploy(controller, partner, host_ids):
122
+ """Deploy probes to hosts associated to a partner"""
123
+ with Spinner(description="Deploying probes to hosts"):
124
+ return controller.deploy(partner=Control[partner], host_ids=host_ids)
125
+
126
+
127
+ @partner.command("reports")
128
+ @click.argument(
129
+ "partner", type=click.Choice([Control.CROWDSTRIKE.name], case_sensitive=False)
130
+ )
131
+ @click.option("-t", "--test_id", help="test to get reports for")
132
+ @click.pass_obj
133
+ @pretty_print
134
+ def partner_reports(controller, partner, test_id):
135
+ """Get reports to a partner for a test"""
136
+ with Spinner(description="Getting reports to partner"):
137
+ return controller.list_reports(partner=Control[partner], test_id=test_id)
138
+
139
+ @partner.command("observed-detected")
140
+ @click.option("-t", "--test_id", help="test to get observed/detected stats for")
141
+ @click.option("-h", "--hours", help="number of hours to look back", type=int)
142
+ @click.pass_obj
143
+ @pretty_print
144
+ def observed_detected(controller, test_id, hours):
145
+ """Get observed / detected stats"""
146
+ with Spinner(description="Getting observed / detected stats"):
147
+ return controller.observed_detected(test_id=test_id, hours=hours)
148
+
149
+
150
+ @partner.command("advisories")
151
+ @click.argument(
152
+ "partner", type=click.Choice([Control.CROWDSTRIKE.name], case_sensitive=False)
153
+ )
154
+ @click.option("-s", "--start", help="start date for advisories")
155
+ @click.option("-o", "--offset", help="API pagination offset", type=int)
156
+ @click.option("-l", "--limit", help="API pagination limit", type=int)
157
+ @click.pass_obj
158
+ @pretty_print
159
+ def partner_advisories(controller, partner, start, offset, limit):
160
+ """Get advisories provided by partner"""
161
+ with Spinner(description="Getting partner advisories"):
162
+ return controller.list_advisories(
163
+ partner=Control[partner], start=start, offset=offset, limit=limit
164
+ )
165
+
166
+ @partner.command("groups")
167
+ @click.argument(
168
+ "partner",
169
+ type=click.Choice(
170
+ [c.name for c in Control if c != Control.INVALID], case_sensitive=False
171
+ ),
172
+ )
173
+ @click.option("--instance_id", required=True, help="instance ID of the partner")
174
+ @click.pass_obj
175
+ @pretty_print
176
+ def partner_groups(controller, partner, instance_id):
177
+ """Get a list of groups from a partner"""
178
+ with Spinner(description="Fetching groups from partner"):
179
+ return controller.partner_groups(
180
+ partner=Control[partner], instance_id=instance_id
181
+ )