1import * as AWS from "@/AWS";
2import {
3 ServiceSpecificCredential,
4 SigningCertificate,
5 SSHPublicKey,
6 User,
7} from "@/AWS/IAM";
8import * as Test from "@/Test/Alchemy";
9import * as IAM from "@distilled.cloud/aws/iam";
10import { describe, expect } from "alchemy-test";
11import * as Effect from "effect/Effect";
12import { testCertificateBody, testSshPublicKey } from "./fixtures.ts";
13import { withSigningCertificateFixture } from "./SigningCertificateTestLease.ts";
14
15const { test } = Test.make({ providers: AWS.providers() });
16
17describe(
18 "AWS.IAM credential resources",
19 { tags: ["provider:aws", "provider:aws:iam", "live"] },
20 () => {
21 test.provider(
22 "create, update, and delete a service-specific credential",
23 (stack) =>
24 Effect.gen(function* () {
25 yield* stack.destroy();
26
27 const deployed = yield* stack.deploy(
28 Effect.gen(function* () {
29 const user = yield* User("CredentialOwner", {});
30 const credential = yield* ServiceSpecificCredential(
31 "ServiceCredential",
32 {
33 userName: user.userName,
34 serviceName: "codecommit.amazonaws.com",
35 status: "Active",
36 },
37 );
38 return { user, credential };
39 }),
40 );
41
42 expect(deployed.credential.servicePassword).toBeDefined();
43
44 const created = yield* IAM.listServiceSpecificCredentials({
45 UserName: deployed.user.userName,
46 ServiceName: deployed.credential.serviceName,
47 });
48 expect(
49 created.ServiceSpecificCredentials?.some(
50 (entry) =>
51 entry.ServiceSpecificCredentialId ===
52 deployed.credential.serviceSpecificCredentialId,
53 ),
54 ).toBe(true);
55
56 yield* stack.deploy(
57 Effect.gen(function* () {
58 const user = yield* User("CredentialOwner", {});
59 yield* ServiceSpecificCredential("ServiceCredential", {
60 userName: user.userName,
61 serviceName: "codecommit.amazonaws.com",
62 status: "Inactive",
63 });
64 }),
65 );
66
67 const updated = yield* IAM.listServiceSpecificCredentials({
68 UserName: deployed.user.userName,
69 ServiceName: deployed.credential.serviceName,
70 });
71 const metadata = updated.ServiceSpecificCredentials?.find(
72 (entry) =>
73 entry.ServiceSpecificCredentialId ===
74 deployed.credential.serviceSpecificCredentialId,
75 );
76 expect(metadata?.Status).toBe("Inactive");
77
78 yield* stack.destroy();
79
80 const deleted = yield* IAM.listServiceSpecificCredentials({
81 UserName: deployed.user.userName,
82 ServiceName: deployed.credential.serviceName,
83 }).pipe(Effect.option);
84 expect(
85 deleted._tag === "None" ||
86 !deleted.value.ServiceSpecificCredentials?.some(
87 (entry) =>
88 entry.ServiceSpecificCredentialId ===
89 deployed.credential.serviceSpecificCredentialId,
90 ),
91 ).toBe(true);
92 }),
93 );
94
95 test.provider(
96 "create, update, and delete SSH and signing credentials for a user",
97 (stack) =>
98 withSigningCertificateFixture(
99 Effect.gen(function* () {
100 yield* stack.destroy();
101
102 const deployed = yield* stack.deploy(
103 Effect.gen(function* () {
104 const user = yield* User("CredentialUser", {});
105 const sshKey = yield* SSHPublicKey("UserSshKey", {
106 userName: user.userName,
107 sshPublicKeyBody: testSshPublicKey,
108 status: "Active",
109 });
110 const signingCertificate = yield* SigningCertificate(
111 "UserSigningCertificate",
112 {
113 userName: user.userName,
114 certificateBody: testCertificateBody,
115 status: "Active",
116 },
117 );
118 return { user, sshKey, signingCertificate };
119 }),
120 );
121
122 const createdKey = yield* IAM.getSSHPublicKey({
123 UserName: deployed.user.userName,
124 SSHPublicKeyId: deployed.sshKey.sshPublicKeyId,
125 Encoding: "SSH",
126 });
127 expect(createdKey.SSHPublicKey?.Status).toBe("Active");
128
129 const createdCerts = yield* IAM.listSigningCertificates({
130 UserName: deployed.user.userName,
131 });
132 expect(
133 createdCerts.Certificates.some(
134 (entry) =>
135 entry.CertificateId ===
136 deployed.signingCertificate.certificateId,
137 ),
138 ).toBe(true);
139
140 yield* stack.deploy(
141 Effect.gen(function* () {
142 const user = yield* User("CredentialUser", {});
143 yield* SSHPublicKey("UserSshKey", {
144 userName: user.userName,
145 sshPublicKeyBody: testSshPublicKey,
146 status: "Inactive",
147 });
148 yield* SigningCertificate("UserSigningCertificate", {
149 userName: user.userName,
150 certificateBody: testCertificateBody,
151 status: "Inactive",
152 });
153 }),
154 );
155
156 const updatedKey = yield* IAM.getSSHPublicKey({
157 UserName: deployed.user.userName,
158 SSHPublicKeyId: deployed.sshKey.sshPublicKeyId,
159 Encoding: "SSH",
160 });
161 expect(updatedKey.SSHPublicKey?.Status).toBe("Inactive");
162
163 const updatedCerts = yield* IAM.listSigningCertificates({
164 UserName: deployed.user.userName,
165 });
166 const updatedCert = updatedCerts.Certificates.find(
167 (entry) =>
168 entry.CertificateId ===
169 deployed.signingCertificate.certificateId,
170 );
171 expect(updatedCert?.Status).toBe("Inactive");
172
173 yield* stack.destroy();
174
175 const deletedKey = yield* IAM.getSSHPublicKey({
176 UserName: deployed.user.userName,
177 SSHPublicKeyId: deployed.sshKey.sshPublicKeyId,
178 Encoding: "SSH",
179 }).pipe(Effect.option);
180 expect(deletedKey._tag).toBe("None");
181
182 const deletedCerts = yield* IAM.listSigningCertificates({
183 UserName: deployed.user.userName,
184 }).pipe(Effect.option);
185 expect(
186 deletedCerts._tag === "None" ||
187 !deletedCerts.value.Certificates.some(
188 (entry) =>
189 entry.CertificateId ===
190 deployed.signingCertificate.certificateId,
191 ),
192 ).toBe(true);
193 }),
194 ),
195 );
196 },
197);