@@ -150,6 +150,74 @@ public async Task InvokeAdminLevelFunction_WithoutMasterKey_ReturnsUnauthorized(
150
150
}
151
151
}
152
152
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
+
153
221
[ Fact ]
154
222
public async Task ExtensionWebHook_Succeeds ( )
155
223
{
@@ -641,7 +709,7 @@ await TestHelpers.RunWithTimeoutAsync(async () =>
641
709
await VerifyOfflineResponse ( response ) ;
642
710
643
711
// verify the same thing when invoking via admin api
644
- response = await AdminInvokeFunction ( functionName ) ;
712
+ response = await AdminInvokeFunctionMasterKey ( functionName ) ;
645
713
await VerifyOfflineResponse ( response ) ;
646
714
647
715
// bring host back online
@@ -656,7 +724,7 @@ await TestHelpers.RunWithTimeoutAsync(async () =>
656
724
await SamplesTestHelpers . InvokeAndValidateHttpTrigger ( _fixture , functionName ) ;
657
725
658
726
// verify the same thing via admin api
659
- response = await AdminInvokeFunction ( functionName ) ;
727
+ response = await AdminInvokeFunctionMasterKey ( functionName ) ;
660
728
Assert . Equal ( HttpStatusCode . Accepted , response . StatusCode ) ;
661
729
}
662
730
@@ -698,7 +766,7 @@ public async Task ListFunctions_Succeeds()
698
766
var response = await _fixture . Host . HttpClient . SendAsync ( request ) ;
699
767
var metadata = ( await response . Content . ReadAsAsync < IEnumerable < FunctionMetadataResponse > > ( ) ) . ToArray ( ) ;
700
768
701
- Assert . Equal ( 18 , metadata . Length ) ;
769
+ Assert . Equal ( 19 , metadata . Length ) ;
702
770
var function = metadata . Single ( p => p . Name == "HttpTrigger-CustomRoute" ) ;
703
771
Assert . Equal ( "https://somewebsite.azurewebsites.net/api/csharp/products/{category:alpha?}/{id:int?}/{extra?}" , function . InvokeUrlTemplate . ToString ( ) ) ;
704
772
@@ -865,8 +933,18 @@ public async Task HttpTrigger_Poco_Get_Succeeds()
865
933
}
866
934
}
867
935
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
+
868
946
// 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 )
870
948
{
871
949
string masterKey = await _fixture . Host . GetMasterKeyAsync ( ) ;
872
950
string uri = $ "admin/functions/{ functionName } ?code={ masterKey } ";
@@ -880,6 +958,32 @@ private async Task<HttpResponseMessage> AdminInvokeFunction(string functionName,
880
958
return await _fixture . Host . HttpClient . SendAsync ( request ) ;
881
959
}
882
960
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
+
883
987
[ Fact ]
884
988
[ Trait ( TestTraits . Group , TestTraits . AdminIsolationTests ) ]
885
989
public async Task HttpTrigger_AdminLevel_AdminIsolationEnabled_Succeeds ( )
@@ -1219,6 +1323,7 @@ public override void ConfigureScriptHost(IWebJobsBuilder webJobsBuilder)
1219
1323
o . Functions = new [ ]
1220
1324
{
1221
1325
"HttpTrigger" ,
1326
+ "HttpTrigger-AnonymousLevel" ,
1222
1327
"HttpTrigger-AdminLevel" ,
1223
1328
"HttpTrigger-Compat" ,
1224
1329
"HttpTrigger-CustomRoute" ,
0 commit comments