@@ -24,6 +24,7 @@ module.exports = {
2424 validateHandlerProperty ( funcObject , functionName ) ;
2525 validateEventsProperty ( funcObject , functionName ) ;
2626 validateVpcConnectorProperty ( funcObject , functionName ) ;
27+ validateIamProperty ( funcObject , functionName ) ;
2728
2829 const funcTemplate = getFunctionTemplate (
2930 funcObject ,
@@ -51,6 +52,11 @@ module.exports = {
5152 _ . get ( this , 'serverless.service.provider.environment' ) ,
5253 funcObject . environment // eslint-disable-line comma-dangle
5354 ) ;
55+ funcTemplate . accessControl . gcpIamPolicy . bindings = _ . unionBy (
56+ _ . get ( funcObject , 'iam.bindings' ) ,
57+ _ . get ( this , 'serverless.service.provider.iam.bindings' ) ,
58+ 'role'
59+ ) ;
5460
5561 if ( ! funcTemplate . properties . serviceAccountEmail ) {
5662 delete funcTemplate . properties . serviceAccountEmail ;
@@ -83,6 +89,14 @@ module.exports = {
8389
8490 funcTemplate . properties . httpsTrigger = { } ;
8591 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+ }
86100 }
87101 if ( eventType === 'event' ) {
88102 const type = funcObject . events [ 0 ] . event . eventType ;
@@ -95,6 +109,10 @@ module.exports = {
95109 funcTemplate . properties . eventTrigger . resource = resource ;
96110 }
97111
112+ if ( ! funcTemplate . accessControl . gcpIamPolicy . bindings . length ) {
113+ delete funcTemplate . accessControl ;
114+ }
115+
98116 this . serverless . service . provider . compiledConfigurationTemplate . resources . push ( funcTemplate ) ;
99117 } ) ;
100118
@@ -157,6 +175,29 @@ const validateVpcConnectorProperty = (funcObject, functionName) => {
157175 }
158176} ;
159177
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+
160201const getFunctionTemplate = ( funcObject , projectName , region , sourceArchiveUrl ) => {
161202 //eslint-disable-line
162203 return {
@@ -171,5 +212,10 @@ const getFunctionTemplate = (funcObject, projectName, region, sourceArchiveUrl)
171212 function : funcObject . name ,
172213 sourceArchiveUrl,
173214 } ,
215+ accessControl : {
216+ gcpIamPolicy : {
217+ bindings : [ ] ,
218+ } ,
219+ } ,
174220 } ;
175221} ;
0 commit comments