@@ -24,6 +24,7 @@ module.exports = {
24
24
validateHandlerProperty ( funcObject , functionName ) ;
25
25
validateEventsProperty ( funcObject , functionName ) ;
26
26
validateVpcConnectorProperty ( funcObject , functionName ) ;
27
+ validateIamProperty ( funcObject , functionName ) ;
27
28
28
29
const funcTemplate = getFunctionTemplate (
29
30
funcObject ,
@@ -51,6 +52,11 @@ module.exports = {
51
52
_ . get ( this , 'serverless.service.provider.environment' ) ,
52
53
funcObject . environment // eslint-disable-line comma-dangle
53
54
) ;
55
+ funcTemplate . accessControl . gcpIamPolicy . bindings = _ . unionBy (
56
+ _ . get ( funcObject , 'iam.bindings' ) ,
57
+ _ . get ( this , 'serverless.service.provider.iam.bindings' ) ,
58
+ 'role'
59
+ ) ;
54
60
55
61
if ( ! funcTemplate . properties . serviceAccountEmail ) {
56
62
delete funcTemplate . properties . serviceAccountEmail ;
@@ -83,6 +89,14 @@ module.exports = {
83
89
84
90
funcTemplate . properties . httpsTrigger = { } ;
85
91
funcTemplate . properties . httpsTrigger . url = url ;
92
+
93
+ if ( funcObject . allowUnauthenticated ) {
94
+ funcTemplate . accessControl . gcpIamPolicy . bindings = _ . unionBy (
95
+ [ { role : 'roles/cloudfunctions.invoker' , members : [ 'allUsers' ] } ] ,
96
+ funcTemplate . accessControl . gcpIamPolicy . bindings ,
97
+ 'role'
98
+ ) ;
99
+ }
86
100
}
87
101
if ( eventType === 'event' ) {
88
102
const type = funcObject . events [ 0 ] . event . eventType ;
@@ -95,6 +109,10 @@ module.exports = {
95
109
funcTemplate . properties . eventTrigger . resource = resource ;
96
110
}
97
111
112
+ if ( ! funcTemplate . accessControl . gcpIamPolicy . bindings . length ) {
113
+ delete funcTemplate . accessControl ;
114
+ }
115
+
98
116
this . serverless . service . provider . compiledConfigurationTemplate . resources . push ( funcTemplate ) ;
99
117
} ) ;
100
118
@@ -157,6 +175,29 @@ const validateVpcConnectorProperty = (funcObject, functionName) => {
157
175
}
158
176
} ;
159
177
178
+ const validateIamProperty = ( funcObject , functionName ) => {
179
+ if ( _ . get ( funcObject , 'iam.bindings' ) && funcObject . iam . bindings . length > 0 ) {
180
+ funcObject . iam . bindings . forEach ( ( binding ) => {
181
+ if ( ! binding . role ) {
182
+ const errorMessage = [
183
+ `The function "${ functionName } " has no role specified for an IAM binding.` ,
184
+ ' Each binding requires a role. For details on supported roles, see the documentation' ,
185
+ ' at: https://cloud.google.com/iam/docs/understanding-roles' ,
186
+ ] . join ( '' ) ;
187
+ throw new Error ( errorMessage ) ;
188
+ }
189
+ if ( ! Array . isArray ( binding . members ) || ! binding . members . length ) {
190
+ const errorMessage = [
191
+ `The function "${ functionName } " has no members specified for an IAM binding.` ,
192
+ ' Each binding requires at least one member to be assigned. See the IAM documentation' ,
193
+ ' for details on configuring members: https://cloud.google.com/iam/docs/overview' ,
194
+ ] . join ( '' ) ;
195
+ throw new Error ( errorMessage ) ;
196
+ }
197
+ } ) ;
198
+ }
199
+ } ;
200
+
160
201
const getFunctionTemplate = ( funcObject , projectName , region , sourceArchiveUrl ) => {
161
202
//eslint-disable-line
162
203
return {
@@ -171,5 +212,10 @@ const getFunctionTemplate = (funcObject, projectName, region, sourceArchiveUrl)
171
212
function : funcObject . name ,
172
213
sourceArchiveUrl,
173
214
} ,
215
+ accessControl : {
216
+ gcpIamPolicy : {
217
+ bindings : [ ] ,
218
+ } ,
219
+ } ,
174
220
} ;
175
221
} ;
0 commit comments