@happyvertical/directory 0.76.2 → 0.77.0

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/AGENT.md CHANGED
@@ -28,7 +28,7 @@ pnpm --filter @happyvertical/directory clean
28
28
  ## Ecosystem Relationships
29
29
  - Provides: Unified directory services with adapter-based architecture (Kanidm, Stalwart, PostgreSQL, AWS)
30
30
  - Implements: none
31
- - Requires: @happyvertical/utils, @aws-sdk/client-iam, @aws-sdk/client-organizations, pg
31
+ - Requires: @happyvertical/utils, @aws-sdk/client-iam, @aws-sdk/client-organizations, @aws-sdk/client-sts, pg
32
32
  - Stability: stable (Primary package surface is described as implemented and production-oriented.)
33
33
  <!-- END AGENT:GENERATED -->
34
34
 
@@ -1,8 +1,9 @@
1
- import { AwsAccessKey, AwsAccount, AwsAccountCreationStatus, AwsDirectoryAdapter, AwsIamUser, AwsOptions, AwsOrganizationalUnit, CreateAwsAccountInput, CreateAwsIamUserInput, CreateAwsOuInput, CreateGroupInput, CreateUserInput, DirectoryGroup, DirectoryUser, UpdateGroupInput, UpdateUserInput } from '../shared/types.js';
1
+ import { AwsAccessKey, AwsAccount, AwsAccountCreationStatus, AwsAccountCreationWaitOptions, AwsAccountParent, AwsAssumeRoleInput, AwsDirectoryAdapter, AwsIamRole, AwsIamUser, AwsOptions, AwsOrganizationalUnit, AwsTemporaryCredentials, CreateAwsAccountInput, CreateAwsIamUserInput, CreateAwsOuInput, CreateGroupInput, CreateUserInput, DirectoryGroup, DirectoryUser, EnsureAwsAccountInput, EnsureAwsIamRoleInput, EnsureAwsOuInput, PutAwsIamRolePolicyInput, UpdateGroupInput, UpdateUserInput } from '../shared/types.js';
2
2
  export declare class AwsAdapter implements AwsDirectoryAdapter {
3
3
  readonly options: AwsOptions;
4
4
  private readonly orgs;
5
5
  private readonly iam;
6
+ private readonly sts;
6
7
  constructor(options: AwsOptions);
7
8
  testConnection(): Promise<boolean>;
8
9
  disconnect(): Promise<void>;
@@ -23,10 +24,22 @@ export declare class AwsAdapter implements AwsDirectoryAdapter {
23
24
  createOrganizationalUnit(input: CreateAwsOuInput): Promise<AwsOrganizationalUnit>;
24
25
  getOrganizationalUnit(id: string): Promise<AwsOrganizationalUnit>;
25
26
  listOrganizationalUnits(parentId: string): Promise<AwsOrganizationalUnit[]>;
27
+ findOrganizationalUnitByName(parentId: string, name: string): Promise<AwsOrganizationalUnit | null>;
28
+ ensureOrganizationalUnit(input: EnsureAwsOuInput): Promise<AwsOrganizationalUnit>;
26
29
  createAccount(input: CreateAwsAccountInput): Promise<AwsAccountCreationStatus>;
27
30
  getAccountCreationStatus(id: string): Promise<AwsAccountCreationStatus>;
28
31
  listAccounts(): Promise<AwsAccount[]>;
32
+ findAccountByEmail(email: string): Promise<AwsAccount | null>;
33
+ ensureAccount(input: EnsureAwsAccountInput): Promise<AwsAccount>;
34
+ waitForAccountCreation(id: string, options?: AwsAccountCreationWaitOptions): Promise<AwsAccountCreationStatus>;
35
+ getAccountParent(accountId: string): Promise<AwsAccountParent | null>;
36
+ ensureAccountInOrganizationalUnit(accountId: string, destinationParentId: string): Promise<void>;
29
37
  moveAccount(accountId: string, sourceParentId: string, destParentId: string): Promise<void>;
38
+ tagAwsOrganizationsResource(resourceId: string, tags: Record<string, string>): Promise<void>;
39
+ assumeAwsRole(input: AwsAssumeRoleInput): Promise<AwsTemporaryCredentials>;
40
+ getIamRole(roleName: string): Promise<AwsIamRole>;
41
+ ensureIamRole(input: EnsureAwsIamRoleInput): Promise<AwsIamRole>;
42
+ putIamRolePolicy(input: PutAwsIamRolePolicyInput): Promise<void>;
30
43
  createIamUser(input: CreateAwsIamUserInput): Promise<AwsIamUser>;
31
44
  getIamUser(username: string): Promise<AwsIamUser>;
32
45
  deleteIamUser(username: string): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"aws.d.ts","sourceRoot":"","sources":["../../src/adapters/aws.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"aws.d.ts","sourceRoot":"","sources":["../../src/adapters/aws.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -10,5 +10,5 @@ export { PostgresAdapter } from './adapters/postgres.js';
10
10
  export { StalwartAdapter } from './adapters/stalwart.js';
11
11
  export { AuthenticationError, ConflictError, ConnectionError, DirectoryError, NotFoundError, RateLimitError, ValidationError, } from './shared/errors.js';
12
12
  export { getAwsAdapter, getDirectoryAdapter, getKanidmAdapter, getPostgresAdapter, getStalwartAdapter, isAwsOptions, isKanidmOptions, isPostgresOptions, isStalwartOptions, } from './shared/factory.js';
13
- export type { AwsAccessKey, AwsAccount, AwsAccountCreationStatus, AwsDirectoryAdapter, AwsIamUser, AwsOptions, AwsOrganizationalUnit, CreateAwsAccountInput, CreateAwsIamUserInput, CreateAwsOuInput, CreateDkimKeyInput, CreateGroupInput, CreateMailboxInput, CreateMailDomainInput, CreateOAuth2ClientInput, CreatePgDatabaseInput, CreatePgRoleInput, CreateUserInput, CredentialResetIntent, DirectoryAdapter, DirectoryAdapterType, DirectoryGroup, DirectoryOrchestrator, DirectoryUser, DkimKey, DnsRecord, GetDirectoryAdapterOptions, KanidmDirectoryAdapter, KanidmOptions, Mailbox, MailDomain, OAuth2Client, PgDatabase, PgRole, PostgresDirectoryAdapter, PostgresOptions, StalwartDirectoryAdapter, StalwartOptions, TenantProvisioningInput, TenantProvisioningResult, UpdateGroupInput, UpdateMailboxInput, UpdateOAuth2ClientInput, UpdateUserInput, } from './shared/types.js';
13
+ export type { AwsAccessKey, AwsAccount, AwsAccountCreationStatus, AwsAccountCreationWaitOptions, AwsAccountParent, AwsAssumeRoleInput, AwsDirectoryAdapter, AwsIamRole, AwsIamUser, AwsOptions, AwsOrganizationalUnit, AwsTemporaryCredentials, CreateAwsAccountInput, CreateAwsIamUserInput, CreateAwsOuInput, CreateDkimKeyInput, CreateGroupInput, CreateMailboxInput, CreateMailDomainInput, CreateOAuth2ClientInput, CreatePgDatabaseInput, CreatePgRoleInput, CreateUserInput, CredentialResetIntent, DirectoryAdapter, DirectoryAdapterType, DirectoryGroup, DirectoryOrchestrator, DirectoryUser, DkimKey, DnsRecord, EnsureAwsAccountInput, EnsureAwsIamRoleInput, EnsureAwsOuInput, GetDirectoryAdapterOptions, KanidmDirectoryAdapter, KanidmOptions, Mailbox, MailDomain, OAuth2Client, PgDatabase, PgRole, PostgresDirectoryAdapter, PostgresOptions, PutAwsIamRolePolicyInput, StalwartDirectoryAdapter, StalwartOptions, TenantProvisioningInput, TenantProvisioningResult, UpdateGroupInput, UpdateMailboxInput, UpdateOAuth2ClientInput, UpdateUserInput, } from './shared/types.js';
14
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAGzD,OAAO,EACL,mBAAmB,EACnB,aAAa,EACb,eAAe,EACf,cAAc,EACd,aAAa,EACb,cAAc,EACd,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,EAClB,kBAAkB,EAClB,YAAY,EACZ,eAAe,EACf,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,qBAAqB,CAAC;AAG7B,YAAY,EACV,YAAY,EACZ,UAAU,EACV,wBAAwB,EACxB,mBAAmB,EACnB,UAAU,EACV,UAAU,EACV,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,qBAAqB,EACrB,uBAAuB,EACvB,qBAAqB,EACrB,iBAAiB,EACjB,eAAe,EACf,qBAAqB,EACrB,gBAAgB,EAChB,oBAAoB,EACpB,cAAc,EACd,qBAAqB,EACrB,aAAa,EACb,OAAO,EACP,SAAS,EACT,0BAA0B,EAC1B,sBAAsB,EACtB,aAAa,EACb,OAAO,EACP,UAAU,EACV,YAAY,EACZ,UAAU,EACV,MAAM,EACN,wBAAwB,EACxB,eAAe,EACf,wBAAwB,EACxB,eAAe,EACf,uBAAuB,EACvB,wBAAwB,EACxB,gBAAgB,EAChB,kBAAkB,EAClB,uBAAuB,EACvB,eAAe,GAChB,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAGzD,OAAO,EACL,mBAAmB,EACnB,aAAa,EACb,eAAe,EACf,cAAc,EACd,aAAa,EACb,cAAc,EACd,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,EAClB,kBAAkB,EAClB,YAAY,EACZ,eAAe,EACf,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,qBAAqB,CAAC;AAG7B,YAAY,EACV,YAAY,EACZ,UAAU,EACV,wBAAwB,EACxB,6BAA6B,EAC7B,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,UAAU,EACV,UAAU,EACV,UAAU,EACV,qBAAqB,EACrB,uBAAuB,EACvB,qBAAqB,EACrB,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,qBAAqB,EACrB,uBAAuB,EACvB,qBAAqB,EACrB,iBAAiB,EACjB,eAAe,EACf,qBAAqB,EACrB,gBAAgB,EAChB,oBAAoB,EACpB,cAAc,EACd,qBAAqB,EACrB,aAAa,EACb,OAAO,EACP,SAAS,EACT,qBAAqB,EACrB,qBAAqB,EACrB,gBAAgB,EAChB,0BAA0B,EAC1B,sBAAsB,EACtB,aAAa,EACb,OAAO,EACP,UAAU,EACV,YAAY,EACZ,UAAU,EACV,MAAM,EACN,wBAAwB,EACxB,eAAe,EACf,wBAAwB,EACxB,wBAAwB,EACxB,eAAe,EACf,uBAAuB,EACvB,wBAAwB,EACxB,gBAAgB,EAChB,kBAAkB,EAClB,uBAAuB,EACvB,eAAe,GAChB,MAAM,mBAAmB,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
- import { IAMClient, ListUsersCommand, CreateUserCommand, GetUserCommand, UpdateUserCommand, DeleteUserCommand, CreateGroupCommand, GetGroupCommand, DeleteGroupCommand, ListGroupsCommand, AddUserToGroupCommand, RemoveUserFromGroupCommand, ListGroupsForUserCommand, AttachUserPolicyCommand, DetachUserPolicyCommand, CreateAccessKeyCommand, DeleteAccessKeyCommand } from "@aws-sdk/client-iam";
2
- import { OrganizationsClient, CreateOrganizationalUnitCommand, DescribeOrganizationalUnitCommand, ListOrganizationalUnitsForParentCommand, CreateAccountCommand, DescribeCreateAccountStatusCommand, ListAccountsCommand, MoveAccountCommand } from "@aws-sdk/client-organizations";
1
+ import { IAMClient, ListUsersCommand, CreateUserCommand, GetUserCommand, UpdateUserCommand, DeleteUserCommand, CreateGroupCommand, GetGroupCommand, DeleteGroupCommand, ListGroupsCommand, AddUserToGroupCommand, RemoveUserFromGroupCommand, ListGroupsForUserCommand, GetRoleCommand, UpdateAssumeRolePolicyCommand, UpdateRoleCommand, TagRoleCommand, CreateRoleCommand, PutRolePolicyCommand, AttachUserPolicyCommand, DetachUserPolicyCommand, CreateAccessKeyCommand, DeleteAccessKeyCommand } from "@aws-sdk/client-iam";
2
+ import { OrganizationsClient, CreateOrganizationalUnitCommand, DescribeOrganizationalUnitCommand, ListOrganizationalUnitsForParentCommand, CreateAccountCommand, DescribeCreateAccountStatusCommand, ListAccountsCommand, ListParentsCommand, MoveAccountCommand, TagResourceCommand } from "@aws-sdk/client-organizations";
3
+ import { STSClient, AssumeRoleCommand } from "@aws-sdk/client-sts";
3
4
  import pg from "pg";
4
5
  class DirectoryError extends Error {
5
6
  code;
@@ -73,6 +74,8 @@ class RateLimitError extends DirectoryError {
73
74
  this.retryAfter = retryAfter;
74
75
  }
75
76
  }
77
+ const DEFAULT_ACCOUNT_CREATION_TIMEOUT_MS = 10 * 60 * 1e3;
78
+ const DEFAULT_ACCOUNT_CREATION_POLL_INTERVAL_MS = 5 * 1e3;
76
79
  function handleAwsError(error, context) {
77
80
  if (error instanceof AuthenticationError || error instanceof ConnectionError || error instanceof NotFoundError || error instanceof ConflictError || error instanceof DirectoryError) {
78
81
  throw error;
@@ -137,6 +140,35 @@ function mapIamUserToAwsIamUser(user) {
137
140
  createDate: user.CreateDate
138
141
  };
139
142
  }
143
+ function mapAwsAccount(account) {
144
+ return {
145
+ id: account.Id ?? "",
146
+ name: account.Name ?? "",
147
+ email: account.Email ?? "",
148
+ arn: account.Arn,
149
+ status: account.Status ? String(account.Status) : void 0
150
+ };
151
+ }
152
+ function mapAwsIamRole(role) {
153
+ return {
154
+ roleName: role.RoleName ?? "",
155
+ arn: role.Arn,
156
+ roleId: role.RoleId,
157
+ path: role.Path,
158
+ createDate: role.CreateDate,
159
+ assumeRolePolicyDocument: role.AssumeRolePolicyDocument
160
+ };
161
+ }
162
+ function toAwsTags(tags) {
163
+ const entries = Object.entries(tags ?? {}).filter(([key]) => key.length > 0);
164
+ if (entries.length === 0) {
165
+ return void 0;
166
+ }
167
+ return entries.map(([Key, Value]) => ({ Key, Value }));
168
+ }
169
+ function sleep(ms) {
170
+ return new Promise((resolve) => setTimeout(resolve, ms));
171
+ }
140
172
  class AwsAdapter {
141
173
  constructor(options) {
142
174
  this.options = options;
@@ -146,9 +178,11 @@ class AwsAdapter {
146
178
  };
147
179
  this.orgs = new OrganizationsClient(clientConfig);
148
180
  this.iam = new IAMClient(clientConfig);
181
+ this.sts = new STSClient(clientConfig);
149
182
  }
150
183
  orgs;
151
184
  iam;
185
+ sts;
152
186
  // ==========================================================================
153
187
  // Connection
154
188
  // ==========================================================================
@@ -323,7 +357,8 @@ class AwsAdapter {
323
357
  const result = await this.orgs.send(
324
358
  new CreateOrganizationalUnitCommand({
325
359
  ParentId: input.parentId,
326
- Name: input.name
360
+ Name: input.name,
361
+ Tags: toAwsTags(input.tags)
327
362
  })
328
363
  );
329
364
  const ou = result.OrganizationalUnit;
@@ -356,21 +391,47 @@ class AwsAdapter {
356
391
  }
357
392
  async listOrganizationalUnits(parentId) {
358
393
  try {
359
- const result = await this.orgs.send(
360
- new ListOrganizationalUnitsForParentCommand({
361
- ParentId: parentId
362
- })
363
- );
364
- return (result.OrganizationalUnits ?? []).map((ou) => ({
365
- id: ou.Id ?? "",
366
- name: ou.Name ?? "",
367
- arn: ou.Arn,
368
- parentId
369
- }));
394
+ const organizationalUnits = [];
395
+ let nextToken;
396
+ do {
397
+ const result = await this.orgs.send(
398
+ new ListOrganizationalUnitsForParentCommand({
399
+ ParentId: parentId,
400
+ NextToken: nextToken
401
+ })
402
+ );
403
+ organizationalUnits.push(
404
+ ...(result.OrganizationalUnits ?? []).map((ou) => ({
405
+ id: ou.Id ?? "",
406
+ name: ou.Name ?? "",
407
+ arn: ou.Arn,
408
+ parentId
409
+ }))
410
+ );
411
+ nextToken = result.NextToken;
412
+ } while (nextToken);
413
+ return organizationalUnits;
370
414
  } catch (error) {
371
415
  handleAwsError(error, `listOrganizationalUnits(${parentId})`);
372
416
  }
373
417
  }
418
+ async findOrganizationalUnitByName(parentId, name) {
419
+ const organizationalUnits = await this.listOrganizationalUnits(parentId);
420
+ return organizationalUnits.find((ou) => ou.name === name) ?? null;
421
+ }
422
+ async ensureOrganizationalUnit(input) {
423
+ const existing = await this.findOrganizationalUnitByName(
424
+ input.parentId,
425
+ input.name
426
+ );
427
+ if (existing) {
428
+ if (input.tags) {
429
+ await this.tagAwsOrganizationsResource(existing.id, input.tags);
430
+ }
431
+ return existing;
432
+ }
433
+ return this.createOrganizationalUnit(input);
434
+ }
374
435
  // ==========================================================================
375
436
  // Accounts (AwsDirectoryAdapter)
376
437
  // ==========================================================================
@@ -380,12 +441,20 @@ class AwsAdapter {
380
441
  new CreateAccountCommand({
381
442
  AccountName: input.name,
382
443
  Email: input.email,
383
- ...input.roleName ? { RoleName: input.roleName } : {}
444
+ ...input.roleName ? { RoleName: input.roleName } : {},
445
+ Tags: toAwsTags(input.tags)
384
446
  })
385
447
  );
386
448
  const status = result.CreateAccountStatus;
449
+ if (!status?.Id) {
450
+ throw new DirectoryError(
451
+ `createAccount(${input.name}) did not return a create account request id`,
452
+ "AWS_ACCOUNT_CREATION_REQUEST_ID_MISSING",
453
+ "aws"
454
+ );
455
+ }
387
456
  return {
388
- id: status?.Id ?? "",
457
+ id: status.Id,
389
458
  accountId: status?.AccountId,
390
459
  state: status?.State ?? "IN_PROGRESS",
391
460
  failureReason: status?.FailureReason ? String(status.FailureReason) : void 0
@@ -414,18 +483,107 @@ class AwsAdapter {
414
483
  }
415
484
  async listAccounts() {
416
485
  try {
417
- const result = await this.orgs.send(new ListAccountsCommand({}));
418
- return (result.Accounts ?? []).map((a) => ({
419
- id: a.Id ?? "",
420
- name: a.Name ?? "",
421
- email: a.Email ?? "",
422
- arn: a.Arn,
423
- status: a.Status ? String(a.Status) : void 0
424
- }));
486
+ const accounts = [];
487
+ let nextToken;
488
+ do {
489
+ const result = await this.orgs.send(
490
+ new ListAccountsCommand({ NextToken: nextToken })
491
+ );
492
+ accounts.push(...(result.Accounts ?? []).map((a) => mapAwsAccount(a)));
493
+ nextToken = result.NextToken;
494
+ } while (nextToken);
495
+ return accounts;
425
496
  } catch (error) {
426
497
  handleAwsError(error, "listAccounts");
427
498
  }
428
499
  }
500
+ async findAccountByEmail(email) {
501
+ const accounts = await this.listAccounts();
502
+ return accounts.find(
503
+ (account) => account.email.toLowerCase() === email.toLowerCase()
504
+ ) ?? null;
505
+ }
506
+ async ensureAccount(input) {
507
+ const existing = await this.findAccountByEmail(input.email);
508
+ if (existing) {
509
+ if (input.tags) {
510
+ await this.tagAwsOrganizationsResource(existing.id, input.tags);
511
+ }
512
+ return existing;
513
+ }
514
+ const createStatus = await this.createAccount(input);
515
+ const finalStatus = await this.waitForAccountCreation(
516
+ createStatus.id,
517
+ input.wait
518
+ );
519
+ if (finalStatus.state !== "SUCCEEDED" || !finalStatus.accountId) {
520
+ throw new DirectoryError(
521
+ `ensureAccount(${input.email}) failed: ${finalStatus.failureReason ?? finalStatus.state}`,
522
+ "AWS_ACCOUNT_CREATION_FAILED",
523
+ "aws"
524
+ );
525
+ }
526
+ const created = await this.findAccountByEmail(input.email);
527
+ if (created) {
528
+ return created;
529
+ }
530
+ return {
531
+ id: finalStatus.accountId,
532
+ name: input.name,
533
+ email: input.email,
534
+ status: "ACTIVE"
535
+ };
536
+ }
537
+ async waitForAccountCreation(id, options = {}) {
538
+ const timeoutMs = options.timeoutMs ?? DEFAULT_ACCOUNT_CREATION_TIMEOUT_MS;
539
+ const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_ACCOUNT_CREATION_POLL_INTERVAL_MS;
540
+ const startedAt = Date.now();
541
+ while (true) {
542
+ const status = await this.getAccountCreationStatus(id);
543
+ if (status.state !== "IN_PROGRESS") {
544
+ return status;
545
+ }
546
+ if (Date.now() - startedAt >= timeoutMs) {
547
+ throw new DirectoryError(
548
+ `waitForAccountCreation(${id}) timed out after ${timeoutMs}ms`,
549
+ "AWS_ACCOUNT_CREATION_TIMEOUT",
550
+ "aws"
551
+ );
552
+ }
553
+ await sleep(pollIntervalMs);
554
+ }
555
+ }
556
+ async getAccountParent(accountId) {
557
+ try {
558
+ const result = await this.orgs.send(
559
+ new ListParentsCommand({ ChildId: accountId })
560
+ );
561
+ const parent = result.Parents?.[0];
562
+ if (!parent?.Id) {
563
+ return null;
564
+ }
565
+ return {
566
+ id: parent.Id,
567
+ type: parent.Type ? String(parent.Type) : ""
568
+ };
569
+ } catch (error) {
570
+ handleAwsError(error, `getAccountParent(${accountId})`);
571
+ }
572
+ }
573
+ async ensureAccountInOrganizationalUnit(accountId, destinationParentId) {
574
+ const currentParent = await this.getAccountParent(accountId);
575
+ if (currentParent?.id === destinationParentId) {
576
+ return;
577
+ }
578
+ if (!currentParent?.id) {
579
+ throw new DirectoryError(
580
+ `ensureAccountInOrganizationalUnit(${accountId}) could not determine the current account parent`,
581
+ "AWS_ACCOUNT_PARENT_MISSING",
582
+ "aws"
583
+ );
584
+ }
585
+ await this.moveAccount(accountId, currentParent.id, destinationParentId);
586
+ }
429
587
  async moveAccount(accountId, sourceParentId, destParentId) {
430
588
  try {
431
589
  await this.orgs.send(
@@ -442,6 +600,128 @@ class AwsAdapter {
442
600
  );
443
601
  }
444
602
  }
603
+ async tagAwsOrganizationsResource(resourceId, tags) {
604
+ const awsTags = toAwsTags(tags);
605
+ if (!awsTags) {
606
+ return;
607
+ }
608
+ try {
609
+ await this.orgs.send(
610
+ new TagResourceCommand({
611
+ ResourceId: resourceId,
612
+ Tags: awsTags
613
+ })
614
+ );
615
+ } catch (error) {
616
+ handleAwsError(error, `tagAwsOrganizationsResource(${resourceId})`);
617
+ }
618
+ }
619
+ // ==========================================================================
620
+ // STS (AwsDirectoryAdapter)
621
+ // ==========================================================================
622
+ async assumeAwsRole(input) {
623
+ try {
624
+ const result = await this.sts.send(
625
+ new AssumeRoleCommand({
626
+ RoleArn: input.roleArn,
627
+ RoleSessionName: input.sessionName,
628
+ ...input.externalId ? { ExternalId: input.externalId } : {},
629
+ ...input.durationSeconds ? { DurationSeconds: input.durationSeconds } : {}
630
+ })
631
+ );
632
+ const credentials = result.Credentials;
633
+ if (!credentials?.AccessKeyId || !credentials.SecretAccessKey || !credentials.SessionToken) {
634
+ throw new DirectoryError(
635
+ `assumeAwsRole(${input.roleArn}) returned incomplete credentials`,
636
+ "AWS_ASSUME_ROLE_INCOMPLETE_CREDENTIALS",
637
+ "aws"
638
+ );
639
+ }
640
+ return {
641
+ accessKeyId: credentials.AccessKeyId,
642
+ secretAccessKey: credentials.SecretAccessKey,
643
+ sessionToken: credentials.SessionToken,
644
+ expiration: credentials.Expiration,
645
+ assumedRoleArn: result.AssumedRoleUser?.Arn,
646
+ assumedRoleId: result.AssumedRoleUser?.AssumedRoleId
647
+ };
648
+ } catch (error) {
649
+ handleAwsError(error, `assumeAwsRole(${input.roleArn})`);
650
+ }
651
+ }
652
+ // ==========================================================================
653
+ // IAM Roles (AwsDirectoryAdapter)
654
+ // ==========================================================================
655
+ async getIamRole(roleName) {
656
+ try {
657
+ const result = await this.iam.send(
658
+ new GetRoleCommand({ RoleName: roleName })
659
+ );
660
+ return mapAwsIamRole(result.Role ?? {});
661
+ } catch (error) {
662
+ handleAwsError(error, `getIamRole(${roleName})`);
663
+ }
664
+ }
665
+ async ensureIamRole(input) {
666
+ try {
667
+ await this.getIamRole(input.roleName);
668
+ await this.iam.send(
669
+ new UpdateAssumeRolePolicyCommand({
670
+ RoleName: input.roleName,
671
+ PolicyDocument: input.assumeRolePolicyDocument
672
+ })
673
+ );
674
+ if (input.description !== void 0) {
675
+ await this.iam.send(
676
+ new UpdateRoleCommand({
677
+ RoleName: input.roleName,
678
+ Description: input.description
679
+ })
680
+ );
681
+ }
682
+ const tags = toAwsTags(input.tags);
683
+ if (tags) {
684
+ await this.iam.send(
685
+ new TagRoleCommand({
686
+ RoleName: input.roleName,
687
+ Tags: tags
688
+ })
689
+ );
690
+ }
691
+ return this.getIamRole(input.roleName);
692
+ } catch (error) {
693
+ if (!(error instanceof NotFoundError)) {
694
+ throw error;
695
+ }
696
+ }
697
+ try {
698
+ const result = await this.iam.send(
699
+ new CreateRoleCommand({
700
+ RoleName: input.roleName,
701
+ AssumeRolePolicyDocument: input.assumeRolePolicyDocument,
702
+ ...input.description ? { Description: input.description } : {},
703
+ ...input.path ? { Path: input.path } : {},
704
+ Tags: toAwsTags(input.tags)
705
+ })
706
+ );
707
+ return mapAwsIamRole(result.Role ?? {});
708
+ } catch (error) {
709
+ handleAwsError(error, `ensureIamRole(${input.roleName})`);
710
+ }
711
+ }
712
+ async putIamRolePolicy(input) {
713
+ try {
714
+ await this.iam.send(
715
+ new PutRolePolicyCommand({
716
+ RoleName: input.roleName,
717
+ PolicyName: input.policyName,
718
+ PolicyDocument: input.policyDocument
719
+ })
720
+ );
721
+ } catch (error) {
722
+ handleAwsError(error, `putIamRolePolicy(${input.roleName})`);
723
+ }
724
+ }
445
725
  // ==========================================================================
446
726
  // IAM Users (AwsDirectoryAdapter)
447
727
  // ==========================================================================