Skip to content

Commit 5dfe8cc

Browse files
authored
Support dynamic OpenApiHttpTriggerAuthorization (#435)
1 parent 9072106 commit 5dfe8cc

File tree

17 files changed

+783
-592
lines changed

17 files changed

+783
-592
lines changed

docs/openapi-core.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,6 @@ Suppose you want to customise the look and feels of the Swagger UI page. In this
261261
}
262262
```
263263

264-
265264
### Use CSS and JavaScript Files from CDN ###
266265

267266
Alternatively, you can use both CSS and JavaScript files from CDN, which is from the Internet.

docs/openapi-in-proc.md

Lines changed: 52 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -45,37 +45,59 @@ namespace MyFunctionApp
4545
{
4646
public override void Configure(IFunctionsHostBuilder builder)
4747
{
48+
/* ⬇️⬇️⬇️ Add this ⬇️⬇️⬇️ */
4849
builder.Services.AddSingleton<IOpenApiConfigurationOptions>(_ =>
49-
{
50-
var options = new OpenApiConfigurationOptions()
51-
{
52-
Info = new OpenApiInfo()
53-
{
54-
Version = "1.0.0",
55-
Title = "Swagger Petstore",
56-
Description = "This is a sample server Petstore API designed by [http://swagger.io](http://swagger.io).",
57-
TermsOfService = new Uri("https://github.com/Azure/azure-functions-openapi-extension"),
58-
Contact = new OpenApiContact()
59-
{
60-
Name = "Enquiry",
61-
Email = "[email protected]",
62-
Url = new Uri("https://github.com/Azure/azure-functions-openapi-extension/issues"),
63-
},
64-
License = new OpenApiLicense()
65-
{
66-
Name = "MIT",
67-
Url = new Uri("http://opensource.org/licenses/MIT"),
68-
}
69-
},
70-
Servers = DefaultOpenApiConfigurationOptions.GetHostNames(),
71-
OpenApiVersion = OpenApiVersionType.V2,
72-
IncludeRequestingHostName = true,
73-
ForceHttps = false,
74-
ForceHttp = false,
75-
};
76-
77-
return options;
78-
});
50+
{
51+
var options = new OpenApiConfigurationOptions()
52+
{
53+
Info = new OpenApiInfo()
54+
{
55+
Version = "1.0.0",
56+
Title = "Swagger Petstore",
57+
Description = "This is a sample server Petstore API designed by [http://swagger.io](http://swagger.io).",
58+
TermsOfService = new Uri("https://github.com/Azure/azure-functions-openapi-extension"),
59+
Contact = new OpenApiContact()
60+
{
61+
Name = "Enquiry",
62+
Email = "[email protected]",
63+
Url = new Uri("https://github.com/Azure/azure-functions-openapi-extension/issues"),
64+
},
65+
License = new OpenApiLicense()
66+
{
67+
Name = "MIT",
68+
Url = new Uri("http://opensource.org/licenses/MIT"),
69+
}
70+
},
71+
Servers = DefaultOpenApiConfigurationOptions.GetHostNames(),
72+
OpenApiVersion = OpenApiVersionType.V2,
73+
IncludeRequestingHostName = true,
74+
ForceHttps = false,
75+
ForceHttp = false,
76+
};
77+
78+
return options;
79+
});
80+
/* ⬆️⬆️⬆️ Add this ⬆️⬆️⬆️ */
81+
}
82+
}
83+
}
84+
```
85+
86+
### Injecting `OpenApiHttpTriggerAuthorization` during Startup ###
87+
88+
You may want to inject the `OpenApiHttpTriggerAuthorization` instance during startup, through the `Startup.cs` class. Here's the example:
89+
90+
```csharp
91+
[assembly: FunctionsStartup(typeof(MyFunctionApp.Startup))]
92+
namespace MyFunctionApp
93+
{
94+
public class Startup : FunctionsStartup
95+
{
96+
public override void Configure(IFunctionsHostBuilder builder)
97+
{
98+
/* ⬇️⬇️⬇️ Add this ⬇️⬇️⬇️ */
99+
builder.Services.AddSingleton<IOpenApiHttpTriggerAuthorization, MyOpenApiHttpTriggerAuthorization>();
100+
/* ⬆️⬆️⬆️ Add this ⬆️⬆️⬆️ */
79101
}
80102
}
81103
}

docs/openapi-out-of-proc.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,33 @@ namespace MyFunctionApp
8484
}
8585
}
8686
```
87+
88+
### Injecting `OpenApiHttpTriggerAuthorization` during Startup ###
89+
90+
You may want to inject the `OpenApiHttpTriggerAuthorization` instance during startup, through the `Program.cs` class. Here's the example:
91+
92+
```csharp
93+
namespace MyFunctionApp
94+
{
95+
public class Program
96+
{
97+
public static void Main()
98+
{
99+
var host = new HostBuilder()
100+
.ConfigureFunctionsWorkerDefaults(worker => worker.UseNewtonsoftJson())
101+
.ConfigureOpenApi()
102+
/* ⬇️⬇️⬇️ Add this ⬇️⬇️⬇️ */
103+
.ConfigureServices(services =>
104+
{
105+
services.AddSingleton<IOpenApiHttpTriggerAuthorization, MyOpenApiHttpTriggerAuthorization>();
106+
})
107+
/* ⬆️⬆️⬆️ Add this ⬆️⬆️⬆️ */
108+
.Build();
109+
110+
host.Run();
111+
}
112+
}
113+
}
114+
```
115+
116+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System.Threading.Tasks;
2+
3+
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
4+
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
5+
6+
namespace Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.OutOfProc.Configurations
7+
{
8+
public class MyOpenApiHttpTriggerAuthorization : DefaultOpenApiHttpTriggerAuthorization
9+
{
10+
public override async Task<OpenApiAuthorizationResult> AuthorizeAsync(IHttpRequestDataObject req)
11+
{
12+
var result = default(OpenApiAuthorizationResult);
13+
14+
// Put your custom logic here!
15+
16+
return await Task.FromResult(result).ConfigureAwait(false);
17+
}
18+
}
19+
}

