microsoft/healthcare-shared-components

Public

mirrored from https://github.com/microsoft/healthcare-shared-componentsAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
1.0.0-1.0.79

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

src/Microsoft.Health.Development.IdentityProvider/RegistrationExtensions.cs

224lines · modecode

1// -------------------------------------------------------------------------------------------------
2// Copyright (c) Microsoft Corporation. All rights reserved.
3// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
4// -------------------------------------------------------------------------------------------------
5
6using System;
7using System.Collections.Generic;
8using System.IO;
9using System.Linq;
10using System.Text.RegularExpressions;
11using EnsureThat;
12using IdentityServer4.Models;
13using IdentityServer4.Test;
14using Microsoft.AspNetCore.Builder;
15using Microsoft.Extensions.Configuration;
16using Microsoft.Extensions.Configuration.Json;
17using Microsoft.Extensions.DependencyInjection;
18using Microsoft.Extensions.Options;
19using Config = Microsoft.Health.Development.IdentityProvider.Configuration;
20
21namespace Microsoft.Health.Development.IdentityProvider
22{
23 public static class RegistrationExtensions
24 {
25 private const string WrongAudienceClient = "wrongAudienceClient";
26
27 /// <summary>
28 /// Adds an in-process identity provider if enabled in configuration.
29 /// </summary>
30 /// <param name="services">The services collection.</param>
31 /// <param name="configuration">The configuration root. The "DevelopmentIdentityProvider" section will be used to populate configuration values.</param>
32 /// <returns>The same services collection.</returns>
33 public static IServiceCollection AddDevelopmentIdentityProvider(this IServiceCollection services, IConfiguration configuration)
34 {
35 EnsureArg.IsNotNull(services, nameof(services));
36 EnsureArg.IsNotNull(configuration, nameof(configuration));
37
38 var developmentIdentityProviderConfiguration = new Config.Provider();
39 configuration.GetSection("DevelopmentIdentityProvider").Bind(developmentIdentityProviderConfiguration);
40 services.AddSingleton(Options.Create(developmentIdentityProviderConfiguration));
41
42 if (developmentIdentityProviderConfiguration.Enabled)
43 {
44 services.AddIdentityServer()
45 .AddDeveloperSigningCredential()
46 .AddInMemoryApiResources(new[]
47 {
48 new ApiResource(Config.Provider.Audience) { },
49 new ApiResource(WrongAudienceClient) { },
50 })
51 .AddTestUsers(developmentIdentityProviderConfiguration.Users?.Select(user =>
52 new TestUser
53 {
54 Username = user.Id,
55 Password = user.Id,
56 IsActive = true,
57 SubjectId = user.Id,
58 }).ToList())
59 .AddInMemoryClients(
60 developmentIdentityProviderConfiguration.ClientApplications.Select(
61 applicationConfiguration =>
62 new Client
63 {
64 ClientId = applicationConfiguration.Id,
65
66 // client credentials and ROPC for testing
67 AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
68
69 // secret for authentication
70 ClientSecrets = { new Secret(applicationConfiguration.Id.Sha256()) },
71
72 // scopes that client has access to
73 AllowedScopes = { Config.Provider.Audience, WrongAudienceClient },
74 }));
75 }
76
77 return services;
78 }
79
80 /// <summary>
81 /// Adds the in-process identity provider to the pipeline if enabled in configuration.
82 /// </summary>
83 /// <param name="app">The application builder</param>
84 /// <returns>The application builder.</returns>
85 public static IApplicationBuilder UseDevelopmentIdentityProviderIfConfigured(this IApplicationBuilder app)
86 {
87 EnsureArg.IsNotNull(app, nameof(app));
88 if (app.ApplicationServices.GetService<IOptions<Config.Provider>>()?.Value?.Enabled == true)
89 {
90 app.UseIdentityServer();
91 }
92
93 return app;
94 }
95
96 /// <summary>
97 /// If <paramref name="existingConfiguration"/> contains a value for TestAuthEnvironment:FilePath and the file exists, this method adds an <see cref="IConfigurationBuilder"/> that
98 /// reads a testauthenvironment.json file and reshapes it to fit into the expected schema of the server
99 /// configuration. Also sets the security audience and sets the development identity server as enabled.
100 /// This is an optional configuration source and is only intended to be used for local development.
101 /// </summary>
102 /// <param name="configurationBuilder">The configuration builder.</param>
103 /// <param name="existingConfiguration">Configuration root</param>
104 /// <param name="healthServerKey">Server key used in the appsettings.json.</param>
105 /// <returns>The same configuration builder.</returns>
106 public static IConfigurationBuilder AddDevelopmentAuthEnvironmentIfConfigured(this IConfigurationBuilder configurationBuilder, IConfigurationRoot existingConfiguration, string healthServerKey)
107 {
108 EnsureArg.IsNotNull(existingConfiguration, nameof(existingConfiguration));
109 EnsureArg.IsNotNullOrWhiteSpace(healthServerKey, nameof(healthServerKey));
110
111 string testEnvironmentFilePath = existingConfiguration["TestAuthEnvironment:FilePath"];
112
113 if (string.IsNullOrWhiteSpace(testEnvironmentFilePath))
114 {
115 return configurationBuilder;
116 }
117
118 testEnvironmentFilePath = Path.GetFullPath(testEnvironmentFilePath);
119 if (!File.Exists(testEnvironmentFilePath))
120 {
121 return configurationBuilder;
122 }
123
124 return configurationBuilder.Add(new DevelopmentAuthEnvironmentConfigurationSource(testEnvironmentFilePath, existingConfiguration, healthServerKey));
125 }
126
127 private class DevelopmentAuthEnvironmentConfigurationSource : IConfigurationSource
128 {
129 private readonly string _filePath;
130 private readonly IConfigurationRoot _existingConfiguration;
131 private readonly string _healthServerKey;
132
133 public DevelopmentAuthEnvironmentConfigurationSource(string filePath, IConfigurationRoot existingConfiguration, string healthServerKey)
134 {
135 EnsureArg.IsNotNullOrWhiteSpace(filePath, nameof(filePath));
136 _filePath = filePath;
137 _existingConfiguration = existingConfiguration;
138 _healthServerKey = healthServerKey;
139 }
140
141 public IConfigurationProvider Build(IConfigurationBuilder builder)
142 {
143 var jsonConfigurationSource = new JsonConfigurationSource
144 {
145 Path = _filePath,
146 Optional = true,
147 };
148
149 jsonConfigurationSource.ResolveFileProvider();
150 return new Provider(jsonConfigurationSource, _existingConfiguration, _healthServerKey);
151 }
152
153 private class Provider : JsonConfigurationProvider
154 {
155 private readonly string _authorityKey;
156 private readonly string _audienceKey;
157 private readonly string _securityEnabledKey;
158 private const string DevelopmentIdpEnabledKey = "DevelopmentIdentityProvider:Enabled";
159
160 private readonly IConfigurationRoot _existingConfiguration;
161 private static readonly Dictionary<string, string> Mappings = new Dictionary<string, string>
162 {
163 { "^users:", "DevelopmentIdentityProvider:Users:" },
164 { "^clientApplications:", "DevelopmentIdentityProvider:ClientApplications:" },
165 };
166
167 public Provider(JsonConfigurationSource source, IConfigurationRoot existingConfiguration, string healthServerKey)
168 : base(source)
169 {
170 _existingConfiguration = existingConfiguration;
171 _authorityKey = $"{healthServerKey}:Security:Authentication:Authority";
172 _audienceKey = $"{healthServerKey}:Security:Authentication:Audience";
173 _securityEnabledKey = $"{healthServerKey}:Security:Enabled";
174 }
175
176 public override void Load()
177 {
178 base.Load();
179
180 // remap the entries
181 Data = Data.ToDictionary(
182 p => Mappings.Aggregate(p.Key, (acc, mapping) => Regex.Replace(acc, mapping.Key, mapping.Value, RegexOptions.IgnoreCase)),
183 p => p.Value,
184 StringComparer.OrdinalIgnoreCase);
185
186 // add properties related to the development identity provider.
187 if (bool.TryParse(_existingConfiguration[_securityEnabledKey], out bool securityEnabledValue)
188 && securityEnabledValue == true)
189 {
190 Data[DevelopmentIdpEnabledKey] = bool.TrueString;
191
192 if (string.IsNullOrWhiteSpace(_existingConfiguration[_audienceKey]))
193 {
194 Data[_audienceKey] = Config.Provider.Audience;
195 }
196
197 if (string.IsNullOrWhiteSpace(_existingConfiguration[_authorityKey]))
198 {
199 Data[_authorityKey] = GetAuthority();
200 }
201 }
202 }
203
204 private string GetAuthority()
205 {
206 return _existingConfiguration["ASPNETCORE_URLS"]
207 ?.Split(';', StringSplitOptions.RemoveEmptyEntries)
208 .Where(u =>
209 {
210 if (Uri.TryCreate(u, UriKind.Absolute, out Uri uri))
211 {
212 if (uri.Scheme == "https")
213 {
214 return true;
215 }
216 }
217
218 return false;
219 }).FirstOrDefault()?.TrimEnd('/');
220 }
221 }
222 }
223 }
224}
225