feat 数据敏感分配角色可见,切换基地,定时任务lock
This commit is contained in:
@@ -31,7 +31,6 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="MealUser\Controllers\" />
|
<Folder Include="MealUser\Controllers\" />
|
||||||
<Folder Include="MealUser\Request\" />
|
|
||||||
<Folder Include="MealUser\Response\" />
|
<Folder Include="MealUser\Response\" />
|
||||||
<Folder Include="MealUser\Services\Implements\" />
|
<Folder Include="MealUser\Services\Implements\" />
|
||||||
<Folder Include="Summary\Request\" />
|
<Folder Include="Summary\Request\" />
|
||||||
|
|||||||
50
Src/Dlhis.Basic/ObfuscateProviderJsonConverter.cs
Normal file
50
Src/Dlhis.Basic/ObfuscateProviderJsonConverter.cs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
using System.Security.Claims;
|
||||||
|
using Kurisu.AspNetCore;
|
||||||
|
using Kurisu.AspNetCore.Authentication.Abstractions;
|
||||||
|
using Kurisu.AspNetCore.Utils;
|
||||||
|
using Kurisu.AspNetCore.Utils.Converters;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Dlhis.Basic;
|
||||||
|
|
||||||
|
public class ObfuscateProviderJsonConverter : ObfuscateJsonConverter
|
||||||
|
{
|
||||||
|
private readonly int _last;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ObfuscateProviderJsonConverter(int last) : base(last)
|
||||||
|
{
|
||||||
|
_last = last;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
var sp = App.GetServiceProvider(true);
|
||||||
|
var cache = sp.GetService<RedisCache>();
|
||||||
|
var currentUser = sp.GetService<ICurrentUser>();
|
||||||
|
var range = cache.ListRange("datapermission:role");
|
||||||
|
if (range.Contains(currentUser.GetUserClaim(ClaimTypes.Role)))
|
||||||
|
{
|
||||||
|
writer.WriteValue(value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var v = value?.ToString() ?? string.Empty;
|
||||||
|
var vc = v.ToCharArray().Reverse().ToList();
|
||||||
|
|
||||||
|
for (int i = 0; i < vc.Count; i++)
|
||||||
|
{
|
||||||
|
if (i + 1 > _last)
|
||||||
|
{
|
||||||
|
vc[i] = '*';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vc.Reverse();
|
||||||
|
v = string.Join("", vc);
|
||||||
|
|
||||||
|
writer.WriteValue(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
using Dlhis.Basic.Organization.Response;
|
using Dlhis.Basic.Organization.Response;
|
||||||
using Dlhis.Basic.Organization.Services;
|
using Dlhis.Basic.Organization.Services;
|
||||||
using Kurisu.AspNetCore.DataAccess.Dto;
|
using Kurisu.AspNetCore.DataAccess.Dto;
|
||||||
|
using Kurisu.AspNetCore.DataAccess.SqlSugar.Aop;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
@@ -37,6 +38,8 @@ public class GainfoMealController : ControllerBase
|
|||||||
/// 迁移
|
/// 迁移
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Authorize(Roles = "admin")]
|
[Authorize(Roles = "admin")]
|
||||||
|
[Transactional]
|
||||||
|
[Authorize(Roles = "admin,tenant-admin")]
|
||||||
[HttpPost("migration")]
|
[HttpPost("migration")]
|
||||||
public async Task MigrationAsync(GainfoBalanceMigrationRequest request)
|
public async Task MigrationAsync(GainfoBalanceMigrationRequest request)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -47,9 +47,15 @@ public class GainfoPageResponse
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 身份证号
|
/// 身份证号
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonConverter(typeof(ObfuscateJsonConverter), 6)]
|
[JsonConverter(typeof(ObfuscateProviderJsonConverter), 6)]
|
||||||
public string IdNo { get; set; }
|
public string IdNo { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 手机号码
|
||||||
|
/// </summary>
|
||||||
|
[JsonConverter(typeof(ObfuscateProviderJsonConverter), 4)]
|
||||||
|
public string Phone { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 身份证籍贯区域代码
|
/// 身份证籍贯区域代码
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -88,12 +94,6 @@ public class GainfoPageResponse
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string Nation { get; set; }
|
public string Nation { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 手机号码
|
|
||||||
/// </summary>
|
|
||||||
[JsonConverter(typeof(ObfuscateJsonConverter), 4)]
|
|
||||||
public string Phone { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 学历
|
/// 学历
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ using Kurisu.AspNetCore.DataAccess.SqlSugar.Extensions;
|
|||||||
using Kurisu.AspNetCore.DataAccess.SqlSugar.Services;
|
using Kurisu.AspNetCore.DataAccess.SqlSugar.Services;
|
||||||
using Kurisu.AspNetCore.Utils;
|
using Kurisu.AspNetCore.Utils;
|
||||||
using Kurisu.AspNetCore.Utils.Extensions;
|
using Kurisu.AspNetCore.Utils.Extensions;
|
||||||
using SqlSugar;
|
|
||||||
|
|
||||||
namespace Dlhis.Basic.Organization.Services.Implements;
|
namespace Dlhis.Basic.Organization.Services.Implements;
|
||||||
|
|
||||||
|
|||||||
@@ -23,27 +23,21 @@ public abstract class BaseJob : IJob
|
|||||||
{
|
{
|
||||||
using (var scope = _serviceProvider.CreateScope())
|
using (var scope = _serviceProvider.CreateScope())
|
||||||
{
|
{
|
||||||
Logger = scope.ServiceProvider.GetService<ILogger<BaseJob>>();
|
|
||||||
|
|
||||||
var recurringJob = this.GetType().GetCustomAttribute<RecurringJobAttribute>()!;
|
var recurringJob = this.GetType().GetCustomAttribute<RecurringJobAttribute>()!;
|
||||||
var key = recurringJob.UniqueJobKey;
|
var key = recurringJob.UniqueJobKey;
|
||||||
var name = recurringJob!.Name;
|
var name = recurringJob.Name;
|
||||||
|
|
||||||
var redisCache = scope.ServiceProvider.GetService<RedisCache>();
|
|
||||||
await using var handler = await redisCache.LockAsync($"scheduled:{key}");
|
|
||||||
|
|
||||||
|
Logger = scope.ServiceProvider.GetService<ILogger<BaseJob>>();
|
||||||
DbContext = scope.ServiceProvider.GetService<IDbContext>();
|
DbContext = scope.ServiceProvider.GetService<IDbContext>();
|
||||||
DbContext.Client.QueryFilter.ClearAndBackup<ITenantId>();
|
|
||||||
var sqlSugarOptionsService = scope.ServiceProvider.GetService<ISqlSugarOptionsService>();
|
|
||||||
sqlSugarOptionsService.IgnoreTenant = true;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
var redisCache = scope.ServiceProvider.GetService<RedisCache>();
|
||||||
|
await using var handler = await redisCache.LockAsync($"Scheduled:{key}");
|
||||||
if (handler.Acquired)
|
if (handler.Acquired)
|
||||||
{
|
{
|
||||||
Logger.LogInformation("[{name}]开始执行...", name);
|
Logger.LogDebug("[{name}]开始执行...", name);
|
||||||
|
await DbContext.IgnoreAsync<ITenantId>(async () => { await RunAsync(scope.ServiceProvider); });
|
||||||
await RunAsync(scope.ServiceProvider);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -53,8 +47,6 @@ public abstract class BaseJob : IJob
|
|||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
Logger.LogDebug("[{name}]执行结束...", name);
|
Logger.LogDebug("[{name}]执行结束...", name);
|
||||||
DbContext.Client.QueryFilter.Restore();
|
|
||||||
sqlSugarOptionsService.IgnoreTenant = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,14 +9,13 @@ namespace Dlhis.ScheduledTask.Jobs;
|
|||||||
[RecurringJob(nameof(DeleteApiLogTaskJob), "0 0 0 */6 * ?", Name = "删除过期日志")]
|
[RecurringJob(nameof(DeleteApiLogTaskJob), "0 0 0 */6 * ?", Name = "删除过期日志")]
|
||||||
public class DeleteApiLogTaskJob : BaseJob
|
public class DeleteApiLogTaskJob : BaseJob
|
||||||
{
|
{
|
||||||
|
|
||||||
public DeleteApiLogTaskJob(IServiceProvider serviceProvider) : base(serviceProvider)
|
public DeleteApiLogTaskJob(IServiceProvider serviceProvider) : base(serviceProvider)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task RunAsync(IServiceProvider serviceProvider)
|
protected override async Task RunAsync(IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
var before = DateTime.Now.Date.AddDays(-7);
|
var before = DateTime.Now.Date.AddDays(-60);
|
||||||
await DbContext.ChangeDb("log").Client.Deleteable<ApiLogEntity>().Where(x => x._ts <= before).ExecuteCommandAsync();
|
await DbContext.ChangeDb("log").Client.Deleteable<ApiLogEntity>().Where(x => x._ts <= before).ExecuteCommandAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,4 +89,25 @@ public class RolesController : ControllerBase
|
|||||||
{
|
{
|
||||||
return await _roleService.GetMyRoleAsync(userId);
|
return await _roleService.GetMyRoleAsync(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置混淆
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
[HttpPost("obfuscate")]
|
||||||
|
public async Task SaveRoleObfuscateAsync(RoleObfuscateRequest request)
|
||||||
|
{
|
||||||
|
await _roleService.SaveRoleObfuscateAsync(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取混淆状态
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet("obfuscate")]
|
||||||
|
public async Task<bool> GetRoleObfuscateAsync(int id)
|
||||||
|
{
|
||||||
|
return await _roleService.GetRoleObfuscateAsync(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
using Dlhis.Sys.Identity.Response;
|
using Dlhis.Sys.Identity.Response;
|
||||||
using Dlhis.Sys.Identity.Services;
|
using Dlhis.Sys.Identity.Services;
|
||||||
using Kurisu.AspNetCore.DataAccess.Dto;
|
using Kurisu.AspNetCore.DataAccess.Dto;
|
||||||
|
using Kurisu.AspNetCore.DataAccess.SqlSugar.Aop;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
@@ -86,4 +87,15 @@ public class UsersController : ControllerBase
|
|||||||
{
|
{
|
||||||
await _userService.UploadUserAvatarAsync(request);
|
await _userService.UploadUserAvatarAsync(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 重置登陆
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
[Transactional]
|
||||||
|
[HttpPost("reloginInfo")]
|
||||||
|
public async Task ResetLoginTenantAsync(UserResetLoginTenantRequest request)
|
||||||
|
{
|
||||||
|
await _userService.ResetLoginTenantAsync(request);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
9
Src/Dlhis.Sys/Identity/Request/RoleObfuscateRequest.cs
Normal file
9
Src/Dlhis.Sys/Identity/Request/RoleObfuscateRequest.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Dlhis.Sys.Identity.Request
|
||||||
|
{
|
||||||
|
public class RoleObfuscateRequest
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public bool IsShow { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Dlhis.Sys.Identity.Request;
|
||||||
|
|
||||||
|
public class UserResetLoginTenantRequest
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 租户
|
||||||
|
/// </summary>
|
||||||
|
[Required(ErrorMessage = "{0}不可为空")]
|
||||||
|
public string Tenant { get; set; }
|
||||||
|
}
|
||||||
@@ -17,4 +17,8 @@ public interface IRoleService : IScopeDependency
|
|||||||
Task RemoveRoleAsync(IdRequest request);
|
Task RemoveRoleAsync(IdRequest request);
|
||||||
|
|
||||||
Task AssignRoleAsync(RoleAssignRequest request);
|
Task AssignRoleAsync(RoleAssignRequest request);
|
||||||
|
|
||||||
|
Task SaveRoleObfuscateAsync(RoleObfuscateRequest request);
|
||||||
|
|
||||||
|
Task<bool> GetRoleObfuscateAsync(int id);
|
||||||
}
|
}
|
||||||
@@ -20,4 +20,6 @@ public interface IUserService : IScopeDependency
|
|||||||
Task<List<UserResponse>> GetUserByIdsAsync(List<int> ids);
|
Task<List<UserResponse>> GetUserByIdsAsync(List<int> ids);
|
||||||
|
|
||||||
Task CreateUserAsync(UserCreateRequest request);
|
Task CreateUserAsync(UserCreateRequest request);
|
||||||
|
|
||||||
|
Task ResetLoginTenantAsync(UserResetLoginTenantRequest request);
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,7 @@ using Kurisu.AspNetCore.CustomClass;
|
|||||||
using Kurisu.AspNetCore.DataAccess.Dto;
|
using Kurisu.AspNetCore.DataAccess.Dto;
|
||||||
using Kurisu.AspNetCore.DataAccess.SqlSugar.Extensions;
|
using Kurisu.AspNetCore.DataAccess.SqlSugar.Extensions;
|
||||||
using Kurisu.AspNetCore.DataAccess.SqlSugar.Services;
|
using Kurisu.AspNetCore.DataAccess.SqlSugar.Services;
|
||||||
|
using Kurisu.AspNetCore.Utils;
|
||||||
using Kurisu.AspNetCore.Utils.Extensions;
|
using Kurisu.AspNetCore.Utils.Extensions;
|
||||||
|
|
||||||
namespace Dlhis.Sys.Identity.Services.Implements;
|
namespace Dlhis.Sys.Identity.Services.Implements;
|
||||||
@@ -13,11 +14,13 @@ namespace Dlhis.Sys.Identity.Services.Implements;
|
|||||||
public class RoleService : IRoleService
|
public class RoleService : IRoleService
|
||||||
{
|
{
|
||||||
private readonly ICurrentUser _currentUser;
|
private readonly ICurrentUser _currentUser;
|
||||||
|
private readonly RedisCache _redisCache;
|
||||||
private readonly IDbContext _dbContext;
|
private readonly IDbContext _dbContext;
|
||||||
|
|
||||||
public RoleService(ICurrentUser currentUser, IDbContext dbContext)
|
public RoleService(ICurrentUser currentUser, IDbContext dbContext, RedisCache redisCache)
|
||||||
{
|
{
|
||||||
_currentUser = currentUser;
|
_currentUser = currentUser;
|
||||||
|
_redisCache = redisCache;
|
||||||
_dbContext = dbContext.ChangeDb("identity");
|
_dbContext = dbContext.ChangeDb("identity");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,6 +115,33 @@ public class RoleService : IRoleService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task SaveRoleObfuscateAsync(RoleObfuscateRequest request)
|
||||||
|
{
|
||||||
|
var role = await _dbContext.Queryable<RoleEntity>().FirstAsync(x => x.Id == request.Id);
|
||||||
|
role.ThrowIfNull("角色不存在");
|
||||||
|
|
||||||
|
var key = "datapermission:role";
|
||||||
|
if (request.IsShow)
|
||||||
|
{
|
||||||
|
await _redisCache.ListLeftPushAsync(key, role.Name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _redisCache.ListRemoveAsync(key, role.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<bool> GetRoleObfuscateAsync(int id)
|
||||||
|
{
|
||||||
|
var role = await _dbContext.Queryable<RoleEntity>().FirstAsync(x => x.Id == id);
|
||||||
|
var key = "datapermission:role";
|
||||||
|
var range = await _redisCache.ListRangeAsync(key);
|
||||||
|
|
||||||
|
return range.Any(x => (string)x == role.Name);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task RemoveRoleAsync(IdRequest request)
|
public async Task RemoveRoleAsync(IdRequest request)
|
||||||
{
|
{
|
||||||
var role = await _dbContext.Queryable<RoleEntity>().FirstAsync(x => x.Id == request.Id);
|
var role = await _dbContext.Queryable<RoleEntity>().FirstAsync(x => x.Id == request.Id);
|
||||||
|
|||||||
@@ -16,12 +16,14 @@ public class UserService : IUserService
|
|||||||
{
|
{
|
||||||
private readonly IDbContext _dbContext;
|
private readonly IDbContext _dbContext;
|
||||||
private readonly ICurrentUser _currentUser;
|
private readonly ICurrentUser _currentUser;
|
||||||
|
private readonly ITenantService _tenantService;
|
||||||
private readonly IDbContext _identityDbContext;
|
private readonly IDbContext _identityDbContext;
|
||||||
|
|
||||||
public UserService(IDbContext dbContext, ICurrentUser currentUser)
|
public UserService(IDbContext dbContext, ICurrentUser currentUser, ITenantService tenantService)
|
||||||
{
|
{
|
||||||
_dbContext = dbContext;
|
_dbContext = dbContext;
|
||||||
_currentUser = currentUser;
|
_currentUser = currentUser;
|
||||||
|
_tenantService = tenantService;
|
||||||
_identityDbContext = dbContext.ChangeDb("identity");
|
_identityDbContext = dbContext.ChangeDb("identity");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,4 +137,25 @@ public class UserService : IUserService
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task ResetLoginTenantAsync(UserResetLoginTenantRequest request)
|
||||||
|
{
|
||||||
|
var tenants = await _tenantService.GetMyTenantsAsync();
|
||||||
|
tenants.All(x => x.Code != request.Tenant).ThrowIfTrue($"不可切换登陆:{request.Tenant}");
|
||||||
|
|
||||||
|
var userId = _currentUser.GetIntSubjectId();
|
||||||
|
|
||||||
|
await _dbContext.Updateable<UserClaimEntity>()
|
||||||
|
.SetColumns(x => x.ClaimValue == request.Tenant)
|
||||||
|
.Where(x => x.UserId == userId)
|
||||||
|
.Where(x => x.ClaimType == "tenant")
|
||||||
|
.ExecuteCommandAsync();
|
||||||
|
|
||||||
|
var tenantStr = tenants.Select(x => x.Code).Join(",");
|
||||||
|
await _dbContext.Updateable<UserClaimEntity>()
|
||||||
|
.SetColumns(x => x.ClaimValue == tenantStr)
|
||||||
|
.Where(x => x.UserId == userId)
|
||||||
|
.Where(x => x.ClaimType == "tenants")
|
||||||
|
.ExecuteCommandAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using Kurisu.AspNetCore.Startup;
|
using Kurisu.AspNetCore.Startup;
|
||||||
|
using Kurisu.AspNetCore.Utils.Extensions;
|
||||||
using Microsoft.IdentityModel.Logging;
|
using Microsoft.IdentityModel.Logging;
|
||||||
|
|
||||||
namespace Dlhis.Sys;
|
namespace Dlhis.Sys;
|
||||||
@@ -17,4 +18,10 @@ public class Startup : DefaultStartup
|
|||||||
{
|
{
|
||||||
IdentityModelEventSource.ShowPII = true;
|
IdentityModelEventSource.ShowPII = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void ConfigureServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
base.ConfigureServices(services);
|
||||||
|
services.AddRedis();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -18,6 +18,9 @@
|
|||||||
"SlowSqlTime": 1,
|
"SlowSqlTime": 1,
|
||||||
"EnableSqlLog": false
|
"EnableSqlLog": false
|
||||||
},
|
},
|
||||||
|
"RedisOptions": {
|
||||||
|
"ConnectionString": "192.168.199.150:30115,defaultDatabase=1,password=dlhis123"
|
||||||
|
},
|
||||||
"SwaggerOptions": {
|
"SwaggerOptions": {
|
||||||
"Enable": true,
|
"Enable": true,
|
||||||
"Authority": "http://identity.oauth.svc.cluster.local",
|
"Authority": "http://identity.oauth.svc.cluster.local",
|
||||||
|
|||||||
Reference in New Issue
Block a user