samples/Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.OutOfProc/Program.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using AutoFixture;
44

55
using Microsoft.Azure.Functions.Worker.Extensions.OpenApi.Extensions;
6+
using Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.OutOfProc.Configurations;
67
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
78
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
89
using Microsoft.Extensions.DependencyInjection;
@@ -52,7 +53,7 @@ public static void Main()
5253

5354
return options;
5455
})
55-
;
56+
.AddSingleton<IOpenApiHttpTriggerAuthorization, MyOpenApiHttpTriggerAuthorization>();
5657
})
5758
.Build();
5859

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using System.Globalization;
2+
using System.Linq;
3+
using System.Net;
4+
using System.Threading.Tasks;
5+
6+
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
7+
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
8+
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Extensions;
9+
10+
namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.InProc.Configurations
11+
{
12+
public class MyOpenApiHttpTriggerAuthorization : DefaultOpenApiHttpTriggerAuthorization
13+
{
14+
public override async Task<OpenApiAuthorizationResult> AuthorizeAsync(IHttpRequestDataObject req)
15+
{
16+
var result = default(OpenApiAuthorizationResult);
17+
var authtoken = (string)req.Headers["Authorization"];
18+
if (authtoken.IsNullOrWhiteSpace())
19+
{
20+
result = new OpenApiAuthorizationResult()
21+
{
22+
StatusCode = HttpStatusCode.Unauthorized,
23+
ContentType = "text/plain",
24+
Payload = "Unauthorized",
25+
};
26+
27+
return await Task.FromResult(result).ConfigureAwait(false);
28+
}
29+
30+
if (authtoken.StartsWith("Bearer", ignoreCase: true, CultureInfo.InvariantCulture) == false)
31+
{
32+
result = new OpenApiAuthorizationResult()
33+
{
34+
StatusCode = HttpStatusCode.Unauthorized,
35+
ContentType = "text/plain",
36+
Payload = "Invalid auth format",
37+
};
38+
39+
return await Task.FromResult(result).ConfigureAwait(false);
40+
}
41+
42+
var token = authtoken.Split(' ').Last();
43+
if (token != "secret")
44+
{
45+
result = new OpenApiAuthorizationResult()
46+
{
47+
StatusCode = HttpStatusCode.Forbidden,
48+
ContentType = "text/plain",
49+
Payload = "Invalid auth token",
50+
};
51+
52+
return await Task.FromResult(result).ConfigureAwait(false);
53+
}
54+
55+
return await Task.FromResult(result).ConfigureAwait(false);
56+
}
57+
}
58+
}

samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.InProc/Configurations/OpenApiHttpTriggerAuthorization.cs

Lines changed: 0 additions & 58 deletions
This file was deleted.

samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.InProc/Startup.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
66
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
77
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
8+
using Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.InProc.Configurations;
89
using Microsoft.Extensions.DependencyInjection;
910
using Microsoft.OpenApi.Models;
1011

@@ -48,7 +49,8 @@ public override void Configure(IFunctionsHostBuilder builder)
4849

