@@ -26,10 +26,9 @@ import * as nock from 'nock';
26
26
27
27
import * as mocks from '../../resources/mocks' ;
28
28
import { FirebaseTokenGenerator , ServiceAccountSigner , IAMSigner } from '../../../src/auth/token-generator' ;
29
- import { FirebaseAuthError , AuthClientErrorCode } from '../../../src/utils/error' ;
30
29
31
30
import { Certificate } from '../../../src/auth/credential' ;
32
- import { SignedApiRequestHandler , HttpRequestHandler } from '../../../src/utils/api-request' ;
31
+ import { AuthorizedHttpClient , HttpClient } from '../../../src/utils/api-request' ;
33
32
import { FirebaseApp } from '../../../src/firebase-app' ;
34
33
import * as utils from '../utils' ;
35
34
@@ -111,11 +110,6 @@ describe('CryptoSigner', () => {
111
110
112
111
before ( ( ) => {
113
112
utils . mockFetchAccessTokenRequests ( mockAccessToken ) ;
114
- nock ( 'http://metadata' )
115
- . matchHeader ( 'Metadata-Flavor' , 'Google' )
116
- . persist ( )
117
- . get ( '/computeMetadata/v1/instance/service-accounts/default/email' )
118
- . reply ( 200 , 'discovered-service-account' ) ;
119
113
} ) ;
120
114
121
115
after ( ( ) => nock . cleanAll ( ) ) ;
@@ -132,102 +126,115 @@ describe('CryptoSigner', () => {
132
126
expect ( ( ) => {
133
127
const anyIAMSigner : any = IAMSigner ;
134
128
return new anyIAMSigner ( ) ;
135
- } ) . to . throw ( 'Must provide a request handler to initialize IAMSigner' ) ;
129
+ } ) . to . throw ( 'Must provide a HTTP client to initialize IAMSigner' ) ;
136
130
} ) ;
137
131
138
132
describe ( 'explicit service account' , ( ) => {
139
133
const response = { signature : Buffer . from ( 'testsignature' ) . toString ( 'base64' ) } ;
134
+ const input = Buffer . from ( 'base64' ) ;
135
+ const signRequest = {
136
+ method : 'POST' ,
137
+ url : `https://iam.googleapis.com/v1/projects/-/serviceAccounts/test-service-account:signBlob` ,
138
+ headers : { Authorization : `Bearer ${ mockAccessToken } ` } ,
139
+ data : { bytesToSign : input . toString ( 'base64' ) } ,
140
+ } ;
140
141
let stub : sinon . SinonStub ;
141
142
142
143
afterEach ( ( ) => {
143
144
stub . restore ( ) ;
144
145
} ) ;
145
146
146
147
it ( 'should sign using the IAM service' , ( ) => {
147
- stub = sinon . stub ( HttpRequestHandler . prototype , 'sendRequest' )
148
- . returns ( Promise . resolve ( response ) ) ;
149
- const requestHandler = new SignedApiRequestHandler ( mockApp ) ;
148
+ const expectedResult = utils . responseFrom ( response ) ;
149
+ stub = sinon . stub ( HttpClient . prototype , 'send' ) . resolves ( expectedResult ) ;
150
+ const requestHandler = new AuthorizedHttpClient ( mockApp ) ;
150
151
const signer = new IAMSigner ( requestHandler , 'test-service-account' ) ;
151
- const input = Buffer . from ( 'base64' ) ;
152
152
return signer . sign ( input ) . then ( ( signature ) => {
153
153
expect ( signature . toString ( 'base64' ) ) . to . equal ( response . signature ) ;
154
- expect ( stub ) . to . have . been . calledOnce . and . calledWith (
155
- 'iam.googleapis.com' , 443 ,
156
- '/v1/projects/-/serviceAccounts/test-service-account:signBlob' ,
157
- 'POST' , { bytesToSign : input . toString ( 'base64' ) } ,
158
- { Authorization : `Bearer ${ mockAccessToken } ` } , undefined ) ;
154
+ expect ( stub ) . to . have . been . calledOnce . and . calledWith ( signRequest ) ;
159
155
} ) ;
160
156
} ) ;
161
157
162
158
it ( 'should fail if the IAM service responds with an error' , ( ) => {
163
- stub = sinon . stub ( HttpRequestHandler . prototype , 'sendRequest' )
164
- . throws ( {
165
- statusCode : 500 ,
166
- error : { error : { status : 'PROJECT_NOT_FOUND' , message : 'test reason' } } ,
167
- } ) ;
168
- const requestHandler = new SignedApiRequestHandler ( mockApp ) ;
159
+ const expectedResult = utils . errorFrom ( {
160
+ error : {
161
+ status : 'PROJECT_NOT_FOUND' ,
162
+ message : 'test reason' ,
163
+ } ,
164
+ } ) ;
165
+ stub = sinon . stub ( HttpClient . prototype , 'send' ) . rejects ( expectedResult ) ;
166
+ const requestHandler = new AuthorizedHttpClient ( mockApp ) ;
169
167
const signer = new IAMSigner ( requestHandler , 'test-service-account' ) ;
170
- const input = Buffer . from ( 'base64' ) ;
171
168
return signer . sign ( input ) . catch ( ( err ) => {
172
169
expect ( err . message ) . to . equal ( 'test reason' ) ;
173
- expect ( stub ) . to . have . been . calledOnce . and . calledWith (
174
- 'iam.googleapis.com' , 443 ,
175
- '/v1/projects/-/serviceAccounts/test-service-account:signBlob' ,
176
- 'POST' , { bytesToSign : input . toString ( 'base64' ) } ,
177
- { Authorization : `Bearer ${ mockAccessToken } ` } , undefined ) ;
170
+ expect ( stub ) . to . have . been . calledOnce . and . calledWith ( signRequest ) ;
178
171
} ) ;
179
172
} ) ;
180
173
181
174
it ( 'should return the explicitly specified service account' , ( ) => {
182
- const signer = new IAMSigner ( new SignedApiRequestHandler ( mockApp ) , 'test-service-account' ) ;
175
+ const signer = new IAMSigner ( new AuthorizedHttpClient ( mockApp ) , 'test-service-account' ) ;
183
176
return signer . getAccount ( ) . should . eventually . equal ( 'test-service-account' ) ;
184
177
} ) ;
185
178
} ) ;
186
179
187
180
describe ( 'auto discovered service account' , ( ) => {
188
181
const input = Buffer . from ( 'base64' ) ;
189
182
const response = { signature : Buffer . from ( 'testsignature' ) . toString ( 'base64' ) } ;
183
+ const metadataRequest = {
184
+ method : 'GET' ,
185
+ url : `http://metadata/computeMetadata/v1/instance/service-accounts/default/email` ,
186
+ headers : { 'Metadata-Flavor' : 'Google' } ,
187
+ } ;
188
+ const signRequest = {
189
+ method : 'POST' ,
190
+ url : `https://iam.googleapis.com/v1/projects/-/serviceAccounts/discovered-service-account:signBlob` ,
191
+ headers : { Authorization : `Bearer ${ mockAccessToken } ` } ,
192
+ data : { bytesToSign : input . toString ( 'base64' ) } ,
193
+ } ;
190
194
let stub : sinon . SinonStub ;
191
195
192
196
afterEach ( ( ) => {
193
197
stub . restore ( ) ;
194
198
} ) ;
195
199
196
200
it ( 'should sign using the IAM service' , ( ) => {
197
- stub = sinon . stub ( HttpRequestHandler . prototype , 'sendRequest' )
198
- . returns ( Promise . resolve ( response ) ) ;
199
- const requestHandler = new SignedApiRequestHandler ( mockApp ) ;
201
+ stub = sinon . stub ( HttpClient . prototype , 'send' ) ;
202
+ stub . onCall ( 0 ) . resolves ( utils . responseFrom ( 'discovered-service-account' ) ) ;
203
+ stub . onCall ( 1 ) . resolves ( utils . responseFrom ( response ) ) ;
204
+ const requestHandler = new AuthorizedHttpClient ( mockApp ) ;
200
205
const signer = new IAMSigner ( requestHandler ) ;
201
206
return signer . sign ( input ) . then ( ( signature ) => {
202
207
expect ( signature . toString ( 'base64' ) ) . to . equal ( response . signature ) ;
203
- expect ( stub ) . to . have . been . calledOnce . and . calledWith (
204
- 'iam.googleapis.com' , 443 ,
205
- '/v1/projects/-/serviceAccounts/discovered-service-account:signBlob' ,
206
- 'POST' , { bytesToSign : input . toString ( 'base64' ) } ,
207
- { Authorization : `Bearer ${ mockAccessToken } ` } , undefined ) ;
208
+ expect ( stub ) . to . have . been . calledTwice ;
209
+ expect ( stub . getCall ( 0 ) . args [ 0 ] ) . to . deep . equal ( metadataRequest ) ;
210
+ expect ( stub . getCall ( 1 ) . args [ 0 ] ) . to . deep . equal ( signRequest ) ;
208
211
} ) ;
209
212
} ) ;
210
213
211
214
it ( 'should fail if the IAM service responds with an error' , ( ) => {
212
- stub = sinon . stub ( HttpRequestHandler . prototype , 'sendRequest' )
213
- . throws ( {
214
- statusCode : 500 ,
215
- error : { error : { status : 'PROJECT_NOT_FOUND' , message : 'test reason' } } ,
216
- } ) ;
217
- const requestHandler = new SignedApiRequestHandler ( mockApp ) ;
215
+ const expectedResult = {
216
+ error : {
217
+ status : 'PROJECT_NOT_FOUND' ,
218
+ message : 'test reason' ,
219
+ } ,
220
+ } ;
221
+ stub = sinon . stub ( HttpClient . prototype , 'send' ) ;
222
+ stub . onCall ( 0 ) . resolves ( utils . responseFrom ( 'discovered-service-account' ) ) ;
223
+ stub . onCall ( 1 ) . rejects ( utils . errorFrom ( expectedResult ) ) ;
224
+ const requestHandler = new AuthorizedHttpClient ( mockApp ) ;
218
225
const signer = new IAMSigner ( requestHandler ) ;
219
226
return signer . sign ( input ) . catch ( ( err ) => {
220
227
expect ( err . message ) . to . equal ( 'test reason' ) ;
221
- expect ( stub ) . to . have . been . calledOnce . and . calledWith (
222
- 'iam.googleapis.com' , 443 ,
223
- '/v1/projects/-/serviceAccounts/discovered-service-account:signBlob' ,
224
- 'POST' , { bytesToSign : input . toString ( 'base64' ) } ,
225
- { Authorization : `Bearer ${ mockAccessToken } ` } , undefined ) ;
228
+ expect ( stub ) . to . have . been . calledTwice ;
229
+ expect ( stub . getCall ( 0 ) . args [ 0 ] ) . to . deep . equal ( metadataRequest ) ;
230
+ expect ( stub . getCall ( 1 ) . args [ 0 ] ) . to . deep . equal ( signRequest ) ;
226
231
} ) ;
227
232
} ) ;
228
233
229
234
it ( 'should return the discovered service account' , ( ) => {
230
- const signer = new IAMSigner ( new SignedApiRequestHandler ( mockApp ) ) ;
235
+ stub = sinon . stub ( HttpClient . prototype , 'send' ) ;
236
+ stub . onCall ( 0 ) . resolves ( utils . responseFrom ( 'discovered-service-account' ) ) ;
237
+ const signer = new IAMSigner ( new AuthorizedHttpClient ( mockApp ) ) ;
231
238
return signer . getAccount ( ) . should . eventually . equal ( 'discovered-service-account' ) ;
232
239
} ) ;
233
240
} ) ;
0 commit comments