@@ -150,6 +150,74 @@ public async Task InvokeAdminLevelFunction_WithoutMasterKey_ReturnsUnauthorized(
150150 }
151151 }
152152
153+ [ Fact ]
154+ public async Task InvokeFunction_RequiresKeyOrNonPlatformToken ( )
155+ {
156+ // no key presented
157+ string uri = $ "api/httptrigger?name=Mathew";
158+ HttpRequestMessage request = new HttpRequestMessage ( HttpMethod . Get , uri ) ;
159+ HttpResponseMessage response = await _fixture . Host . HttpClient . SendAsync ( request ) ;
160+ Assert . Equal ( HttpStatusCode . Unauthorized , response . StatusCode ) ;
161+
162+ // required key supplied
163+ request = new HttpRequestMessage ( HttpMethod . Get , uri ) ;
164+ string key = await _fixture . Host . GetFunctionSecretAsync ( "httptrigger" ) ;
165+ request . Headers . Add ( AuthenticationLevelHandler . FunctionsKeyHeaderName , key ) ;
166+ response = await _fixture . Host . HttpClient . SendAsync ( request ) ;
167+ Assert . Equal ( HttpStatusCode . OK , response . StatusCode ) ;
168+
169+ // verify that even though a site token grants admin level access to
170+ // host APIs, it can't be used to invoke user functions
171+ using ( new TestScopedEnvironmentVariable ( EnvironmentSettingNames . WebSiteAuthEncryptionKey , TestHelpers . GenerateKeyHexString ( ) ) )
172+ {
173+ string swtToken = SimpleWebTokenHelper . CreateToken ( DateTime . UtcNow . AddMinutes ( 2 ) ) ;
174+
175+ // verify the token is valid by invoking an admin API
176+ request = new HttpRequestMessage ( HttpMethod . Get , "admin/host/status" ) ;
177+ request . Headers . Add ( ScriptConstants . SiteRestrictedTokenHeaderName , swtToken ) ;
178+ response = await _fixture . Host . HttpClient . SendAsync ( request ) ;
179+ Assert . Equal ( HttpStatusCode . OK , response . StatusCode ) ;
180+
181+ // verify it can't be used to invoke non-anonymous user functions
182+ request = new HttpRequestMessage ( HttpMethod . Get , uri ) ;
183+ request . Headers . Add ( ScriptConstants . SiteRestrictedTokenHeaderName , swtToken ) ;
184+ response = await _fixture . Host . HttpClient . SendAsync ( request ) ;
185+ Assert . Equal ( HttpStatusCode . Unauthorized , response . StatusCode ) ;
186+ }
187+
188+ // verify non-platform JWT token can be used to invoke non-anonymous user functions
189+ string jwtToken = _fixture . Host . GenerateAdminJwtToken ( ) ;
190+ request = new HttpRequestMessage ( HttpMethod . Get , uri ) ;
191+ request . Headers . Add ( ScriptConstants . SiteTokenHeaderName , jwtToken ) ;
192+ response = await _fixture . Host . HttpClient . SendAsync ( request ) ;
193+ Assert . Equal ( HttpStatusCode . OK , response . StatusCode ) ;
194+
195+ // verify platform JWT token can't be used to invoke non-anonymous user functions
196+ jwtToken = _fixture . Host . GenerateAdminJwtToken ( issuer : ScriptConstants . AppServiceCoreUri ) ;
197+ request = new HttpRequestMessage ( HttpMethod . Get , uri ) ;
198+ request . Headers . Add ( ScriptConstants . SiteTokenHeaderName , jwtToken ) ;
199+ response = await _fixture . Host . HttpClient . SendAsync ( request ) ;
200+ Assert . Equal ( HttpStatusCode . Unauthorized , response . StatusCode ) ;
201+ }
202+
203+ [ Fact ]
204+ public async Task InvokeFunction_AdminInvokeApi_Succeeds ( )
205+ {
206+ string functionName = "HttpTrigger" ;
207+
208+ // jwt token with site issuer
209+ var response = await AdminInvokeFunctionAdminToken ( functionName , true ) ;
210+ Assert . Equal ( HttpStatusCode . Accepted , response . StatusCode ) ;
211+
212+ // jwt token with platform issuer
213+ response = await AdminInvokeFunctionAdminToken ( functionName , true , issuer : ScriptConstants . AppServiceCoreUri ) ;
214+ Assert . Equal ( HttpStatusCode . Accepted , response . StatusCode ) ;
215+
216+ // swt token
217+ response = await AdminInvokeFunctionAdminToken ( functionName , false ) ;
218+ Assert . Equal ( HttpStatusCode . Accepted , response . StatusCode ) ;
219+ }
220+
153221 [ Fact ]
154222 public async Task ExtensionWebHook_Succeeds ( )
155223 {
@@ -641,7 +709,7 @@ await TestHelpers.RunWithTimeoutAsync(async () =>
641709 await VerifyOfflineResponse ( response ) ;
642710
643711 // verify the same thing when invoking via admin api
644- response = await AdminInvokeFunction ( functionName ) ;
712+ response = await AdminInvokeFunctionMasterKey ( functionName ) ;
645713 await VerifyOfflineResponse ( response ) ;
646714
647715 // bring host back online
@@ -656,7 +724,7 @@ await TestHelpers.RunWithTimeoutAsync(async () =>
656724 await SamplesTestHelpers . InvokeAndValidateHttpTrigger ( _fixture , functionName ) ;
657725
658726 // verify the same thing via admin api
659- response = await AdminInvokeFunction ( functionName ) ;
727+ response = await AdminInvokeFunctionMasterKey ( functionName ) ;
660728 Assert . Equal ( HttpStatusCode . Accepted , response . StatusCode ) ;
661729 }
662730
@@ -698,7 +766,7 @@ public async Task ListFunctions_Succeeds()
698766 var response = await _fixture . Host . HttpClient . SendAsync ( request ) ;
699767 var metadata = ( await response . Content . ReadAsAsync < IEnumerable < FunctionMetadataResponse > > ( ) ) . ToArray ( ) ;
700768
701- Assert . Equal ( 18 , metadata . Length ) ;
769+ Assert . Equal ( 19 , metadata . Length ) ;
702770 var function = metadata . Single ( p => p . Name == "HttpTrigger-CustomRoute" ) ;
703771 Assert . Equal ( "https://somewebsite.azurewebsites.net/api/csharp/products/{category:alpha?}/{id:int?}/{extra?}" , function . InvokeUrlTemplate . ToString ( ) ) ;
704772
@@ -865,8 +933,18 @@ public async Task HttpTrigger_Poco_Get_Succeeds()
865933 }
866934 }
867935
936+ [ Fact ]
937+ public async Task HttpTrigger_Anonymous_Get_Succeeds ( )
938+ {
939+ string uri = $ "api/httptrigger-anonymouslevel?name=Mathew";
940+ HttpRequestMessage request = new HttpRequestMessage ( HttpMethod . Get , uri ) ;
941+
942+ HttpResponseMessage response = await _fixture . Host . HttpClient . SendAsync ( request ) ;
943+ Assert . Equal ( HttpStatusCode . OK , response . StatusCode ) ;
944+ }
945+
868946 // invoke a function via the admin invoke api
869- private async Task < HttpResponseMessage > AdminInvokeFunction ( string functionName , string input = null )
947+ private async Task < HttpResponseMessage > AdminInvokeFunctionMasterKey ( string functionName , string input = null )
870948 {
871949 string masterKey = await _fixture . Host . GetMasterKeyAsync ( ) ;
872950 string uri = $ "admin/functions/{ functionName } ?code={ masterKey } ";
@@ -880,6 +958,32 @@ private async Task<HttpResponseMessage> AdminInvokeFunction(string functionName,
880958 return await _fixture . Host . HttpClient . SendAsync ( request ) ;
881959 }
882960
961+ private async Task < HttpResponseMessage > AdminInvokeFunctionAdminToken ( string functionName , bool jwt , string input = null , string issuer = null )
962+ {
963+
964+ string uri = $ "admin/functions/{ functionName } ";
965+ HttpRequestMessage request = new HttpRequestMessage ( HttpMethod . Post , uri ) ;
966+ JObject jo = new JObject
967+ {
968+ { "input" , input }
969+ } ;
970+ request . Content = new StringContent ( jo . ToString ( ) ) ;
971+ request . Content . Headers . ContentType = new MediaTypeHeaderValue ( "application/json" ) ;
972+
973+ if ( jwt )
974+ {
975+ string jwtToken = _fixture . Host . GenerateAdminJwtToken ( issuer : issuer ) ;
976+ request . Headers . Add ( ScriptConstants . SiteTokenHeaderName , jwtToken ) ;
977+ }
978+ else
979+ {
980+ string swtToken = SimpleWebTokenHelper . CreateToken ( DateTime . UtcNow . AddMinutes ( 2 ) ) ;
981+ request . Headers . Add ( ScriptConstants . SiteRestrictedTokenHeaderName , swtToken ) ;
982+ }
983+
984+ return await _fixture . Host . HttpClient . SendAsync ( request ) ;
985+ }
986+
883987 [ Fact ]
884988 [ Trait ( TestTraits . Group , TestTraits . AdminIsolationTests ) ]
885989 public async Task HttpTrigger_AdminLevel_AdminIsolationEnabled_Succeeds ( )
@@ -1219,6 +1323,7 @@ public override void ConfigureScriptHost(IWebJobsBuilder webJobsBuilder)
12191323 o . Functions = new [ ]
12201324 {
12211325 "HttpTrigger" ,
1326+ "HttpTrigger-AnonymousLevel" ,
12221327 "HttpTrigger-AdminLevel" ,
12231328 "HttpTrigger-Compat" ,
12241329 "HttpTrigger-CustomRoute" ,
0 commit comments