4950
return options;
5051
})
51-
;
52+
.AddSingleton<IOpenApiHttpTriggerAuthorization, MyOpenApiHttpTriggerAuthorization>();
53+
5254
}
5355
}
5456
}

src/Microsoft.Azure.Functions.Worker.Extensions.OpenApi/Extensions/OpenApiHostBuilderExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public static IHostBuilder ConfigureOpenApi(this IHostBuilder hostBuilder)
2121
hostBuilder.ConfigureServices(services =>
2222
{
2323
services.AddSingleton<IOpenApiHttpTriggerContext, OpenApiHttpTriggerContext>();
24-
services.AddSingleton<IOpenApiTriggerFunction, OpenApiTriggerFunction>();
24+
services.AddSingleton<IOpenApiTriggerFunction, OpenApiTriggerFunction>();
2525
// services.AddSingleton<DefaultOpenApiHttpTrigger, DefaultOpenApiHttpTrigger>();
2626
});
2727

src/Microsoft.Azure.Functions.Worker.Extensions.OpenApi/OpenApiHttpTriggerContext.cs

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,18 @@ public class OpenApiHttpTriggerContext : IOpenApiHttpTriggerContext
3636
private Assembly _appAssembly;
3737
private IOpenApiConfigurationOptions _configOptions;
3838
private IOpenApiCustomUIOptions _uiOptions;
39+
private IOpenApiHttpTriggerAuthorization _httpTriggerAuthorization;
3940

4041
/// <summary>
4142
/// Initializes a new instance of the <see cref="OpenApiHttpTriggerContext"/> class.
4243
/// </summary>
4344
/// <param name="configOptions"><see cref="IOpenApiConfigurationOptions"/> instance.</param>
44-
public OpenApiHttpTriggerContext(IOpenApiConfigurationOptions configOptions = null)
45+
/// <param name="httpTriggerAuthorization"><see cref="IOpenApiHttpTriggerAuthorization"/> instance.</param>
46+
public OpenApiHttpTriggerContext(IOpenApiConfigurationOptions configOptions = null, IOpenApiHttpTriggerAuthorization httpTriggerAuthorization = null)
4547
{
4648
this._configOptions = configOptions;
49+
this._httpTriggerAuthorization = httpTriggerAuthorization;
50+
4751
this.PackageAssembly = this.GetAssembly<ISwaggerUI>();
4852

4953
var host = HostJsonResolver.Resolve();
@@ -102,6 +106,20 @@ public virtual IOpenApiCustomUIOptions OpenApiCustomUIOptions
102106
}
103107
}
104108

109+
/// <inheritdoc />
110+
public virtual IOpenApiHttpTriggerAuthorization OpenApiHttpTriggerAuthorization
111+
{
112+
get
113+
{
114+
if (this._httpTriggerAuthorization.IsNullOrDefault())
115+
{
116+
this._httpTriggerAuthorization = OpenApiHttpTriggerAuthorizationResolver.Resolve(this.ApplicationAssembly);
117+
}
118+
119+
return this._httpTriggerAuthorization;
120+
}
121+
}
122+
105123
/// <inheritdoc />
106124
public virtual HttpSettings HttpSettings { get; }
107125

@@ -146,19 +164,7 @@ public virtual async Task<IOpenApiHttpTriggerContext> SetApplicationAssemblyAsyn
146164
/// <inheritdoc />
147165
public virtual async Task<OpenApiAuthorizationResult> AuthorizeAsync(IHttpRequestDataObject req)
148166
{
149-
var result = default(OpenApiAuthorizationResult);
150-
var type = this.ApplicationAssembly
151-
.GetLoadableTypes()
152-
.SingleOrDefault(p => p.HasInterface<IOpenApiHttpTriggerAuthorization>());
153-
if (type.IsNullOrDefault())
154-
{
155-
return result;
156-
}
157-
158-
var auth = Activator.CreateInstance(type) as IOpenApiHttpTriggerAuthorization;
159-
result = await auth.AuthorizeAsync(req).ConfigureAwait(false);
160-
161-
return result;
167+
return await this.OpenApiHttpTriggerAuthorization.AuthorizeAsync(req).ConfigureAwait(false);
162168
}
163169

164170
/// <inheritdoc />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System;
2+
3+
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
4+
5+
namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Attributes
6+
{
7+
/// <summary>
8+
/// This represents the attribute entity for <see cref="IOpenApiConfigurationOptions"/> to be excluded from auto-loading.
9+
/// </summary>
10+
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
11+
public class OpenApiHttpTriggerAuthorizationIgnoreAttribute : Attribute
12+
{
13+
}
14+
}

0 commit comments

Comments
 (0)