@cubist-labs/cubesigner-sdk 0.2.28 → 0.3.8

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.
Files changed (146) hide show
  1. package/README.md +30 -28
  2. package/dist/cjs/package.json +41 -0
  3. package/dist/cjs/spec/env/beta.json +9 -0
  4. package/dist/cjs/spec/env/gamma.json +9 -0
  5. package/dist/cjs/spec/env/prod.json +9 -0
  6. package/dist/cjs/src/api.d.ts +652 -0
  7. package/dist/cjs/src/api.js +1345 -0
  8. package/dist/cjs/src/client.d.ts +642 -0
  9. package/dist/cjs/src/client.js +455 -0
  10. package/dist/cjs/src/env.d.ts +15 -0
  11. package/dist/cjs/src/env.js +35 -0
  12. package/dist/cjs/src/error.d.ts +32 -0
  13. package/dist/cjs/src/error.js +37 -0
  14. package/dist/cjs/src/events.d.ts +84 -0
  15. package/dist/cjs/src/events.js +195 -0
  16. package/dist/cjs/src/index.d.ts +203 -0
  17. package/dist/cjs/src/index.js +298 -0
  18. package/dist/cjs/src/key.d.ts +169 -0
  19. package/dist/cjs/src/key.js +262 -0
  20. package/dist/{src/fido.d.ts → cjs/src/mfa.d.ts} +38 -17
  21. package/dist/cjs/src/mfa.js +172 -0
  22. package/dist/cjs/src/org.d.ts +99 -0
  23. package/dist/cjs/src/org.js +95 -0
  24. package/dist/cjs/src/paginator.d.ts +76 -0
  25. package/dist/cjs/src/paginator.js +99 -0
  26. package/dist/cjs/src/response.d.ts +114 -0
  27. package/dist/cjs/src/response.js +203 -0
  28. package/dist/cjs/src/role.d.ts +289 -0
  29. package/dist/cjs/src/role.js +261 -0
  30. package/dist/cjs/src/schema.d.ts +6404 -0
  31. package/dist/cjs/src/schema.js +7 -0
  32. package/dist/cjs/src/schema_types.d.ts +116 -0
  33. package/dist/cjs/src/schema_types.js +3 -0
  34. package/dist/cjs/src/session/session_storage.d.ts +27 -0
  35. package/dist/cjs/src/session/session_storage.js +47 -0
  36. package/dist/cjs/src/session/signer_session_manager.d.ts +125 -0
  37. package/dist/cjs/src/session/signer_session_manager.js +239 -0
  38. package/dist/cjs/src/signer_session.d.ts +41 -0
  39. package/dist/cjs/src/signer_session.js +77 -0
  40. package/dist/cjs/src/user_export.d.ts +52 -0
  41. package/dist/cjs/src/user_export.js +129 -0
  42. package/dist/cjs/src/util.d.ts +56 -0
  43. package/dist/cjs/src/util.js +87 -0
  44. package/dist/esm/package.json +41 -0
  45. package/dist/esm/spec/env/beta.json +9 -0
  46. package/dist/esm/spec/env/gamma.json +9 -0
  47. package/dist/esm/spec/env/prod.json +9 -0
  48. package/dist/esm/src/api.d.ts +652 -0
  49. package/dist/esm/src/api.js +1335 -0
  50. package/dist/esm/src/client.d.ts +642 -0
  51. package/dist/esm/src/client.js +451 -0
  52. package/dist/esm/src/env.d.ts +15 -0
  53. package/dist/esm/src/env.js +9 -0
  54. package/dist/esm/src/error.d.ts +32 -0
  55. package/dist/esm/src/error.js +32 -0
  56. package/dist/esm/src/events.d.ts +84 -0
  57. package/dist/esm/src/events.js +189 -0
  58. package/dist/esm/src/index.d.ts +203 -0
  59. package/dist/esm/src/index.js +276 -0
  60. package/dist/esm/src/key.d.ts +169 -0
  61. package/dist/esm/src/key.js +256 -0
  62. package/dist/esm/src/mfa.d.ts +97 -0
  63. package/dist/esm/src/mfa.js +166 -0
  64. package/dist/esm/src/org.d.ts +99 -0
  65. package/dist/esm/src/org.js +91 -0
  66. package/dist/esm/src/paginator.d.ts +76 -0
  67. package/dist/esm/src/paginator.js +94 -0
  68. package/dist/esm/src/response.d.ts +114 -0
  69. package/dist/esm/src/response.js +198 -0
  70. package/dist/esm/src/role.d.ts +289 -0
  71. package/dist/esm/src/role.js +256 -0
  72. package/dist/esm/src/schema.d.ts +6404 -0
  73. package/dist/esm/src/schema.js +6 -0
  74. package/dist/esm/src/schema_types.d.ts +116 -0
  75. package/dist/esm/src/schema_types.js +2 -0
  76. package/dist/esm/src/session/session_storage.d.ts +27 -0
  77. package/dist/esm/src/session/session_storage.js +43 -0
  78. package/dist/esm/src/session/signer_session_manager.d.ts +125 -0
  79. package/dist/esm/src/session/signer_session_manager.js +235 -0
  80. package/dist/esm/src/signer_session.d.ts +41 -0
  81. package/dist/esm/src/signer_session.js +72 -0
  82. package/dist/esm/src/user_export.d.ts +52 -0
  83. package/dist/esm/src/user_export.js +99 -0
  84. package/dist/esm/src/util.d.ts +56 -0
  85. package/dist/esm/src/util.js +77 -0
  86. package/dist/package.json +13 -45
  87. package/dist/src/api.d.ts +29 -1
  88. package/dist/src/api.js +66 -1
  89. package/dist/src/client.d.ts +35 -14
  90. package/dist/src/client.js +12 -8
  91. package/dist/src/events.js +1 -1
  92. package/dist/src/index.d.ts +6 -11
  93. package/dist/src/index.js +9 -25
  94. package/dist/src/key.d.ts +18 -7
  95. package/dist/src/key.js +52 -19
  96. package/dist/src/role.d.ts +46 -3
  97. package/dist/src/role.js +60 -8
  98. package/dist/src/schema.d.ts +206 -72
  99. package/dist/src/schema.js +1 -1
  100. package/dist/src/schema_types.d.ts +3 -0
  101. package/dist/src/schema_types.js +1 -1
  102. package/dist/src/session/signer_session_manager.d.ts +38 -14
  103. package/dist/src/session/signer_session_manager.js +93 -33
  104. package/dist/src/util.d.ts +14 -0
  105. package/dist/src/util.js +24 -27
  106. package/package.json +19 -46
  107. package/src/api.ts +145 -19
  108. package/src/client.ts +106 -10
  109. package/src/error.ts +4 -0
  110. package/src/events.ts +2 -0
  111. package/src/index.ts +10 -24
  112. package/src/key.ts +67 -20
  113. package/src/mfa.ts +8 -4
  114. package/src/response.ts +50 -4
  115. package/src/role.ts +87 -7
  116. package/src/schema.ts +764 -152
  117. package/src/schema_types.ts +6 -0
  118. package/src/session/session_storage.ts +0 -32
  119. package/src/session/signer_session_manager.ts +126 -38
  120. package/src/util.ts +18 -10
  121. package/tsconfig.json +1 -21
  122. package/LICENSE-APACHE +0 -177
  123. package/LICENSE-MIT +0 -25
  124. package/NOTICE +0 -13
  125. package/dist/examples/ethers.d.ts +0 -1
  126. package/dist/examples/ethers.js +0 -142
  127. package/dist/src/ethers/index.d.ts +0 -95
  128. package/dist/src/ethers/index.js +0 -215
  129. package/dist/src/fido.js +0 -148
  130. package/dist/src/session/cognito_manager.d.ts +0 -71
  131. package/dist/src/session/cognito_manager.js +0 -129
  132. package/dist/src/session/generic.d.ts +0 -47
  133. package/dist/src/session/generic.js +0 -3
  134. package/dist/src/session/management_session_manager.d.ts +0 -59
  135. package/dist/src/session/management_session_manager.js +0 -111
  136. package/dist/src/session/oidc_session_manager.d.ts +0 -78
  137. package/dist/src/session/oidc_session_manager.js +0 -142
  138. package/dist/src/session/session_manager.d.ts +0 -99
  139. package/dist/src/session/session_manager.js +0 -136
  140. package/dist/src/sign.d.ts +0 -114
  141. package/dist/src/sign.js +0 -248
  142. package/dist/test/sessions.d.ts +0 -35
  143. package/dist/test/sessions.js +0 -56
  144. package/src/ethers/index.ts +0 -253
  145. package/src/session/cognito_manager.ts +0 -161
  146. package/src/session/session_manager.ts +0 -165
