@@ -2,28 +2,35 @@ import { expect } from "chai";
2
2
import * as sinon from "sinon" ;
3
3
4
4
import * as secrets from "../../../apphosting/secrets" ;
5
+ import * as iam from "../../../gcp/iam" ;
6
+ import * as gcb from "../../../gcp/cloudbuild" ;
7
+ import * as gce from "../../../gcp/computeEngine" ;
5
8
import * as gcsmImport from "../../../gcp/secretManager" ;
6
9
import * as utilsImport from "../../../utils" ;
7
10
import * as promptImport from "../../../prompt" ;
11
+ import { FirebaseError } from "../../../error" ;
8
12
9
13
describe ( "secrets" , ( ) => {
10
- describe ( "upsertSecret" , ( ) => {
11
- let gcsm : sinon . SinonStubbedInstance < typeof gcsmImport > ;
12
- let utils : sinon . SinonStubbedInstance < typeof utilsImport > ;
13
- let prompt : sinon . SinonStubbedInstance < typeof promptImport > ;
14
-
15
- beforeEach ( ( ) => {
16
- gcsm = sinon . stub ( gcsmImport ) ;
17
- utils = sinon . stub ( utilsImport ) ;
18
- prompt = sinon . stub ( promptImport ) ;
19
- gcsm . isFunctionsManaged . restore ( ) ;
20
- gcsm . labels . restore ( ) ;
21
- } ) ;
14
+ let gcsm : sinon . SinonStubbedInstance < typeof gcsmImport > ;
15
+ let utils : sinon . SinonStubbedInstance < typeof utilsImport > ;
16
+ let prompt : sinon . SinonStubbedInstance < typeof promptImport > ;
22
17
23
- afterEach ( ( ) => {
24
- sinon . verifyAndRestore ( ) ;
25
- } ) ;
18
+ beforeEach ( ( ) => {
19
+ gcsm = sinon . stub ( gcsmImport ) ;
20
+ utils = sinon . stub ( utilsImport ) ;
21
+ prompt = sinon . stub ( promptImport ) ;
22
+ gcsm . isFunctionsManaged . restore ( ) ;
23
+ gcsm . labels . restore ( ) ;
24
+ gcsm . secretExists . throws ( "Unexpected secretExists call" ) ;
25
+ gcsm . getIamPolicy . throws ( "Unexpected getIamPolicy call" ) ;
26
+ gcsm . setIamPolicy . throws ( "Unexpected setIamPolicy call" ) ;
27
+ } ) ;
28
+
29
+ afterEach ( ( ) => {
30
+ sinon . verifyAndRestore ( ) ;
31
+ } ) ;
26
32
33
+ describe ( "upsertSecret" , ( ) => {
27
34
it ( "errors if a user tries to change replication policies (was global)" , async ( ) => {
28
35
gcsm . getSecret . withArgs ( "project" , "secret" ) . resolves ( {
29
36
name : "secret" ,
@@ -159,4 +166,72 @@ describe("secrets", () => {
159
166
) ;
160
167
} ) ;
161
168
} ) ;
169
+
170
+ describe ( "grantSecretAccess" , ( ) => {
171
+ const projectId = "projectId" ;
172
+ const projectNumber = "123456789" ;
173
+ const location = "us-central1" ;
174
+ const backendId = "backendId" ;
175
+ const secretName = "secretName" ;
176
+ const existingPolicy : iam . Policy = {
177
+ version : 1 ,
178
+ etag : "tag" ,
179
+ bindings : [
180
+ {
181
+ role : "roles/viewer" ,
182
+ members : [ `serviceAccount:${ gce . getDefaultServiceAccount ( projectNumber ) } ` ] ,
183
+ } ,
184
+ ] ,
185
+ } ;
186
+
187
+ it ( "should grant access to the appropriate service accounts" , async ( ) => {
188
+ gcsm . secretExists . resolves ( true ) ;
189
+ gcsm . getIamPolicy . resolves ( existingPolicy ) ;
190
+ gcsm . setIamPolicy . resolves ( ) ;
191
+
192
+ await secrets . grantSecretAccess ( secretName , location , backendId , projectId , projectNumber ) ;
193
+
194
+ const secret = {
195
+ projectId : projectId ,
196
+ name : secretName ,
197
+ } ;
198
+
199
+ const newBindings : iam . Binding [ ] = [
200
+ {
201
+ role : "roles/viewer" ,
202
+ members : [ `serviceAccount:${ gce . getDefaultServiceAccount ( projectNumber ) } ` ] ,
203
+ } ,
204
+ {
205
+ role : "roles/secretmanager.secretAccessor" ,
206
+ members : [
207
+ `serviceAccount:${ gcb . getDefaultServiceAccount ( projectNumber ) } ` ,
208
+ `serviceAccount:${ gce . getDefaultServiceAccount ( projectNumber ) } ` ,
209
+ ] ,
210
+ } ,
211
+ {
212
+ role : "roles/secretmanager.viewer" ,
213
+ members : [ `serviceAccount:${ gcb . getDefaultServiceAccount ( projectNumber ) } ` ] ,
214
+ } ,
215
+ ] ;
216
+
217
+ expect ( gcsm . secretExists ) . to . be . calledWith ( projectId , secretName ) ;
218
+ expect ( gcsm . getIamPolicy ) . to . be . calledWith ( secret ) ;
219
+ expect ( gcsm . setIamPolicy ) . to . be . calledWith ( secret , newBindings ) ;
220
+ } ) ;
221
+
222
+ it ( "does not grant access to a secret that doesn't exist" , ( ) => {
223
+ gcsm . secretExists . resolves ( false ) ;
224
+
225
+ expect (
226
+ secrets . grantSecretAccess ( secretName , location , backendId , projectId , projectNumber ) ,
227
+ ) . to . be . rejectedWith (
228
+ FirebaseError ,
229
+ `Secret ${ secretName } does not exist in project ${ projectId } ` ,
230
+ ) ;
231
+
232
+ expect ( gcsm . secretExists ) . to . be . calledWith ( projectId , secretName ) ;
233
+ expect ( gcsm . secretExists ) . to . be . calledOnce ;
234
+ expect ( gcsm . setIamPolicy ) . to . not . have . been . called ;
235
+ } ) ;
236
+ } ) ;
162
237
} ) ;
0 commit comments