diff --git a/IRaCIS.Core.API/Controllers/ExtraController.cs b/IRaCIS.Core.API/Controllers/ExtraController.cs index a6a000249..b32bc67ab 100644 --- a/IRaCIS.Core.API/Controllers/ExtraController.cs +++ b/IRaCIS.Core.API/Controllers/ExtraController.cs @@ -1,6 +1,9 @@ -using Amazon.SecurityToken; +using Amazon.Auth.AccessControlPolicy; +using Amazon.SecurityToken; +using Azure.Core; using IRaCIS.Application.Contracts; using IRaCIS.Application.Interfaces; +using IRaCIS.Core.API.OAuth; using IRaCIS.Core.Application.Auth; using IRaCIS.Core.Application.Contracts; using IRaCIS.Core.Application.Helper; @@ -10,13 +13,18 @@ using IRaCIS.Core.Domain.Share; using IRaCIS.Core.Infra.EFCore; using IRaCIS.Core.Infrastructure.Extention; using MassTransit; +using MassTransit.Futures.Contracts; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using RestSharp; +using RestSharp.Authenticators; using System; +using System.Collections.Generic; using System.Linq; +using System.Net; using System.Text; using System.Threading.Tasks; using ZiggyCreatures.Caching.Fusion; @@ -62,15 +70,17 @@ namespace IRaCIS.Api.Controllers BasicInfoView = await _doctorService.GetBasicInfo(inDto.doctorId), EmploymentView = await _doctorService.GetEmploymentInfo(inDto.doctorId), AttachmentList = await attachmentService.GetAttachments(inDto.doctorId), - SummarizeInfo = await _doctorService.GetSummarizeInfo(new GetSummarizeInfoInDto() { - DoctorId = inDto.doctorId, - TrialId=inDto.TrialId + SummarizeInfo = await _doctorService.GetSummarizeInfo(new GetSummarizeInfoInDto() + { + DoctorId = inDto.doctorId, + TrialId = inDto.TrialId }), PaymentModeInfo = await _doctorService.GetPaymentMode(inDto.doctorId), EducationList = education.EducationList, PostgraduateList = education.PostgraduateList, - TrialExperienceView = await _trialExperienceService.GetTrialExperience(new TrialExperienceModelIndto() { + TrialExperienceView = await _trialExperienceService.GetTrialExperience(new TrialExperienceModelIndto() + { DoctorId = inDto.doctorId, TrialId = inDto.TrialId }), @@ -412,12 +422,82 @@ namespace IRaCIS.Api.Controllers [HttpGet("User/OAuthCallBack")] public async Task OAuthCallBack(string type, string code) { + #region 获取AccessTo + + //var headerDic = new Dictionary(); + //headerDic.Add("code", code); + //headerDic.Add("grant_type", "authorization_code"); + //headerDic.Add("redirect_uri", "http://localhost:6100"); + //headerDic.Add("scope", "all"); + + #endregion + + + #region 客户端方式获取logto 里面的信息 + + var baseUrl = "https://logto.test.extimaging.com"; + var appId = "v2mr2ndxwkxz0xpsuc1th"; + var appSecret = "yq9jUxl70QoOmwHxJ37h1rDoyJ5iz92Q"; + var apiAddress = "https://default.logto.app/api"; //这里是个坑 + var scope = "all"; + + var opts = new RestClientOptions(baseUrl); + using var client = new RestClient(opts); + + //https://bump.sh/logto/doc/logto-management-api/authentication + var request = new RestRequest("oidc/token", Method.Post); + request + .AddHeader("Content-Type", "application/x-www-form-urlencoded") + .AddParameter("grant_type", "client_credentials") + .AddParameter("client_id", appId) + .AddParameter("client_secret", appSecret) + .AddParameter("resource", apiAddress) //注意这里默认值地址和api 地址有区别 + .AddParameter("scope", scope); + + + var response = await client.ExecuteAsync(request); + + if (response.StatusCode == HttpStatusCode.OK) + { + var tokenResponse = response.Data; + + Console.WriteLine(tokenResponse.ToJsonStr()); + + #region 获取应用信息 + + var applicationRequest = new RestRequest($"/api/applications", Method.Get) + .AddHeader("Authorization", $"Bearer {tokenResponse.AccessToken}"); + + var applicationResponse = await client.ExecuteAsync(applicationRequest); + #endregion + + #region 获取用户信息 + //curl \ + // -X GET https://[tenant_id].logto.app/api/users/{userId} \ + // -H "Authorization: Bearer $ACCESS_TOKEN" + + var userId = "4fqx4cb3438k"; + var userInfoRequest = new RestRequest($"api/users/{userId}", Method.Get) + .AddHeader("Authorization", $"Bearer {tokenResponse.AccessToken}"); + + + var userResponse = await client.ExecuteAsync(userInfoRequest); + + Console.WriteLine(userResponse.Content); + + #endregion + } + + + #endregion return ResponseOutput.Ok(); } + + #endregion #region 测试获取用户 ip diff --git a/IRaCIS.Core.API/IRaCIS.Core.API.xml b/IRaCIS.Core.API/IRaCIS.Core.API.xml index 222dde557..d2bfbef62 100644 --- a/IRaCIS.Core.API/IRaCIS.Core.API.xml +++ b/IRaCIS.Core.API/IRaCIS.Core.API.xml @@ -346,6 +346,147 @@ + + + The token names used by Cookie and OpenID Connect middleware to store and retrieve tokens from + Logto OpenID Connect provider. +
+ See tokens that are stored by OpenID Connect middleware for more details. +
+
+ + + The scope names used by Logto OpenID Connect provider to request for user information. + + + + + The scope name for requesting user's email. + Logto will issue two claims to the ID token: email and email_verified. + + + + + The scope name for requesting user's phone number. + Logto will issue two claims to the ID token: phone and phone_verified. + + + + + The scope name for requesting user's custom data. + Logto will issue a claim to the response of the userinfo endpoint: custom_data. +
+ Note that when requesting this scope, you must set to true. +
+
+ + + The scope name for requesting user's identities. + Logto will issue a claim to the response of the userinfo endpoint: identities. +
+ Note that when requesting this scope, you must set to true. +
+
+ + + The claim names used by Logto OpenID Connect provider for ID token and userinfo endpoint. + + + + + The claim name for the issuer identifier for whom issued the token. + + + + + The claim name for the subject identifier for whom the token is intended (user ID). + + + + + The claim name for the audience that the token is intended for, which is the client ID. + + + + + The claim name for the expiration time of the token (in seconds). + + + + + The claim name for the time at which the token was issued (in seconds). + + + + + The claim name for the user's full name. + + + + + The claim name for user's username. + + + + + The claim name for user's profile picture URL. + + + + + The claim name for user's email. + + + + + The claim name for user's email verification status. + + + + + The claim name for user's phone number. + + + + + The claim name for user's phone number verification status. + + + + + The claim name for user's custom data. + + + + + The claim name for user's identities. + + + + + The access token issued by the Logto authorization server. + + + + + The type of the token issued by the Logto authorization server. + + + + + The lifetime in seconds of the access token. + + + + + The refresh token, which can be used to obtain new access tokens using the same authorization grant. + + + + + The ID token, which can be used to verify the identity of the user. + + IPLimit限流 启动服务 diff --git a/IRaCIS.Core.API/OAuth/LogotoParams.cs b/IRaCIS.Core.API/OAuth/LogotoParams.cs new file mode 100644 index 000000000..0cfb61299 --- /dev/null +++ b/IRaCIS.Core.API/OAuth/LogotoParams.cs @@ -0,0 +1,121 @@ +using Microsoft.IdentityModel.Protocols.OpenIdConnect; +using System.Text.Json.Serialization; + +namespace IRaCIS.Core.API.OAuth; + + + +public static class LogtoParameters +{ + /// + /// The token names used by Cookie and OpenID Connect middleware to store and retrieve tokens from + /// Logto OpenID Connect provider. + ///
+ /// See tokens that are stored by OpenID Connect middleware for more details. + ///
+ public static class Tokens + { + public const string AccessToken = OpenIdConnectParameterNames.AccessToken; + public const string ExpiresAt = "expires_at"; + public const string AccessTokenForResource = $"{AccessToken}.resource"; + public const string ExpiresAtForResource = $"{ExpiresAt}.resource"; + public const string RefreshToken = OpenIdConnectParameterNames.RefreshToken; + public const string IdToken = OpenIdConnectParameterNames.IdToken; + public const string TokenType = OpenIdConnectParameterNames.TokenType; + + } + + /// + /// The scope names used by Logto OpenID Connect provider to request for user information. + /// + public static class Scopes + { + /// + /// The scope name for requesting user's email. + /// Logto will issue two claims to the ID token: email and email_verified. + /// + public const string Email = "email"; + /// + /// The scope name for requesting user's phone number. + /// Logto will issue two claims to the ID token: phone and phone_verified. + /// + public const string Phone = "phone"; + /// + /// The scope name for requesting user's custom data. + /// Logto will issue a claim to the response of the userinfo endpoint: custom_data. + ///
+ /// Note that when requesting this scope, you must set to true. + ///
+ public const string CustomData = "custom_data"; + /// + /// The scope name for requesting user's identities. + /// Logto will issue a claim to the response of the userinfo endpoint: identities. + ///
+ /// Note that when requesting this scope, you must set to true. + ///
+ public const string Identities = "identities"; + } + + /// + /// The claim names used by Logto OpenID Connect provider for ID token and userinfo endpoint. + /// + public static class Claims + { + /// + /// The claim name for the issuer identifier for whom issued the token. + /// + public const string Issuer = "iss"; + /// + /// The claim name for the subject identifier for whom the token is intended (user ID). + /// + public const string Subject = "sub"; + /// + /// The claim name for the audience that the token is intended for, which is the client ID. + /// + public const string Audience = "aud"; + /// + /// The claim name for the expiration time of the token (in seconds). + /// + public const string Expiration = "exp"; + /// + /// The claim name for the time at which the token was issued (in seconds). + /// + public const string IssuedAt = "iat"; + /// + /// The claim name for the user's full name. + /// + public const string Name = "name"; + /// + /// The claim name for user's username. + /// + public const string Username = "username"; + /// + /// The claim name for user's profile picture URL. + /// + public const string Picture = "picture"; + /// + /// The claim name for user's email. + /// + public const string Email = "email"; + /// + /// The claim name for user's email verification status. + /// + public const string EmailVerified = "email_verified"; + /// + /// The claim name for user's phone number. + /// + public const string PhoneNumber = "phone_number"; + /// + /// The claim name for user's phone number verification status. + /// + public const string PhoneNumberVerified = "phone_number_verified"; + /// + /// The claim name for user's custom data. + /// + public const string CustomData = "custom_data"; + /// + /// The claim name for user's identities. + /// + public const string Identities = "identities"; + } +} \ No newline at end of file diff --git a/IRaCIS.Core.API/OAuth/LogtoTokenResponse.cs b/IRaCIS.Core.API/OAuth/LogtoTokenResponse.cs new file mode 100644 index 000000000..9c9dd5600 --- /dev/null +++ b/IRaCIS.Core.API/OAuth/LogtoTokenResponse.cs @@ -0,0 +1,37 @@ +using System.Text.Json.Serialization; + +namespace IRaCIS.Core.API.OAuth; + +public class LogtoTokenResponse +{ + /// + /// The access token issued by the Logto authorization server. + /// + [JsonPropertyName("access_token")] + public string AccessToken { get; set; } = null!; + + /// + /// The type of the token issued by the Logto authorization server. + /// + [JsonPropertyName("token_type")] + public string TokenType { get; set; } = null!; + + /// + /// The lifetime in seconds of the access token. + /// + [JsonPropertyName("expires_in")] + public int ExpiresIn { get; set; } + + /// + /// The refresh token, which can be used to obtain new access tokens using the same authorization grant. + /// + [JsonPropertyName("refresh_token")] + public string? RefreshToken { get; set; } = null!; + + /// + /// The ID token, which can be used to verify the identity of the user. + /// + [JsonPropertyName("id_token")] + public string? IdToken { get; set; } = null; +} + diff --git a/IRaCIS.Core.API/OAuth/LogtoUser.cs b/IRaCIS.Core.API/OAuth/LogtoUser.cs new file mode 100644 index 000000000..8f627f4c6 --- /dev/null +++ b/IRaCIS.Core.API/OAuth/LogtoUser.cs @@ -0,0 +1,21 @@ +using Org.BouncyCastle.Tls; +using System.Collections.Generic; + +namespace IRaCIS.Core.API.OAuth; + +public class LogtoUser +{ + public string Id { get; set; } + public string Username { get; set; } + public string PrimaryEmail { get; set; } + public string PrimaryPhone { get; set; } + public string Name { get; set; } + public string Avatar { get; set; } + public Dictionary CustomData { get; set; } // Assuming customData can be any object + public Dictionary Identities { get; set; } + + public Dictionary Profile { get; set; } + public string ApplicationId { get; set; } + public bool IsSuspended { get; set; } + public bool HasPassword { get; set; } +} diff --git a/IRaCIS.Core.Application/Service/Common/ExcelExportService.cs b/IRaCIS.Core.Application/Service/Common/ExcelExportService.cs index 5092e31e8..2b8188b01 100644 --- a/IRaCIS.Core.Application/Service/Common/ExcelExportService.cs +++ b/IRaCIS.Core.Application/Service/Common/ExcelExportService.cs @@ -2345,7 +2345,7 @@ namespace IRaCIS.Core.Application.Service.Common var doctor2List = _visitTaskRepository.Where(comonTaskFilter).Where(t => t.ReadingTaskState == ReadingTaskState.HaveSigned) .GroupBy(t => new { t.DoctorUserId, t.DoctorUser.UserName, t.DoctorUser.FullName }) - //有全局裁判 + //有全局裁判 //.Where(g => g.Any(t => t.ReadingCategory == ReadingCategory.Global && t.JudgeVisitTaskId != null)) .Select(g => new DoctorJudgeRatio() { @@ -2354,7 +2354,7 @@ namespace IRaCIS.Core.Application.Service.Common FullName = g.Key.FullName, //触发裁判的阅片期的数量 - TotalJudgeCount = g.Where(t => t.ReadingCategory == ReadingCategory.Global && t.SouceReadModuleId != null && t.JudgeVisitTaskId != null).Select(t => t.SouceReadModuleId).Distinct().Count(), + TotalJudgeCount = g.Where(t => t.ReadingCategory == ReadingCategory.Global && t.SouceReadModuleId != null && t.JudgeVisitTaskId != null && t.JudgeVisitTask.ReadingTaskState==ReadingTaskState.HaveSigned).Select(t => t.SouceReadModuleId).Distinct().Count(), JudgeAgreeCount = g.Where(t => t.ReadingCategory == ReadingCategory.Global && t.JudgeVisitTaskId != null && t.JudgeVisitTask.JudgeResultTaskId == t.Id) .Select(t => t.SouceReadModuleId).Distinct().Count(), diff --git a/IRaCIS.Core.Application/Service/Document/TrialEmailNoticeConfigService.cs b/IRaCIS.Core.Application/Service/Document/TrialEmailNoticeConfigService.cs index 196abc9b0..4327d722f 100644 --- a/IRaCIS.Core.Application/Service/Document/TrialEmailNoticeConfigService.cs +++ b/IRaCIS.Core.Application/Service/Document/TrialEmailNoticeConfigService.cs @@ -1243,8 +1243,8 @@ namespace IRaCIS.Core.Application.Service public async Task> GetSysEmailNoticeConfigList(EmailNoticeConfigQuery inQuery) { var emailNoticeConfigQueryable = _emailNoticeConfigRepository - .WhereIf(inQuery.SystemLevel == null, t => t.SystemLevel == SysEmailLevel.not_sys) - .WhereIf(inQuery.SystemLevel != null, t => t.SystemLevel == inQuery.SystemLevel) + .WhereIf(inQuery.SystemLevel == null, t => t.SystemLevel == SysEmailLevel.not_sys || t.SystemLevel == SysEmailLevel.sys_Config_role) + //.WhereIf(inQuery.SystemLevel != null, t => t.SystemLevel == inQuery.SystemLevel) .WhereIf(inQuery.BusinessScenarioEnum != null, t => t.BusinessScenarioEnum == inQuery.BusinessScenarioEnum) .WhereIf(inQuery.IsReturnRequired != null, t => t.IsReturnRequired == inQuery.IsReturnRequired) .WhereIf(inQuery.IsEnable != null, t => t.IsEnable == inQuery.IsEnable) diff --git a/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs b/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs index 1a24b3ad0..cd297db15 100644 --- a/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs +++ b/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs @@ -353,7 +353,7 @@ namespace IRaCIS.Core.Application.Contracts public string Content { get; set; } public string ContentReplaced => Content.Replace("
", "").Replace("
", "").Replace("
", "") - .Replace("
", "").Replace("
", "").Replace("
", ""); + .Replace("
", "").Replace("
", "").Replace("
", "").Replace("
", ""); }