You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

374 lines
15 KiB

2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Runtime.Intrinsics.Arm;
  5. using System.Security.Cryptography;
  6. using System.Text;
  7. using System.Text.Json;
  8. using System.Threading.Tasks;
  9. using JetBrains.Annotations;
  10. using Microsoft.Extensions.Configuration;
  11. using Microsoft.Extensions.Localization;
  12. using OpenIddict.Abstractions;
  13. using Shentun.WebPeis.Enums;
  14. using Volo.Abp;
  15. using Volo.Abp.Authorization.Permissions;
  16. using Volo.Abp.Data;
  17. using Volo.Abp.DependencyInjection;
  18. using Volo.Abp.OpenIddict.Applications;
  19. using Volo.Abp.OpenIddict.Scopes;
  20. using Volo.Abp.PermissionManagement;
  21. using Volo.Abp.Uow;
  22. namespace Shentun.WebPeis.OpenIddict;
  23. /* Creates initial data that is needed to property run the application
  24. * and make client-to-server communication possible.
  25. */
  26. public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDependency
  27. {
  28. private readonly IConfiguration _configuration;
  29. private readonly IOpenIddictApplicationRepository _openIddictApplicationRepository;
  30. private readonly IAbpApplicationManager _applicationManager;
  31. private readonly IOpenIddictScopeRepository _openIddictScopeRepository;
  32. private readonly IOpenIddictScopeManager _scopeManager;
  33. private readonly IPermissionDataSeeder _permissionDataSeeder;
  34. private readonly IStringLocalizer<OpenIddictResponse> L;
  35. public OpenIddictDataSeedContributor(
  36. IConfiguration configuration,
  37. IOpenIddictApplicationRepository openIddictApplicationRepository,
  38. IAbpApplicationManager applicationManager,
  39. IOpenIddictScopeRepository openIddictScopeRepository,
  40. IOpenIddictScopeManager scopeManager,
  41. IPermissionDataSeeder permissionDataSeeder,
  42. IStringLocalizer<OpenIddictResponse> l)
  43. {
  44. _configuration = configuration;
  45. _openIddictApplicationRepository = openIddictApplicationRepository;
  46. _applicationManager = applicationManager;
  47. _openIddictScopeRepository = openIddictScopeRepository;
  48. _scopeManager = scopeManager;
  49. _permissionDataSeeder = permissionDataSeeder;
  50. L = l;
  51. }
  52. [UnitOfWork]
  53. public virtual async Task SeedAsync(DataSeedContext context)
  54. {
  55. await CreateScopesAsync();
  56. await CreateApplicationsAsync();
  57. }
  58. private async Task CreateScopesAsync()
  59. {
  60. if (await _openIddictScopeRepository.FindByNameAsync("WebPeis") == null)
  61. {
  62. await _scopeManager.CreateAsync(new OpenIddictScopeDescriptor
  63. {
  64. Name = "WebPeis",
  65. DisplayName = "WebPeis API",
  66. Resources = { "WebPeis" }
  67. });
  68. }
  69. }
  70. private async Task CreateApplicationsAsync()
  71. {
  72. var commonScopes = new List<string> {
  73. OpenIddictConstants.Permissions.Scopes.Address,
  74. OpenIddictConstants.Permissions.Scopes.Email,
  75. OpenIddictConstants.Permissions.Scopes.Phone,
  76. OpenIddictConstants.Permissions.Scopes.Profile,
  77. OpenIddictConstants.Permissions.Scopes.Roles,
  78. "WebPeis"
  79. };
  80. var configurationSection = _configuration.GetSection("OpenIddict:Applications");
  81. // Swagger Client
  82. var swaggerClientId = configurationSection["WebPeis_Swagger:ClientId"];
  83. if (!swaggerClientId.IsNullOrWhiteSpace())
  84. {
  85. var swaggerRootUrl = configurationSection["WebPeis_Swagger:RootUrl"]?.TrimEnd('/');
  86. await CreateApplicationAsync(
  87. name: swaggerClientId!,
  88. type: OpenIddictConstants.ClientTypes.Public,
  89. consentType: OpenIddictConstants.ConsentTypes.Implicit,
  90. displayName: "Swagger Application",
  91. secret: null,
  92. grantTypes: new List<string> { OpenIddictConstants.GrantTypes.AuthorizationCode, },
  93. scopes: commonScopes,
  94. redirectUri: $"{swaggerRootUrl}/swagger/oauth2-redirect.html",
  95. clientUri: swaggerRootUrl
  96. );
  97. }
  98. var weChatClientId = configurationSection.GetSection("WeChatApp").GetSection("ClientId").Value;
  99. var secret = configurationSection.GetSection("WeChatApp").GetSection("ClientSecret").Value;
  100. if(string.IsNullOrWhiteSpace(secret))
  101. {
  102. throw new Exception("secret不能为空");
  103. }
  104. //secret = GetSha256Hash(secret);
  105. secret = null;
  106. if (!string.IsNullOrWhiteSpace(weChatClientId))
  107. {
  108. var swaggerRootUrl = configurationSection["WebPeis_Swagger:RootUrl"]?.TrimEnd('/');
  109. await CreateApplicationAsync(
  110. name: weChatClientId!,
  111. type: OpenIddictConstants.ClientTypes.Public,
  112. consentType: OpenIddictConstants.ConsentTypes.Implicit,
  113. displayName: "WeChat Application",
  114. secret: secret,
  115. grantTypes: new List<string>
  116. {
  117. OpenIddictConstants.GrantTypes.AuthorizationCode,
  118. OpenIddictConstants.GrantTypes.Password,
  119. OpenIddictConstants.GrantTypes.ClientCredentials,
  120. OpenIddictConstants.GrantTypes.RefreshToken,
  121. WeChatGrant.GrantType
  122. },
  123. scopes: commonScopes,
  124. redirectUri: $"{swaggerRootUrl}/swagger/oauth2-redirect.html",
  125. clientUri: swaggerRootUrl
  126. );
  127. }
  128. }
  129. private async Task CreateApplicationAsync(
  130. [NotNull] string name,
  131. [NotNull] string type,
  132. [NotNull] string consentType,
  133. string displayName,
  134. string? secret,
  135. List<string> grantTypes,
  136. List<string> scopes,
  137. string? clientUri = null,
  138. string? redirectUri = null,
  139. string? postLogoutRedirectUri = null,
  140. List<string>? permissions = null)
  141. {
  142. if (!string.IsNullOrEmpty(secret) && string.Equals(type, OpenIddictConstants.ClientTypes.Public,
  143. StringComparison.OrdinalIgnoreCase))
  144. {
  145. throw new BusinessException(L["NoClientSecretCanBeSetForPublicApplications"]);
  146. }
  147. if (string.IsNullOrEmpty(secret) && string.Equals(type, OpenIddictConstants.ClientTypes.Confidential,
  148. StringComparison.OrdinalIgnoreCase))
  149. {
  150. throw new BusinessException(L["TheClientSecretIsRequiredForConfidentialApplications"]);
  151. }
  152. var client = await _openIddictApplicationRepository.FindByClientIdAsync(name);
  153. var application = new AbpApplicationDescriptor
  154. {
  155. ClientId = name,
  156. ClientType = type,
  157. ClientSecret = secret,
  158. ConsentType = consentType,
  159. DisplayName = displayName,
  160. ClientUri = clientUri,
  161. };
  162. Check.NotNullOrEmpty(grantTypes, nameof(grantTypes));
  163. Check.NotNullOrEmpty(scopes, nameof(scopes));
  164. if (new[] { OpenIddictConstants.GrantTypes.AuthorizationCode, OpenIddictConstants.GrantTypes.Implicit }.All(
  165. grantTypes.Contains))
  166. {
  167. application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeIdToken);
  168. if (string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase))
  169. {
  170. application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeIdTokenToken);
  171. application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeToken);
  172. }
  173. }
  174. if (!redirectUri.IsNullOrWhiteSpace() || !postLogoutRedirectUri.IsNullOrWhiteSpace())
  175. {
  176. application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Logout);
  177. }
  178. var buildInGrantTypes = new[] {
  179. OpenIddictConstants.GrantTypes.Implicit, OpenIddictConstants.GrantTypes.Password,
  180. OpenIddictConstants.GrantTypes.AuthorizationCode, OpenIddictConstants.GrantTypes.ClientCredentials,
  181. OpenIddictConstants.GrantTypes.DeviceCode, OpenIddictConstants.GrantTypes.RefreshToken
  182. };
  183. foreach (var grantType in grantTypes)
  184. {
  185. if (grantType == OpenIddictConstants.GrantTypes.AuthorizationCode)
  186. {
  187. application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode);
  188. application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.Code);
  189. }
  190. if (grantType == OpenIddictConstants.GrantTypes.AuthorizationCode ||
  191. grantType == OpenIddictConstants.GrantTypes.Implicit)
  192. {
  193. application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Authorization);
  194. }
  195. if (grantType == OpenIddictConstants.GrantTypes.AuthorizationCode ||
  196. grantType == OpenIddictConstants.GrantTypes.ClientCredentials ||
  197. grantType == OpenIddictConstants.GrantTypes.Password ||
  198. grantType == OpenIddictConstants.GrantTypes.RefreshToken ||
  199. grantType == OpenIddictConstants.GrantTypes.DeviceCode)
  200. {
  201. application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Token);
  202. application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Revocation);
  203. application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Introspection);
  204. }
  205. if (grantType == OpenIddictConstants.GrantTypes.ClientCredentials)
  206. {
  207. application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.ClientCredentials);
  208. }
  209. if (grantType == OpenIddictConstants.GrantTypes.Implicit)
  210. {
  211. application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.Implicit);
  212. }
  213. if (grantType == OpenIddictConstants.GrantTypes.Password)
  214. {
  215. application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.Password);
  216. }
  217. if (grantType == OpenIddictConstants.GrantTypes.RefreshToken)
  218. {
  219. application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.RefreshToken);
  220. }
  221. if (grantType == OpenIddictConstants.GrantTypes.DeviceCode)
  222. {
  223. application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.DeviceCode);
  224. application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Device);
  225. }
  226. if (grantType == OpenIddictConstants.GrantTypes.Implicit)
  227. {
  228. application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.IdToken);
  229. if (string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase))
  230. {
  231. application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.IdTokenToken);
  232. application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.Token);
  233. }
  234. }
  235. if (!buildInGrantTypes.Contains(grantType))
  236. {
  237. application.Permissions.Add(OpenIddictConstants.Permissions.Prefixes.GrantType + grantType);
  238. }
  239. }
  240. var buildInScopes = new[] {
  241. OpenIddictConstants.Permissions.Scopes.Address, OpenIddictConstants.Permissions.Scopes.Email,
  242. OpenIddictConstants.Permissions.Scopes.Phone, OpenIddictConstants.Permissions.Scopes.Profile,
  243. OpenIddictConstants.Permissions.Scopes.Roles
  244. };
  245. foreach (var scope in scopes)
  246. {
  247. if (buildInScopes.Contains(scope))
  248. {
  249. application.Permissions.Add(scope);
  250. }
  251. else
  252. {
  253. application.Permissions.Add(OpenIddictConstants.Permissions.Prefixes.Scope + scope);
  254. }
  255. }
  256. if (redirectUri != null)
  257. {
  258. if (!redirectUri.IsNullOrEmpty())
  259. {
  260. if (!Uri.TryCreate(redirectUri, UriKind.Absolute, out var uri) || !uri.IsWellFormedOriginalString())
  261. {
  262. throw new BusinessException(L["InvalidRedirectUri", redirectUri]);
  263. }
  264. if (application.RedirectUris.All(x => x != uri))
  265. {
  266. application.RedirectUris.Add(uri);
  267. }
  268. }
  269. }
  270. if (postLogoutRedirectUri != null)
  271. {
  272. if (!postLogoutRedirectUri.IsNullOrEmpty())
  273. {
  274. if (!Uri.TryCreate(postLogoutRedirectUri, UriKind.Absolute, out var uri) ||
  275. !uri.IsWellFormedOriginalString())
  276. {
  277. throw new BusinessException(L["InvalidPostLogoutRedirectUri", postLogoutRedirectUri]);
  278. }
  279. if (application.PostLogoutRedirectUris.All(x => x != uri))
  280. {
  281. application.PostLogoutRedirectUris.Add(uri);
  282. }
  283. }
  284. }
  285. if (permissions != null)
  286. {
  287. await _permissionDataSeeder.SeedAsync(
  288. ClientPermissionValueProvider.ProviderName,
  289. name,
  290. permissions,
  291. null
  292. );
  293. }
  294. if (client == null)
  295. {
  296. await _applicationManager.CreateAsync(application);
  297. return;
  298. }
  299. if (!HasSameRedirectUris(client, application))
  300. {
  301. client.RedirectUris = JsonSerializer.Serialize(application.RedirectUris.Select(q => q.ToString().TrimEnd('/')));
  302. client.PostLogoutRedirectUris = JsonSerializer.Serialize(application.PostLogoutRedirectUris.Select(q => q.ToString().TrimEnd('/')));
  303. await _applicationManager.UpdateAsync(client.ToModel());
  304. }
  305. if (!HasSameScopes(client, application))
  306. {
  307. client.Permissions = JsonSerializer.Serialize(application.Permissions.Select(q => q.ToString()));
  308. await _applicationManager.UpdateAsync(client.ToModel());
  309. }
  310. }
  311. private bool HasSameRedirectUris(OpenIddictApplication existingClient, AbpApplicationDescriptor application)
  312. {
  313. return existingClient.RedirectUris == JsonSerializer.Serialize(application.RedirectUris.Select(q => q.ToString().TrimEnd('/')));
  314. }
  315. private bool HasSameScopes(OpenIddictApplication existingClient, AbpApplicationDescriptor application)
  316. {
  317. return existingClient.Permissions == JsonSerializer.Serialize(application.Permissions.Select(q => q.ToString().TrimEnd('/')));
  318. }
  319. private static string GetSha256Hash(string input)
  320. {
  321. using (SHA256 sha256Hash = SHA256.Create())
  322. {
  323. byte[] bytes = Encoding.UTF8.GetBytes(input);
  324. return Convert.ToBase64String(((HashAlgorithm)sha256Hash).ComputeHash(bytes));
  325. }
  326. }
  327. }