Skip to content

Commit

Permalink
Support dynamic OpenApiHttpTriggerAuthorization (#435)
Browse files Browse the repository at this point in the history
  • Loading branch information
jackbatzner authored Aug 22, 2022
1 parent 9072106 commit 5dfe8cc
Show file tree
Hide file tree
Showing 17 changed files with 783 additions and 592 deletions.
1 change: 0 additions & 1 deletion docs/openapi-core.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,6 @@ Suppose you want to customise the look and feels of the Swagger UI page. In this
}
```


### Use CSS and JavaScript Files from CDN ###

Alternatively, you can use both CSS and JavaScript files from CDN, which is from the Internet.
Expand Down
82 changes: 52 additions & 30 deletions docs/openapi-in-proc.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,37 +45,59 @@ namespace MyFunctionApp
{
public override void Configure(IFunctionsHostBuilder builder)
{
/* ⬇️⬇️⬇️ Add this ⬇️⬇️⬇️ */
builder.Services.AddSingleton<IOpenApiConfigurationOptions>(_ =>
{
var options = new OpenApiConfigurationOptions()
{
Info = new OpenApiInfo()
{
Version = "1.0.0",
Title = "Swagger Petstore",
Description = "This is a sample server Petstore API designed by [http://swagger.io](http://swagger.io).",
TermsOfService = new Uri("https://github.com/Azure/azure-functions-openapi-extension"),
Contact = new OpenApiContact()
{
Name = "Enquiry",
Email = "[email protected]",
Url = new Uri("https://github.com/Azure/azure-functions-openapi-extension/issues"),
},
License = new OpenApiLicense()
{
Name = "MIT",
Url = new Uri("http://opensource.org/licenses/MIT"),
}
},
Servers = DefaultOpenApiConfigurationOptions.GetHostNames(),
OpenApiVersion = OpenApiVersionType.V2,
IncludeRequestingHostName = true,
ForceHttps = false,
ForceHttp = false,
};

return options;
});
{
var options = new OpenApiConfigurationOptions()
{
Info = new OpenApiInfo()
{
Version = "1.0.0",
Title = "Swagger Petstore",
Description = "This is a sample server Petstore API designed by [http://swagger.io](http://swagger.io).",
TermsOfService = new Uri("https://github.com/Azure/azure-functions-openapi-extension"),
Contact = new OpenApiContact()
{
Name = "Enquiry",
Email = "[email protected]",
Url = new Uri("https://github.com/Azure/azure-functions-openapi-extension/issues"),
},
License = new OpenApiLicense()
{
Name = "MIT",
Url = new Uri("http://opensource.org/licenses/MIT"),
}
},
Servers = DefaultOpenApiConfigurationOptions.GetHostNames(),
OpenApiVersion = OpenApiVersionType.V2,
IncludeRequestingHostName = true,
ForceHttps = false,
ForceHttp = false,
};

return options;
});
/* ⬆️⬆️⬆️ Add this ⬆️⬆️⬆️ */
}
}
}
```

### Injecting `OpenApiHttpTriggerAuthorization` during Startup ###

You may want to inject the `OpenApiHttpTriggerAuthorization` instance during startup, through the `Startup.cs` class. Here's the example:

```csharp
[assembly: FunctionsStartup(typeof(MyFunctionApp.Startup))]
namespace MyFunctionApp
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
/* ⬇️⬇️⬇️ Add this ⬇️⬇️⬇️ */
builder.Services.AddSingleton<IOpenApiHttpTriggerAuthorization, MyOpenApiHttpTriggerAuthorization>();
/* ⬆️⬆️⬆️ Add this ⬆️⬆️⬆️ */
}
}
}
Expand Down
30 changes: 30 additions & 0 deletions docs/openapi-out-of-proc.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,33 @@ namespace MyFunctionApp
}
}
```

### Injecting `OpenApiHttpTriggerAuthorization` during Startup ###

You may want to inject the `OpenApiHttpTriggerAuthorization` instance during startup, through the `Program.cs` class. Here's the example:

```csharp
namespace MyFunctionApp
{
public class Program
{
public static void Main()
{
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults(worker => worker.UseNewtonsoftJson())
.ConfigureOpenApi()
/* ⬇️⬇️⬇️ Add this ⬇️⬇️⬇️ */
.ConfigureServices(services =>
{
services.AddSingleton<IOpenApiHttpTriggerAuthorization, MyOpenApiHttpTriggerAuthorization>();
})
/* ⬆️⬆️⬆️ Add this ⬆️⬆️⬆️ */
.Build();

host.Run();
}
}
}
```


Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Threading.Tasks;

using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;

namespace Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.OutOfProc.Configurations
{
public class MyOpenApiHttpTriggerAuthorization : DefaultOpenApiHttpTriggerAuthorization
{
public override async Task<OpenApiAuthorizationResult> AuthorizeAsync(IHttpRequestDataObject req)
{
var result = default(OpenApiAuthorizationResult);

// Put your custom logic here!

return await Task.FromResult(result).ConfigureAwait(false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using AutoFixture;

using Microsoft.Azure.Functions.Worker.Extensions.OpenApi.Extensions;
using Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.OutOfProc.Configurations;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -52,7 +53,7 @@ public static void Main()
return options;
})
;
.AddSingleton<IOpenApiHttpTriggerAuthorization, MyOpenApiHttpTriggerAuthorization>();
})
.Build();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System.Globalization;
using System.Linq;
using System.Net;
using System.Threading.Tasks;

