-
Notifications
You must be signed in to change notification settings - Fork 2.4k
/
Startup.cs
139 lines (119 loc) · 5.86 KB
/
Startup.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
using System;
using System.Threading.Tasks;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using Fluid;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using OrchardCore.Environment.Shell;
using OrchardCore.Environment.Shell.Configuration;
using OrchardCore.Modules;
namespace OrchardCore.DataProtection.Azure
{
public class Startup : StartupBase
{
private readonly ShellOptions _shellOptions;
private readonly ShellSettings _shellSettings;
private readonly ILogger _logger;
// Local instance since it can be discarded once the startup is over.
private readonly FluidParser _fluidParser = new();
public Startup(
IOptions<ShellOptions> shellOptions,
ShellSettings shellSettings,
ILogger<Startup> logger)
{
_shellOptions = shellOptions.Value;
_shellSettings = shellSettings;
_logger = logger;
}
public override void ConfigureServices(IServiceCollection services)
{
services.Initialize(async sp =>
{
var configuration = sp.GetRequiredService<IShellConfiguration>();
var connectionString = configuration.GetValue<string>("OrchardCore_DataProtection_Azure:ConnectionString");
if (!string.IsNullOrWhiteSpace(connectionString))
{
var containerName = await GetBlobContainerNameAsync(configuration, connectionString);
services.AddDataProtection()
.PersistKeysToAzureBlobStorage(connectionString, containerName, await GetBlobNameAsync(configuration));
}
else
{
_logger.LogCritical("No connection string was supplied for OrchardCore.DataProtection.Azure. Ensure that an application setting containing a valid Azure Storage connection string is available at `Modules:OrchardCore.DataProtection.Azure:ConnectionString`.");
}
});
}
private async Task<string> GetBlobContainerNameAsync(IShellConfiguration configuration, string connectionString)
{
var containerName = configuration.GetValue("OrchardCore_DataProtection_Azure:ContainerName", "dataprotection");
// Use Fluid directly as the service provider has not been built.
try
{
var templateOptions = new TemplateOptions();
templateOptions.MemberAccessStrategy.Register<ShellSettings>();
var templateContext = new TemplateContext(templateOptions);
templateContext.SetValue("ShellSettings", _shellSettings);
var template = _fluidParser.Parse(containerName);
// container name must be lowercase
containerName = template.Render(templateContext, NullEncoder.Default).ToLower();
containerName = containerName.Replace("\r", string.Empty).Replace("\n", string.Empty);
}
catch (Exception e)
{
_logger.LogCritical(e, "Unable to parse data protection connection string.");
throw;
}
var createContainer = configuration.GetValue("OrchardCore_DataProtection_Azure:CreateContainer", true);
if (createContainer)
{
try
{
_logger.LogDebug("Testing data protection container {ContainerName} existence", containerName);
var _blobContainer = new BlobContainerClient(connectionString, containerName);
var response = await _blobContainer.CreateIfNotExistsAsync(PublicAccessType.None);
_logger.LogDebug("Data protection container {ContainerName} created.", containerName);
}
catch (Exception)
{
_logger.LogCritical("Unable to connect to Azure Storage to configure data protection storage. Ensure that an application setting containing a valid Azure Storage connection string is available at `Modules:OrchardCore.DataProtection.Azure:ConnectionString`.");
throw;
}
}
return containerName;
}
private async Task<string> GetBlobNameAsync(IShellConfiguration configuration)
{
var blobName = configuration.GetValue<string>("OrchardCore_DataProtection_Azure:BlobName");
if (string.IsNullOrEmpty(blobName))
{
blobName = $"{_shellOptions.ShellsContainerName}/{_shellSettings.Name}/DataProtectionKeys.xml";
}
else
{
try
{
// Use Fluid directly as the service provider has not been built.
var templateOptions = new TemplateOptions();
var templateContext = new TemplateContext(templateOptions);
templateOptions.MemberAccessStrategy.Register<ShellSettings>();
templateContext.SetValue("ShellSettings", _shellSettings);
var template = _fluidParser.Parse(blobName);
blobName = await template.RenderAsync(templateContext, NullEncoder.Default);
blobName = blobName.Replace("\r", string.Empty).Replace("\n", string.Empty);
}
catch (Exception e)
{
_logger.LogCritical(e, "Unable to parse data protection blob name.");
throw;
}
}
return blobName;
}
// Assume that this module will override default configuration, so set the Order to a value above the default.
public override int Order => 10;
}
}