package/src/key.ts CHANGED
@@ -1,5 +1,12 @@
1
1
  import { KeyPolicy } from "./role";
2
- import { KeyInfoApi, KeyTypeApi, UpdateKeyRequest, SchemaKeyType } from "./schema_types";
2
+ import { PageOpts } from "./paginator";
3
+ import {
4
+ KeyInfoApi,
5
+ KeyTypeApi,
6
+ UpdateKeyRequest,
7
+ SchemaKeyType,
8
+ KeyInRoleInfo,
9
+ } from "./schema_types";
3
10
  import { CubeSignerClient } from "./client";
4
11
 
5
12
  /** Secp256k1 key type */
@@ -66,14 +73,18 @@ export function toKeyInfo(key: KeyInfoApi): KeyInfo {
66
73
  };
67
74
  }
68
75
 
69
- /** Signing keys. */
76
+ /**
77
+ * A representation of a signing key.
78
+ */
70
79
  export class Key {
71
80
  /** The CubeSigner instance that this key is associated with */
72
- readonly #csc: CubeSignerClient;
81
+ protected readonly csc: CubeSignerClient;
82
+ /** The key information */
83
+ #data: KeyInfo;
73
84
 
74
85
  /** The organization that this key is in */
75
86
  get orgId() {
76
- return this.#csc.orgId;
87
+ return this.csc.orgId;
77
88
  }
78
89
 
79
90
  /**
@@ -81,13 +92,17 @@ export class Key {
81
92
  * the type of key (such as a public key for BLS or an ethereum address for Secp)
82
93
  * @example Key#0x8e3484687e66cdd26cf04c3647633ab4f3570148
83
94
  */
84
- readonly id: string;
95
+ get id(): string {
96
+ return this.#data.key_id;
97
+ }
85
98
 
86
99
  /**
87
100
  * A unique identifier specific to the type of key, such as a public key or an ethereum address
88
101
  * @example 0x8e3484687e66cdd26cf04c3647633ab4f3570148
89
102
  */
90
- readonly materialId: string;
103
+ get materialId(): string {
104
+ return this.#data.material_id;
105
+ }
91
106
 
92
107
  /**
93
108
  * @description Hex-encoded, serialized public key. The format used depends on the key type:
@@ -95,7 +110,18 @@ export class Key {
95
110
  * - BLS keys use 48-byte compressed BLS12-381 (ZCash) format
96
111
  * @example 0x04d2688b6bc2ce7f9879b9e745f3c4dc177908c5cef0c1b64cff19ae7ff27dee623c64fe9d9c325c7fbbc748bbd5f607ce14dd83e28ebbbb7d3e7f2ffb70a79431
97
112
  */
98
- readonly publicKey: string;
113
+ get publicKey(): string {
114
+ return this.#data.public_key;
115
+ }
116
+
117
+ /**
118
+ * Get the cached properties of this key. The cached properties reflect the
119
+ * state of the last fetch or update (e.g., after awaiting `Key.enabled()`
120
+ * or `Key.disable()`).
121
+ */
122
+ get cached(): KeyInfo {
123
+ return this.#data;
124
+ }
99
125
 
100
126
  /** The type of key. */
101
127
  async type(): Promise<KeyType> {
@@ -119,6 +145,15 @@ export class Key {
119
145
  await this.update({ enabled: false });
120
146
  }
121
147
 
148
+ /**
149
+ * The list roles this key is in.
150
+ * @param {PageOpts} page Optional pagination options; by default, retrieves all roles this key is in.
151
+ * @return {Promise<KeyInRoleInfo[]>} Roles this key is in.
152
+ */
153
+ async roles(page?: PageOpts): Promise<KeyInRoleInfo[]> {
154
+ return await this.csc.keyRolesList(this.id, page).fetch();
155
+ }
156
+
122
157
  /**
123
158
  * Set new policy (overwriting any policies previously set for this key)
124
159
  * @param {KeyPolicy} policy The new policy to set
@@ -128,7 +163,20 @@ export class Key {
128
163
  }
129
164
 
130
165
  /**
131
- * Append to existing key policy. This append is not atomic -- it uses {@link policy} to fetch the current policy and then {@link setPolicy} to set the policy -- and should not be used in across concurrent sessions.
166
+ * Set key metadata. The metadata must be at most 1024 characters
167
+ * and must match the following regex: ^[A-Za-z0-9_=+/ \-\.\,]{0,1024}$.
168
+ *
169
+ * @param {string} metadata The new metadata to set.
170
+ */
171
+ async setMetadata(metadata: string) {
172
+ await this.update({ metadata });
173
+ }
174
+
175
+ /**
176
+ * Append to existing key policy. This append is not atomic -- it uses {@link policy}
177
+ * to fetch the current policy and then {@link setPolicy} to set the policy -- and
178
+ * should not be used in across concurrent sessions.
179
+ *
132
180
  * @param {KeyPolicy} policy The policy to append to the existing one.
133
181
  */
134
182
  async appendPolicy(policy: KeyPolicy) {
@@ -137,8 +185,8 @@ export class Key {
137
185
  }
138
186
 
139
187
  /**
140
- * Get the policy for the org.
141
- * @return {Promise<KeyPolicy>} The policy for the org.
188
+ * Get the policy for the key.
189
+ * @return {Promise<KeyPolicy>} The policy for the key.
142
190
  */
143
191
  async policy(): Promise<KeyPolicy> {
144
192
  const data = await this.fetch();
@@ -166,7 +214,7 @@ export class Key {
166
214
  * Delete this key.
167
215
  */
168
216
  async delete() {
169
- await this.#csc.keyDelete(this.id);
217
+ await this.csc.keyDelete(this.id);
170
218
  }
171
219
 
172
220
  // --------------------------------------------------------------------------
@@ -177,24 +225,23 @@ export class Key {
177
225
  * Create a new key.
178
226
  *
179
227
  * @param {CubeSignerClient} csc The CubeSigner instance to use for signing.
180
- * @param {KeyInfo} data The JSON response from the API server.
228
+ * @param {KeyInfoApi} data The JSON response from the API server.
181
229
  * @internal
182
230
  */
183
231
  constructor(csc: CubeSignerClient, data: KeyInfoApi) {
184
- this.#csc = csc;
185
- this.id = data.key_id;
186
- this.materialId = data.material_id;
187
- this.publicKey = data.public_key;
232
+ this.csc = csc;
233
+ this.#data = toKeyInfo(data);
188
234
  }
189
235
 
190
236
  /**
191
237
  * Update the key.
192
238
  * @param {UpdateKeyRequest} request The JSON request to send to the API server.
193
239
  * @return {KeyInfo} The JSON response from the API server.
240
+ * @internal
194
241
  */
195
242
  private async update(request: UpdateKeyRequest): Promise<KeyInfo> {
196
- const data = await this.#csc.keyUpdate(this.id, request);
197
- return toKeyInfo(data);
243
+ this.#data = await this.csc.keyUpdate(this.id, request).then(toKeyInfo);
244
+ return this.#data;
198
245
  }
199
246
 
200
247
  /**
@@ -204,8 +251,8 @@ export class Key {
204
251
  * @internal
205
252
  */
206
253
  private async fetch(): Promise<KeyInfo> {
207
- const data = await this.#csc.keyGet(this.id);
208
- return toKeyInfo(data);
254
+ this.#data = await this.csc.keyGet(this.id).then(toKeyInfo);
255
+ return this.#data;
209
256
  }
210
257
  }
211
258
 
package/src/mfa.ts CHANGED
@@ -4,6 +4,7 @@ import {
4
4
  ApiAddFidoChallenge,
5
5
  ApiMfaFidoChallenge,
6
6
  MfaRequestInfo,
7
+ MfaVote,
7
8
  PublicKeyCredential,
8
9
  TotpInfo,
9
10
  } from "./schema_types";
@@ -159,10 +160,12 @@ export class MfaFidoChallenge {
159
160
  /**
160
161
  * Answers this challenge by using the `CredentialsContainer` API to get a credential
161
162
  * based on the the public key credential request options from this challenge.
163
+ *
164
+ * @param {MfaVote} vote Approve or reject the MFA request. Defaults to "approve".
162
165
  */
163
- async createCredentialAndAnswer(): Promise<MfaRequestInfo> {
166
+ async createCredentialAndAnswer(vote?: MfaVote): Promise<MfaRequestInfo> {
164
167
  const cred = await navigator.credentials.get({ publicKey: this.options });
165
- return await this.answer(cred);
168
+ return await this.answer(cred, vote);
166
169
  }
167
170
 
168
171
  /**
@@ -175,8 +178,9 @@ export class MfaFidoChallenge {
175
178
  *
176
179
  * @param {any} cred Credential created by calling the `CredentialContainer`'s `get` method
177
180
  * based on the public key credential request options from this challenge.
181
+ * @param {MfaVote} vote Approve or reject. Defaults to "approve".
178
182
  */
179
- async answer(cred: any): Promise<MfaRequestInfo> {
183
+ async answer(cred: any, vote: MfaVote = "approve"): Promise<MfaRequestInfo> {
180
184
  const answer = <PublicKeyCredential>{
181
185
  id: cred.id,
182
186
  response: {
@@ -185,6 +189,6 @@ export class MfaFidoChallenge {
185
189
  signature: encodeToBase64Url(cred.response.signature),
186
190
  },
187
191
  };
188
- return await this.#api.mfaApproveFidoComplete(this.mfaId, this.challengeId, answer);
192
+ return await this.#api.mfaVoteFidoComplete(this.mfaId, vote, this.challengeId, answer);
189
193
  }
190
194
  }
package/src/response.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { CubeSignerClient, SignerSession } from ".";
1
+ import { CubeSignerClient, MfaVote, SignerSession } from ".";
2
2
  import { MfaReceipt } from "./mfa";
3
3
  import { AcceptedResponse, NewSessionResponse } from "./schema_types";
4
4
 
@@ -90,13 +90,39 @@ export class CubeSignerResponse<U> {
90
90
  * @return {CubeSignerResponse<U>} The result of signing with the approval
91
91
  */
92
92
  async approveTotp(session: SignerSession, code: string): Promise<CubeSignerResponse<U>> {
93
+ return await this.#mfaVoteTotp(session, code, "approve");
94
+ }
95
+
96
+ /**
97
+ * Reject the MFA request using a given session and a TOTP code.
98
+ *
99
+ * @param {SignerSession} session Signer session to use
100
+ * @param {string} code 6-digit TOTP code
101
+ */
102
+ async rejectTotp(session: SignerSession, code: string) {
103
+ await this.#mfaVoteTotp(session, code, "reject");
104
+ }
105
+
106
+ /**
107
+ * Approve or reject an MFA request using a given session and a TOTP code.
108
+ *
109
+ * @param {SignerSession} session Signer session to use
110
+ * @param {string} code 6-digit TOTP code
111
+ * @param {MfaVote} vote Approve or reject
112
+ * @return {CubeSignerResponse<U>} The result of signing with the approval
113
+ */
114
+ async #mfaVoteTotp(
115
+ session: SignerSession,
116
+ code: string,
117
+ vote: MfaVote,
118
+ ): Promise<CubeSignerResponse<U>> {
93
119
  if (!this.requiresMfa()) {
94
120
  return this;
95
121
  }
96
122
 
97
123
  const mfaId = this.mfaId();
98
124
  const mfaOrgId = this.#mfaRequired!.org_id;
99
- const mfaApproval = await session.mfaApproveTotp(mfaId, code);
125
+ const mfaApproval = await session.mfaVoteTotp(mfaId, code, vote);
100
126
  const mfaConf = mfaApproval.receipt?.confirmation;
101
127
 
102
128
  if (!mfaConf) {
@@ -107,12 +133,32 @@ export class CubeSignerResponse<U> {
107
133
  }
108
134
 
109
135
  /**
110
- * Approve the MFA request using a given `CubeSignerClient` instance (i.e., its session).
136
+ * Approve the MFA request using a given {@link CubeSignerClient} instance (i.e., its session).
111
137
  *
112
138
  * @param {CubeSignerClient} cs CubeSigner whose session to use
113
139
  * @return {CubeSignerResponse<U>} The result of signing with the approval
114
140
  */
115
141
  async approve(cs: CubeSignerClient): Promise<CubeSignerResponse<U>> {
142
+ return await this.#mfaVoteCs(cs, "approve");
143
+ }
144
+
145
+ /**
146
+ * Reject the MFA request using a given {@link CubeSignerClient} instance (i.e., its session).
147
+ *
148
+ * @param {CubeSignerClient} cs CubeSigner client whose session to use
149
+ */
150
+ async reject(cs: CubeSignerClient) {
151
+ await this.#mfaVoteCs(cs, "reject");
152
+ }
153
+
154
+ /**
155
+ * Approve or reject an MFA request using a given {@link CubeSignerClient} instance (i.e., its session).
156
+ *
157
+ * @param {CubeSignerClient} cs CubeSigner whose session to use
158
+ * @param {MfaVote} mfaVote Approve or reject
159
+ * @return {CubeSignerResponse<U>} The result of signing with the approval
160
+ */
161
+ async #mfaVoteCs(cs: CubeSignerClient, mfaVote: MfaVote): Promise<CubeSignerResponse<U>> {
116
162
  if (!this.requiresMfa()) {
117
163
  return this;
118
164
  }
@@ -120,7 +166,7 @@ export class CubeSignerResponse<U> {
120
166
  const mfaId = this.#mfaRequired!.id;
121
167
  const mfaOrgId = this.#mfaRequired!.org_id;
122
168
 
123
- const mfaApproval = await cs.mfaApprove(mfaId);
169
+ const mfaApproval = await cs.mfaVoteCs(mfaId, mfaVote);
124
170
  const mfaConf = mfaApproval.receipt?.confirmation;
125
171
 
126
172
  if (!mfaConf) {
package/src/role.ts CHANGED
@@ -48,6 +48,13 @@ export type TxDepositPubkey = { TxDeposit: { kind: DepositContract; pubkey: stri
48
48
  */
49
49
  export type TxDepositRole = { TxDeposit: { kind: DepositContract; role_id: string } };
50
50
 
51
+ /**
52
+ * Only allow connections from clients whose IP addresses match any of these IPv4 CIDR blocks.
53
+ *
54
+ * @example { SourceIpAllowlist: [ "123.456.78.9/16" ] }
55
+ */
56
+ export type SourceIpAllowlist = { SourceIpAllowlist: string[] };
57
+
51
58
  /** All different kinds of sensitive operations. */
52
59
  export enum OperationKind {
53
60
  BlobSign = "BlobSign", // eslint-disable-line no-unused-vars
@@ -100,6 +107,17 @@ export type RequireMfa = {
100
107
  export const AllowRawBlobSigning = "AllowRawBlobSigning" as const;
101
108
  export type AllowRawBlobSigning = typeof AllowRawBlobSigning;
102
109
 
110
+ /** Allow EIP-191 signing */
111
+ export const AllowEip191Signing = "AllowEip191Signing" as const;
112
+ export type AllowEip191Signing = typeof AllowEip191Signing;
113
+
114
+ /** Allow EIP-712 signing */
115
+ export const AllowEip712Signing = "AllowEip712Signing" as const;
116
+ export type AllowEip712Signing = typeof AllowEip712Signing;
117
+
118
+ /** Key policies that restrict the requests that the signing endpoints accept */
119
+ type KeyDenyPolicy = TxReceiver | TxDeposit | SourceIpAllowlist | RequireMfa;
120
+
103
121
  /**
104
122
  * Key policy
105
123
  *
@@ -124,7 +142,15 @@ export type AllowRawBlobSigning = typeof AllowRawBlobSigning;
124
142
  * }
125
143
  * ]
126
144
  */
127
- export type KeyPolicy = (TxReceiver | TxDeposit | RequireMfa | AllowRawBlobSigning)[];
145
+ export type KeyPolicy = (
146
+ | KeyDenyPolicy
147
+ | AllowRawBlobSigning
148
+ | AllowEip191Signing
149
+ | AllowEip712Signing
150
+ )[];
151
+
152
+ /** Role policy */
153
+ export type RolePolicy = KeyDenyPolicy[];
128
154
 
129
155
  /** A key guarded by a policy. */
130
156
  export class KeyWithPolicies {
@@ -154,15 +180,30 @@ export class KeyWithPolicies {
154
180
  /** Roles. */
155
181
  export class Role {
156
182
  readonly #csc: CubeSignerClient;
183
+ /** The role information */
184
+ #data: RoleInfo;
157
185
 
158
186
  /** Human-readable name for the role */
159
- public readonly name?: string;
187
+ get name(): string | undefined {
188
+ return this.#data.name ?? undefined;
189
+ }
160
190
 
161
191
  /**
162
192
  * The ID of the role.
163
193
  * @example Role#bfe3eccb-731e-430d-b1e5-ac1363e6b06b
164
194
  */
165
- readonly id: string;
195
+ get id(): string {
196
+ return this.#data.role_id;
197
+ }
198
+
199
+ /**
200
+ * @return {RoleInfo} the cached properties of this role. The cached properties
201
+ * reflect the state of the last fetch or update (e.g., after awaiting
202
+ * `Role.enabled()` or `Role.disable()`).
203
+ */
204
+ get cached(): RoleInfo {
205
+ return this.#data;
206
+ }
166
207
 
167
208
  /** Delete the role. */
168
209
  async delete(): Promise<void> {
@@ -185,6 +226,35 @@ export class Role {
185
226
  await this.update({ enabled: false });
186
227
  }
187
228
 
229
+ /**
230
+ * Set new policy (overwriting any policies previously set for this role)
231
+ * @param {RolePolicy} policy The new policy to set
232
+ */
233
+ async setPolicy(policy: RolePolicy) {
234
+ await this.update({ policy: policy as unknown as Record<string, never>[] });
235
+ }
236
+
237
+ /**
238
+ * Append to existing role policy. This append is not atomic---it uses
239
+ * {@link policy} to fetch the current policy and then {@link setPolicy}
240
+ * to set the policy---and should not be used in across concurrent sessions.
241
+ *
242
+ * @param {RolePolicy} policy The policy to append to the existing one.
243
+ */
244
+ async appendPolicy(policy: RolePolicy) {
245
+ const existing = await this.policy();
246
+ await this.setPolicy([...existing, ...policy]);
247
+ }
248
+
249
+ /**
250
+ * Get the policy for the role.
251
+ * @return {Promise<RolePolicy>} The policy for the role.
252
+ */
253
+ async policy(): Promise<RolePolicy> {
254
+ const data = await this.fetch();
255
+ return (data.policy ?? []) as unknown as RolePolicy;
256
+ }
257
+
188
258
  /**
189
259
  * The list of all users with access to the role.
190
260
  * @example [
@@ -208,6 +278,15 @@ export class Role {
208
278
  await this.#csc.roleUserAdd(this.id, userId);
209
279
  }
210
280
 
281
+ /**
282
+ * Remove an existing user from an existing role.
283
+ *
284
+ * @param {string} userId The user-id of the user to remove from the role.
285
+ */
286
+ async removeUser(userId: string) {
287
+ await this.#csc.roleUserRemove(this.id, userId);
288
+ }
289
+
211
290
  /**
212
291
  * The list of keys in the role.
213
292
  * @example [
@@ -301,8 +380,7 @@ export class Role {
301
380
  */
302
381
  constructor(csc: CubeSignerClient, data: RoleInfo) {
303
382
  this.#csc = csc;
304
- this.id = data.role_id;
305
- this.name = data.name ?? undefined;
383
+ this.#data = data;
306
384
  }
307
385
 
308
386
  /**
@@ -312,7 +390,8 @@ export class Role {
312
390
  * @return {Promise<RoleInfo>} The updated role information.
313
391
  */
314
392
  private async update(request: UpdateRoleRequest): Promise<RoleInfo> {
315
- return await this.#csc.roleUpdate(this.id, request);
393
+ this.#data = await this.#csc.roleUpdate(this.id, request);
394
+ return this.#data;
316
395
  }
317
396
 
318
397
  /**
@@ -322,6 +401,7 @@ export class Role {
322
401
  * @internal
323
402
  */
324
403
  private async fetch(): Promise<RoleInfo> {
325
- return await this.#csc.roleGet(this.id);
404
+ this.#data = await this.#csc.roleGet(this.id);
405
+ return this.#data;
326
406
  }
327
407
  }