using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Extensions;

namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.InProc.Configurations
{
public class MyOpenApiHttpTriggerAuthorization : DefaultOpenApiHttpTriggerAuthorization
{
public override async Task<OpenApiAuthorizationResult> AuthorizeAsync(IHttpRequestDataObject req)
{
var result = default(OpenApiAuthorizationResult);
var authtoken = (string)req.Headers["Authorization"];
if (authtoken.IsNullOrWhiteSpace())
{
result = new OpenApiAuthorizationResult()
{
StatusCode = HttpStatusCode.Unauthorized,
ContentType = "text/plain",
Payload = "Unauthorized",
};

return await Task.FromResult(result).ConfigureAwait(false);
}

if (authtoken.StartsWith("Bearer", ignoreCase: true, CultureInfo.InvariantCulture) == false)
{
result = new OpenApiAuthorizationResult()
{
StatusCode = HttpStatusCode.Unauthorized,
ContentType = "text/plain",
Payload = "Invalid auth format",
};

return await Task.FromResult(result).ConfigureAwait(false);
}

var token = authtoken.Split(' ').Last();
if (token != "secret")
{
result = new OpenApiAuthorizationResult()
{
StatusCode = HttpStatusCode.Forbidden,
ContentType = "text/plain",
Payload = "Invalid auth token",
};

return await Task.FromResult(result).ConfigureAwait(false);
}

return await Task.FromResult(result).ConfigureAwait(false);
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.InProc.Configurations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;

Expand Down Expand Up @@ -48,7 +49,8 @@ public override void Configure(IFunctionsHostBuilder builder)
return options;
})
;
.AddSingleton<IOpenApiHttpTriggerAuthorization, MyOpenApiHttpTriggerAuthorization>();

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public static IHostBuilder ConfigureOpenApi(this IHostBuilder hostBuilder)
hostBuilder.ConfigureServices(services =>
{
services.AddSingleton<IOpenApiHttpTriggerContext, OpenApiHttpTriggerContext>();
services.AddSingleton<IOpenApiTriggerFunction, OpenApiTriggerFunction>();
services.AddSingleton<IOpenApiTriggerFunction, OpenApiTriggerFunction>();
// services.AddSingleton<DefaultOpenApiHttpTrigger, DefaultOpenApiHttpTrigger>();
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,18 @@ public class OpenApiHttpTriggerContext : IOpenApiHttpTriggerContext
private Assembly _appAssembly;
private IOpenApiConfigurationOptions _configOptions;
private IOpenApiCustomUIOptions _uiOptions;
private IOpenApiHttpTriggerAuthorization _httpTriggerAuthorization;

/// <summary>
/// Initializes a new instance of the <see cref="OpenApiHttpTriggerContext"/> class.
/// </summary>
/// <param name="configOptions"><see cref="IOpenApiConfigurationOptions"/> instance.</param>
public OpenApiHttpTriggerContext(IOpenApiConfigurationOptions configOptions = null)
/// <param name="httpTriggerAuthorization"><see cref="IOpenApiHttpTriggerAuthorization"/> instance.</param>
public OpenApiHttpTriggerContext(IOpenApiConfigurationOptions configOptions = null, IOpenApiHttpTriggerAuthorization httpTriggerAuthorization = null)
{
this._configOptions = configOptions;
this._httpTriggerAuthorization = httpTriggerAuthorization;

this.PackageAssembly = this.GetAssembly<ISwaggerUI>();

var host = HostJsonResolver.Resolve();
Expand Down Expand Up @@ -102,6 +106,20 @@ public virtual IOpenApiCustomUIOptions OpenApiCustomUIOptions
}
}

/// <inheritdoc />
public virtual IOpenApiHttpTriggerAuthorization OpenApiHttpTriggerAuthorization
{
get
{
if (this._httpTriggerAuthorization.IsNullOrDefault())
{
this._httpTriggerAuthorization = OpenApiHttpTriggerAuthorizationResolver.Resolve(this.ApplicationAssembly);
}

return this._httpTriggerAuthorization;
}
}

/// <inheritdoc />
public virtual HttpSettings HttpSettings { get; }

Expand Down Expand Up @@ -146,19 +164,7 @@ public virtual async Task<IOpenApiHttpTriggerContext> SetApplicationAssemblyAsyn
/// <inheritdoc />
public virtual async Task<OpenApiAuthorizationResult> AuthorizeAsync(IHttpRequestDataObject req)
{
var result = default(OpenApiAuthorizationResult);
var type = this.ApplicationAssembly
.GetLoadableTypes()
.SingleOrDefault(p => p.HasInterface<IOpenApiHttpTriggerAuthorization>());
if (type.IsNullOrDefault())
{
return result;
}

var auth = Activator.CreateInstance(type) as IOpenApiHttpTriggerAuthorization;
result = await auth.AuthorizeAsync(req).ConfigureAwait(false);

return result;
return await this.OpenApiHttpTriggerAuthorization.AuthorizeAsync(req).ConfigureAwait(false);
}

/// <inheritdoc />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;

using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;

namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Attributes
{
/// <summary>
/// This represents the attribute entity for <see cref="IOpenApiConfigurationOptions"/> to be excluded from auto-loading.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class OpenApiHttpTriggerAuthorizationIgnoreAttribute : Attribute
{
}
}
Loading

0 comments on commit 5dfe8cc

Please sign in to comment.