backend: update JWT dll. Added authorize via JWT token
This commit is contained in:
parent
eefb4b5e2e
commit
d5b009554e
@ -20,7 +20,7 @@
|
||||
<PackageReference Include="AspNetCore.HealthChecks.Redis" Version="6.0.4" />
|
||||
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="6.0.5" />
|
||||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="7.0.0" />
|
||||
<PackageReference Include="NLog.Web.AspNetCore" Version="5.2.1" />
|
||||
|
200
common/ASC.Api.Core/Auth/AuthorizationExtension.cs
Normal file
200
common/ASC.Api.Core/Auth/AuthorizationExtension.cs
Normal file
@ -0,0 +1,200 @@
|
||||
// (c) Copyright Ascensio System SIA 2010-2022
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
using System.Collections.Specialized;
|
||||
|
||||
using Microsoft.IdentityModel.Logging;
|
||||
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
|
||||
|
||||
namespace ASC.Api.Core.Auth;
|
||||
|
||||
public static class AuthorizationExtension
|
||||
{
|
||||
private static readonly NameValueCollection _scopesMap = new NameValueCollection()
|
||||
{
|
||||
{ "GET api/[0-9].[0-9]/files/rooms", "rooms:read,rooms:write" },
|
||||
{ "(POST|PUT|DELETE|UPDATE) api/[0-9].[0-9]/files/rooms", "rooms:write" },
|
||||
{ "GET api/[0-9].[0-9]/files", "files:read,files:write" },
|
||||
{ "(POST|PUT|DELETE|UPDATE) api/[0-9].[0-9]/files", "files:write" },
|
||||
{ "GET api/[0-9].[0-9]/people/@self", "account.self:read,account.self:write" },
|
||||
{ "(POST|PUT|DELETE|UPDATE) api/[0-9].[0-9]/people/@self", "account.self:write" },
|
||||
{ "GET api/[0-9].[0-9]/people", "accounts:read,accounts:write" },
|
||||
{ "(POST|PUT|DELETE|UPDATE) api/[0-9].[0-9]/people", "accounts:write" },
|
||||
};
|
||||
|
||||
private static readonly string[] _allScopes = new[] {
|
||||
"files:read",
|
||||
"files:write",
|
||||
"rooms:read",
|
||||
"rooms:write",
|
||||
"account.self:read",
|
||||
"account.self:write",
|
||||
"accounts:read",
|
||||
"accounts:write" };
|
||||
|
||||
private static string GetAuthorizePolicy(string routePattern, string httpMethod)
|
||||
{
|
||||
foreach (var regexPattern in _scopesMap.AllKeys)
|
||||
{
|
||||
var regex = new Regex(regexPattern);
|
||||
|
||||
if (!regex.IsMatch($"{httpMethod} {routePattern}")) continue;
|
||||
|
||||
var scopes = _scopesMap[regexPattern];
|
||||
|
||||
return scopes;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddJwtBearerAuthentication(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<IAuthorizationHandler, ScopesAuthorizationHandler>();
|
||||
services.AddSingleton<IAuthorizationPolicyProvider, AuthorizationPolicyProvider>();
|
||||
|
||||
services.AddAuthentication()
|
||||
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
|
||||
{
|
||||
#if DEBUG
|
||||
|
||||
options.IncludeErrorDetails = true;
|
||||
#endif
|
||||
options.Configuration = new OpenIdConnectConfiguration();
|
||||
|
||||
IdentityModelEventSource.ShowPII = true;
|
||||
options.MapInboundClaims = false;
|
||||
|
||||
options.TokenValidationParameters.RoleClaimType = "role";
|
||||
options.TokenValidationParameters.NameClaimType = "name";
|
||||
|
||||
options.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
// Clock skew compensates for server time drift.
|
||||
// We recommend 5 minutes or less:
|
||||
ClockSkew = TimeSpan.FromMinutes(5),
|
||||
// Specify the key used to sign the token:
|
||||
// IssuerSigningKey = signingKey,
|
||||
RequireSignedTokens = false,
|
||||
RequireAudience = false,
|
||||
// Ensure the token hasn't expired:
|
||||
RequireExpirationTime = false,
|
||||
ValidateLifetime = false,
|
||||
// Ensure the token audience matches our audience value (default true):
|
||||
ValidateAudience = false,
|
||||
ValidAudience = "4testing",
|
||||
// Ensure the token was issued by a trusted authorization server (default true):
|
||||
ValidateIssuer = false,
|
||||
// ValidIssuer = "https://{yourOktaDomain}/oauth2/default",
|
||||
ValidateIssuerSigningKey = false,
|
||||
SignatureValidator = delegate (string token, TokenValidationParameters parameters)
|
||||
{
|
||||
var jwt = new JwtSecurityToken(token);
|
||||
|
||||
return jwt;
|
||||
}
|
||||
};
|
||||
|
||||
options.Events = new JwtBearerEvents
|
||||
{
|
||||
OnTokenValidated = async ctx =>
|
||||
{
|
||||
using var scope = ctx.HttpContext.RequestServices.CreateScope();
|
||||
|
||||
var securityContext = scope.ServiceProvider.GetService<ASC.Core.SecurityContext>();
|
||||
|
||||
var logger = scope.ServiceProvider.GetService<ILogger<BaseStartup>>();
|
||||
|
||||
logger.DebugOnTokenValidatedCallback();
|
||||
|
||||
if (ctx?.Principal != null)
|
||||
{
|
||||
foreach (var claim in ctx.Principal.Claims)
|
||||
{
|
||||
logger.DebugOnTokenValidatedCallback(claim.Type, claim.Value);
|
||||
}
|
||||
}
|
||||
|
||||
var claimSid = ctx.Principal.Claims.FirstOrDefault(x => x.Type == "userId");
|
||||
|
||||
if (claimSid == null || !Guid.TryParse(claimSid.Value, out var userId))
|
||||
{
|
||||
throw new AuthenticationException($"Claim 'Sid' is not present in JWT");
|
||||
}
|
||||
|
||||
await securityContext.AuthenticateMeWithoutCookieAsync(userId, ctx.Principal.Claims.ToList());
|
||||
},
|
||||
OnMessageReceived = msg =>
|
||||
{
|
||||
using var scope = msg?.HttpContext.RequestServices.CreateScope();
|
||||
|
||||
var logger = scope?.ServiceProvider.GetService<ILogger<BaseStartup>>();
|
||||
|
||||
var token = msg?.Request.Headers.Authorization.ToString();
|
||||
string path = msg?.Request.Path ?? "";
|
||||
|
||||
logger.DebugOnMessageReceivedCallback(path);
|
||||
|
||||
if (!string.IsNullOrEmpty(token))
|
||||
{
|
||||
logger.DebugOnMessageReceivedCallbackAccessToken(token);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.DebugOnMessageReceivedCallbackNoAccessToken();
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
return services;
|
||||
|
||||
}
|
||||
|
||||
public static TBuilder WithRequirementAuthorization<TBuilder>(this TBuilder builder) where TBuilder : IEndpointConventionBuilder
|
||||
{
|
||||
builder.Add(endpointBuilder =>
|
||||
{
|
||||
var httpMethodMetadata = endpointBuilder.Metadata.OfType<HttpMethodMetadata>().FirstOrDefault();
|
||||
var authorizeAttribute = endpointBuilder.Metadata.OfType<AuthorizeAttribute>().FirstOrDefault();
|
||||
var httpMethod = httpMethodMetadata?.HttpMethods.FirstOrDefault();
|
||||
|
||||
var authorizePolicy = GetAuthorizePolicy(((RouteEndpointBuilder)endpointBuilder).RoutePattern.RawText, httpMethod);
|
||||
|
||||
if (authorizeAttribute == null && authorizePolicy != null)
|
||||
{
|
||||
authorizeAttribute = new AuthorizeAttribute(authorizePolicy);
|
||||
|
||||
endpointBuilder.Metadata.Add(authorizeAttribute);
|
||||
}
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
57
common/ASC.Api.Core/Auth/AuthorizationPolicyProvider.cs
Normal file
57
common/ASC.Api.Core/Auth/AuthorizationPolicyProvider.cs
Normal file
@ -0,0 +1,57 @@
|
||||
// (c) Copyright Ascensio System SIA 2010-2022
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
namespace ASC.Api.Core.Auth;
|
||||
public class AuthorizationPolicyProvider : IAuthorizationPolicyProvider
|
||||
{
|
||||
public AuthorizationPolicyProvider(IOptions<AuthorizationOptions> options)
|
||||
{
|
||||
FallbackPolicyProvider = new DefaultAuthorizationPolicyProvider(options);
|
||||
}
|
||||
|
||||
public DefaultAuthorizationPolicyProvider FallbackPolicyProvider { get; }
|
||||
|
||||
public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
|
||||
{
|
||||
var basePolicy = new AuthorizationPolicyBuilder()
|
||||
.RequireAuthenticatedUser();
|
||||
|
||||
basePolicy.AddRequirements(new ScopesRequirement(AuthConstants.Claim_ScopeRootWrite.Value));
|
||||
|
||||
return Task.FromResult(basePolicy.Build());
|
||||
}
|
||||
|
||||
public Task<AuthorizationPolicy> GetFallbackPolicyAsync() => FallbackPolicyProvider.GetFallbackPolicyAsync();
|
||||
|
||||
public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
|
||||
{
|
||||
var policy = new AuthorizationPolicyBuilder();
|
||||
|
||||
policy.AddRequirements(new ScopesRequirement(policyName));
|
||||
|
||||
return Task.FromResult(policy.Build());
|
||||
}
|
||||
}
|
@ -85,10 +85,14 @@ public class BasicAuthHandler : AuthenticationHandler<AuthenticationSchemeOption
|
||||
try
|
||||
{
|
||||
var userInfo = await _userManager.GetUserByEmailAsync(authUsername);
|
||||
var passwordHash = _passwordHasher.GetClientPassword(authPassword);
|
||||
|
||||
await _securityContext.AuthenticateMeAsync(userInfo.Email, passwordHash);
|
||||
var passwordHash = _passwordHasher.GetClientPassword(authPassword);
|
||||
|
||||
var claims = new List<Claim>()
|
||||
{
|
||||
AuthConstants.Claim_ScopeRootWrite
|
||||
};
|
||||
|
||||
await _securityContext.AuthenticateMeAsync(userInfo.Email, passwordHash, null, claims);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
@ -81,9 +81,10 @@ public class ConfirmAuthHandler : AuthenticationHandler<AuthenticationSchemeOpti
|
||||
|
||||
var claims = new List<Claim>()
|
||||
{
|
||||
new Claim(ClaimTypes.Role, emailValidationKeyModel.Type.ToString())
|
||||
new Claim(ClaimTypes.Role, emailValidationKeyModel.Type.ToString()),
|
||||
AuthConstants.Claim_ScopeRootWrite
|
||||
};
|
||||
|
||||
|
||||
if (checkKeyResult == EmailValidationKeyProvider.ValidationResult.Ok)
|
||||
{
|
||||
Guid userId;
|
||||
|
81
common/ASC.Api.Core/Auth/ScopesAuthorizationHandler.cs
Normal file
81
common/ASC.Api.Core/Auth/ScopesAuthorizationHandler.cs
Normal file
@ -0,0 +1,81 @@
|
||||
// (c) Copyright Ascensio System SIA 2010-2022
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
namespace ASC.Api.Core.Auth;
|
||||
|
||||
public class ScopesAuthorizationHandler : AuthorizationHandler<ScopesRequirement>
|
||||
{
|
||||
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ScopesRequirement requirement)
|
||||
{
|
||||
if (!context.User.Identity.IsAuthenticated)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
if (context.HasSucceeded)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
if (context.User == null || requirement == null || string.IsNullOrWhiteSpace(requirement.Scopes))
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
var requirementScopes = requirement.Scopes.Split(",", StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (requirementScopes?.Any() != true)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
var expectedRequirements = requirementScopes.ToList();
|
||||
|
||||
if (expectedRequirements.Count == 0)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
var userScopeClaims = context.User.Claims?.Where(c => string.Equals(c.Type, "scope", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
foreach (var claim in userScopeClaims ?? Enumerable.Empty<Claim>())
|
||||
{
|
||||
var match = expectedRequirements
|
||||
.Where(r => string.Equals(r, claim.Value, StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(AuthConstants.Claim_ScopeRootWrite.Value, claim.Value, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (match.Any())
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
36
common/ASC.Api.Core/Auth/ScopesRequirement.cs
Normal file
36
common/ASC.Api.Core/Auth/ScopesRequirement.cs
Normal file
@ -0,0 +1,36 @@
|
||||
// (c) Copyright Ascensio System SIA 2010-2022
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
namespace ASC.Api.Core.Auth;
|
||||
public class ScopesRequirement : IAuthorizationRequirement
|
||||
{
|
||||
public string Scopes { get; }
|
||||
|
||||
public ScopesRequirement(string scopes)
|
||||
{
|
||||
Scopes = scopes ?? throw new ArgumentNullException(nameof(scopes));
|
||||
}
|
||||
}
|
@ -199,45 +199,14 @@ public abstract class BaseStartup
|
||||
config.Filters.Add(new CustomExceptionFilterAttribute());
|
||||
config.Filters.Add(new TypeFilterAttribute(typeof(WebhooksGlobalFilterAttribute)));
|
||||
});
|
||||
|
||||
var authBuilder = services.AddAuthentication(options =>
|
||||
|
||||
services.AddAuthentication(options =>
|
||||
{
|
||||
options.DefaultScheme = MultiAuthSchemes;
|
||||
options.DefaultChallengeScheme = MultiAuthSchemes;
|
||||
}).AddScheme<AuthenticationSchemeOptions, CookieAuthHandler>(CookieAuthenticationDefaults.AuthenticationScheme, a => { })
|
||||
.AddScheme<AuthenticationSchemeOptions, BasicAuthHandler>(BasicAuthScheme, a => { })
|
||||
.AddScheme<AuthenticationSchemeOptions, ConfirmAuthHandler>("confirm", a => { })
|
||||
.AddJwtBearer("Bearer", options =>
|
||||
{
|
||||
options.Authority = _configuration["core:oidc:authority"];
|
||||
options.IncludeErrorDetails = true;
|
||||
|
||||
options.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateAudience = false
|
||||
};
|
||||
|
||||
options.Events = new JwtBearerEvents
|
||||
{
|
||||
OnTokenValidated = async ctx =>
|
||||
{
|
||||
using var scope = ctx.HttpContext.RequestServices.CreateScope();
|
||||
|
||||
var securityContext = scope.ServiceProvider.GetService<ASC.Core.SecurityContext>();
|
||||
|
||||
var claimUserId = ctx.Principal.FindFirstValue("userId");
|
||||
|
||||
if (string.IsNullOrEmpty(claimUserId))
|
||||
{
|
||||
throw new Exception("Claim 'UserId' is not present in claim list");
|
||||
}
|
||||
|
||||
var userId = new Guid(claimUserId);
|
||||
|
||||
await securityContext.AuthenticateMeWithoutCookieAsync(userId, ctx.Principal.Claims.ToList());
|
||||
}
|
||||
};
|
||||
})
|
||||
.AddPolicyScheme(MultiAuthSchemes, JwtBearerDefaults.AuthenticationScheme, options =>
|
||||
{
|
||||
options.ForwardDefaultSelector = context =>
|
||||
@ -261,11 +230,7 @@ public abstract class BaseStartup
|
||||
|
||||
if (jwtHandler.CanReadToken(token))
|
||||
{
|
||||
var issuer = jwtHandler.ReadJwtToken(token).Issuer;
|
||||
if (!string.IsNullOrEmpty(issuer) && issuer.Equals(_configuration["core:oidc:authority"]))
|
||||
{
|
||||
return JwtBearerDefaults.AuthenticationScheme;
|
||||
}
|
||||
return JwtBearerDefaults.AuthenticationScheme;
|
||||
}
|
||||
}
|
||||
|
||||
@ -273,6 +238,8 @@ public abstract class BaseStartup
|
||||
};
|
||||
});
|
||||
|
||||
services.AddJwtBearerAuthentication();
|
||||
|
||||
services.AddAutoMapper(GetAutoMapperProfileAssemblies());
|
||||
|
||||
if (!_hostEnvironment.IsDevelopment())
|
||||
@ -305,16 +272,16 @@ public abstract class BaseStartup
|
||||
|
||||
app.UseSynchronizationContextMiddleware();
|
||||
|
||||
app.UseAuthentication();
|
||||
|
||||
app.UseAuthorization();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseCultureMiddleware();
|
||||
|
||||
app.UseLoggerMiddleware();
|
||||
|
||||
|
||||
app.UseEndpoints(async endpoints =>
|
||||
{
|
||||
|
||||
await endpoints.MapCustomAsync(WebhooksEnabled, app.ApplicationServices);
|
||||
|
||||
endpoints.MapHealthChecks("/health", new HealthCheckOptions()
|
||||
|
@ -25,7 +25,6 @@
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
namespace ASC.Api.Core.Core;
|
||||
|
||||
public class CustomEndpointDataSource : EndpointDataSource
|
||||
{
|
||||
private readonly EndpointDataSource _source;
|
||||
@ -77,7 +76,8 @@ public static class EndpointExtension
|
||||
{
|
||||
public static async Task<IEndpointRouteBuilder> MapCustomAsync(this IEndpointRouteBuilder endpoints, bool webhooksEnabled = false, IServiceProvider serviceProvider = null)
|
||||
{
|
||||
endpoints.MapControllers().RequireAuthorization();
|
||||
endpoints.MapControllers()
|
||||
.WithRequirementAuthorization();
|
||||
|
||||
if (webhooksEnabled && serviceProvider != null)
|
||||
{
|
||||
|
44
common/ASC.Api.Core/Log/BaseStartupLogger.cs
Normal file
44
common/ASC.Api.Core/Log/BaseStartupLogger.cs
Normal file
@ -0,0 +1,44 @@
|
||||
// (c) Copyright Ascensio System SIA 2010-2022
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
namespace ASC.Api.Core.Log;
|
||||
internal static partial class BaseStartupLogger
|
||||
{
|
||||
[LoggerMessage(Level = LogLevel.Debug, Message = "OnTokenValidatedCallback: Claims from the access token")]
|
||||
public static partial void DebugOnTokenValidatedCallback(this ILogger<BaseStartup> logger);
|
||||
|
||||
[LoggerMessage(Level = LogLevel.Debug, Message = "OnTokenValidatedCallback: {claimType} - {claimValue}")]
|
||||
public static partial void DebugOnTokenValidatedCallback(this ILogger<BaseStartup> logger, string claimType, string claimValue);
|
||||
|
||||
[LoggerMessage(Level = LogLevel.Debug, Message = "OnMessageReceived: Access token from {url}")]
|
||||
public static partial void DebugOnMessageReceivedCallback(this ILogger<BaseStartup> logger, string url);
|
||||
|
||||
[LoggerMessage(Level = LogLevel.Debug, Message = "Access token: {token}")]
|
||||
public static partial void DebugOnMessageReceivedCallbackAccessToken(this ILogger<BaseStartup> logger, String token);
|
||||
|
||||
[LoggerMessage(Level = LogLevel.Debug, Message = "Token: No access token provided")]
|
||||
public static partial void DebugOnMessageReceivedCallbackNoAccessToken(this ILogger<BaseStartup> logger);
|
||||
}
|
@ -36,7 +36,7 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="JWT" Version="9.0.3" />
|
||||
<PackageReference Include="JWT" Version="10.0.2" />
|
||||
<PackageReference Include="log4net" Version="2.0.15" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="7.0.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
|
||||
|
@ -24,9 +24,11 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace ASC.Common.Security.Authorizing;
|
||||
|
||||
public static class Constants
|
||||
public static class AuthConstants
|
||||
{
|
||||
public static readonly Role DocSpaceAdmin = new Role(new Guid("cd84e66b-b803-40fc-99f9-b2969a54a1de"), "DocSpaceAdmin");
|
||||
public static readonly Role Everyone = new Role(new Guid("c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e"), "Everyone");
|
||||
@ -36,4 +38,7 @@ public static class Constants
|
||||
public static readonly Role Member = new Role(new Guid("ba74ca02-873f-43dc-8470-8620c156bc67"), "Member");
|
||||
public static readonly Role Owner = new Role(new Guid("bba32183-a14d-48ed-9d39-c6b4d8925fbf"), "Owner");
|
||||
public static readonly Role Self = new Role(new Guid("5d5b7260-f7f7-49f1-a1c9-95fbb6a12604"), "Self");
|
||||
|
||||
public static readonly Claim Claim_ScopeRootWrite = new Claim("scope", "root_write");
|
||||
|
||||
}
|
@ -60,7 +60,7 @@ public static class JsonWebToken
|
||||
}
|
||||
}
|
||||
|
||||
public class DictionaryStringObjectJsonConverter : JsonConverter<Dictionary<string, object>>
|
||||
public class DictionaryStringObjectJsonConverter : System.Text.Json.Serialization.JsonConverter<Dictionary<string, object>>
|
||||
{
|
||||
public override Dictionary<string, object> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
|
@ -24,7 +24,7 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
using Constants = ASC.Common.Security.Authorizing.Constants;
|
||||
using AuthConstants = ASC.Common.Security.Authorizing.AuthConstants;
|
||||
|
||||
namespace ASC.Core;
|
||||
|
||||
@ -37,10 +37,10 @@ public class SubscriptionManager
|
||||
private static readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1);
|
||||
public static readonly List<Guid> Groups = Groups = new List<Guid>
|
||||
{
|
||||
Constants.DocSpaceAdmin.ID,
|
||||
Constants.Everyone.ID,
|
||||
Constants.RoomAdmin.ID,
|
||||
Constants.Collaborator.ID,
|
||||
AuthConstants.DocSpaceAdmin.ID,
|
||||
AuthConstants.Everyone.ID,
|
||||
AuthConstants.RoomAdmin.ID,
|
||||
AuthConstants.Collaborator.ID,
|
||||
};
|
||||
|
||||
public SubscriptionManager(CachedSubscriptionService service, TenantManager tenantManager, ICache cache)
|
||||
|
@ -84,7 +84,7 @@ public class SecurityContext
|
||||
}
|
||||
|
||||
|
||||
public async Task<string> AuthenticateMeAsync(string login, string passwordHash, Func<Task<int>> funcLoginEvent = null)
|
||||
public async Task<string> AuthenticateMeAsync(string login, string passwordHash, Func<Task<int>> funcLoginEvent = null, List<Claim> additionalClaims = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(login);
|
||||
ArgumentNullException.ThrowIfNull(passwordHash);
|
||||
@ -92,7 +92,7 @@ public class SecurityContext
|
||||
var tenantid = await _tenantManager.GetCurrentTenantIdAsync();
|
||||
var u = await _userManager.GetUsersByPasswordHashAsync(tenantid, login, passwordHash);
|
||||
|
||||
return await AuthenticateMeAsync(new UserAccount(u, tenantid, _userFormatter), funcLoginEvent);
|
||||
return await AuthenticateMeAsync(new UserAccount(u, tenantid, _userFormatter), funcLoginEvent,additionalClaims);
|
||||
}
|
||||
|
||||
public async Task<bool> AuthenticateMe(string cookie)
|
||||
@ -168,7 +168,13 @@ public class SecurityContext
|
||||
return false;
|
||||
}
|
||||
|
||||
await AuthenticateMeWithoutCookieAsync(new UserAccount(new UserInfo { Id = userid }, tenant, _userFormatter));
|
||||
var claims = new List<Claim>()
|
||||
{
|
||||
AuthConstants.Claim_ScopeRootWrite
|
||||
};
|
||||
|
||||
await AuthenticateMeWithoutCookieAsync(new UserAccount(new UserInfo { Id = userid }, tenant, _userFormatter), claims);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (InvalidCredentialException ice)
|
||||
@ -381,10 +387,13 @@ public class AuthContext
|
||||
|
||||
internal ClaimsPrincipal Principal
|
||||
{
|
||||
get => CustomSynchronizationContext.CurrentContext.CurrentPrincipal as ClaimsPrincipal ?? HttpContextAccessor?.HttpContext?.User;
|
||||
get => CustomSynchronizationContext.CurrentContext?.CurrentPrincipal as ClaimsPrincipal ?? HttpContextAccessor?.HttpContext?.User;
|
||||
set
|
||||
{
|
||||
CustomSynchronizationContext.CurrentContext.CurrentPrincipal = value;
|
||||
if (CustomSynchronizationContext.CurrentContext != null)
|
||||
{
|
||||
CustomSynchronizationContext.CurrentContext.CurrentPrincipal = value;
|
||||
}
|
||||
|
||||
if (HttpContextAccessor?.HttpContext != null)
|
||||
{
|
||||
|
@ -56,7 +56,7 @@ public class AzManager
|
||||
|
||||
internal async Task<AzManagerAcl> GetAzManagerAclAsync(ISubject subject, IAction action, ISecurityObjectId objectId, ISecurityObjectProvider securityObjProvider)
|
||||
{
|
||||
if (action.AdministratorAlwaysAllow && (Constants.DocSpaceAdmin.ID == subject.ID || await _roleProvider.IsSubjectInRoleAsync(subject, Constants.DocSpaceAdmin)
|
||||
if (action.AdministratorAlwaysAllow && (AuthConstants.DocSpaceAdmin.ID == subject.ID || await _roleProvider.IsSubjectInRoleAsync(subject, AuthConstants.DocSpaceAdmin)
|
||||
|| (objectId is SecurityObject obj && await obj.IsMatchDefaultRulesAsync(subject, action, _roleProvider))))
|
||||
{
|
||||
return AzManagerAcl.Allow;
|
||||
|
@ -24,7 +24,7 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
using Constants = ASC.Common.Security.Authorizing.Constants;
|
||||
using AuthConstants = ASC.Common.Security.Authorizing.AuthConstants;
|
||||
|
||||
namespace ASC.Core.Security.Authorizing;
|
||||
|
||||
@ -53,9 +53,9 @@ class RoleProvider : IRoleProvider
|
||||
}
|
||||
}
|
||||
|
||||
if (roles.Any(r => r.ID == Constants.Collaborator.ID || r.ID == Constants.User.ID))
|
||||
if (roles.Any(r => r.ID == AuthConstants.Collaborator.ID || r.ID == AuthConstants.User.ID))
|
||||
{
|
||||
roles = roles.Where(r => r.ID != Constants.RoomAdmin.ID).ToList();
|
||||
roles = roles.Where(r => r.ID != AuthConstants.RoomAdmin.ID).ToList();
|
||||
}
|
||||
|
||||
return roles;
|
||||
|
@ -25,7 +25,7 @@
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
using UserConstants = ASC.Core.Users.Constants;
|
||||
using Constants = ASC.Common.Security.Authorizing.Constants;
|
||||
using AuthConstants = ASC.Common.Security.Authorizing.AuthConstants;
|
||||
|
||||
namespace ASC.Core.Common.Security;
|
||||
|
||||
@ -34,26 +34,26 @@ public static class Security
|
||||
public static readonly Dictionary<Guid, Dictionary<Guid, HashSet<Rule>>> Rules = new()
|
||||
{
|
||||
{
|
||||
Constants.RoomAdmin.ID, new Dictionary<Guid, HashSet<Rule>>
|
||||
AuthConstants.RoomAdmin.ID, new Dictionary<Guid, HashSet<Rule>>
|
||||
{
|
||||
{
|
||||
Constants.User.ID, new HashSet<Rule>
|
||||
AuthConstants.User.ID, new HashSet<Rule>
|
||||
{
|
||||
new(UserConstants.Action_EditGroups.ID, Constants.User),
|
||||
new(UserConstants.Action_EditGroups.ID, AuthConstants.User),
|
||||
new(UserConstants.Action_AddRemoveUser.ID),
|
||||
}
|
||||
},
|
||||
{
|
||||
Constants.RoomAdmin.ID, new HashSet<Rule>
|
||||
AuthConstants.RoomAdmin.ID, new HashSet<Rule>
|
||||
{
|
||||
new(UserConstants.Action_EditGroups.ID, Constants.User),
|
||||
new(UserConstants.Action_EditGroups.ID, AuthConstants.User),
|
||||
new(UserConstants.Action_AddRemoveUser.ID),
|
||||
}
|
||||
},
|
||||
{
|
||||
Constants.Collaborator.ID, new HashSet<Rule>
|
||||
AuthConstants.Collaborator.ID, new HashSet<Rule>
|
||||
{
|
||||
new(UserConstants.Action_EditGroups.ID, Constants.Collaborator),
|
||||
new(UserConstants.Action_EditGroups.ID, AuthConstants.Collaborator),
|
||||
new(UserConstants.Action_AddRemoveUser.ID),
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
using AuthConstants = ASC.Common.Security.Authorizing.Constants;
|
||||
using AuthConstants = ASC.Common.Security.Authorizing.AuthConstants;
|
||||
|
||||
namespace ASC.Core.Common.Security;
|
||||
|
||||
|
@ -24,7 +24,7 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
using AuthConstants = ASC.Common.Security.Authorizing.Constants;
|
||||
using AuthConstants = ASC.Common.Security.Authorizing.AuthConstants;
|
||||
|
||||
namespace ASC.Core.Users;
|
||||
|
||||
|
@ -25,7 +25,6 @@
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
using Action = ASC.Common.Security.Authorizing.Action;
|
||||
using AuthConst = ASC.Common.Security.Authorizing.Constants;
|
||||
|
||||
namespace ASC.Core.Users;
|
||||
|
||||
@ -65,32 +64,32 @@ public sealed class Constants
|
||||
|
||||
public static readonly GroupInfo GroupEveryone = new GroupInfo(SysGroupCategoryId)
|
||||
{
|
||||
ID = AuthConst.Everyone.ID,
|
||||
Name = AuthConst.Everyone.Name,
|
||||
ID = AuthConstants.Everyone.ID,
|
||||
Name = AuthConstants.Everyone.Name,
|
||||
};
|
||||
|
||||
public static readonly GroupInfo GroupUser = new GroupInfo(SysGroupCategoryId)
|
||||
{
|
||||
ID = AuthConst.User.ID,
|
||||
Name = AuthConst.User.Name,
|
||||
ID = AuthConstants.User.ID,
|
||||
Name = AuthConstants.User.Name,
|
||||
};
|
||||
|
||||
public static readonly GroupInfo GroupManager = new GroupInfo(SysGroupCategoryId)
|
||||
{
|
||||
ID = AuthConst.RoomAdmin.ID,
|
||||
Name = AuthConst.RoomAdmin.Name,
|
||||
ID = AuthConstants.RoomAdmin.ID,
|
||||
Name = AuthConstants.RoomAdmin.Name,
|
||||
};
|
||||
|
||||
public static readonly GroupInfo GroupAdmin = new GroupInfo(SysGroupCategoryId)
|
||||
{
|
||||
ID = AuthConst.DocSpaceAdmin.ID,
|
||||
Name = AuthConst.DocSpaceAdmin.Name,
|
||||
ID = AuthConstants.DocSpaceAdmin.ID,
|
||||
Name = AuthConstants.DocSpaceAdmin.Name,
|
||||
};
|
||||
|
||||
public static readonly GroupInfo GroupCollaborator = new(SysGroupCategoryId)
|
||||
{
|
||||
ID = AuthConst.Collaborator.ID,
|
||||
Name = AuthConst.Collaborator.Name,
|
||||
ID = AuthConstants.Collaborator.ID,
|
||||
Name = AuthConstants.Collaborator.Name,
|
||||
};
|
||||
|
||||
public static readonly GroupInfo[] BuildinGroups = new[]
|
||||
|
@ -31,7 +31,7 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.25.1" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.31.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -44,6 +44,8 @@
|
||||
<logger name="ASC.SQL" minlevel="Debug" writeTo="sql" final="true" />
|
||||
<logger name="ASC*" minlevel="Debug" writeTo="web" />
|
||||
<logger name="Microsoft.AspNetCore.Hosting.Diagnostics" minlevel="Debug" writeTo="ownFile-web" final="true" />
|
||||
<logger name="Microsoft.*" maxlevel="Off" final="true" />
|
||||
<logger name="Microsoft.AspNetCore.Authentication" minlevel="Debug" writeTo="ownFile-web" final="true" />
|
||||
<logger name="Microsoft.AspNetCore.Authorization" minlevel="Debug" writeTo="ownFile-web" final="true" />
|
||||
<logger name="Microsoft.*" maxlevel="off" final="true" />
|
||||
</rules>
|
||||
</nlog>
|
@ -665,7 +665,7 @@ public class FileHandlerService
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
var stringPayload = JwtBuilder.Create()
|
||||
.WithAlgorithm(new HMACSHA256Algorithm())
|
||||
.WithSerializer(new JwtSerializer())
|
||||
.WithJsonSerializer(new JwtSerializer())
|
||||
.WithSecret(_fileUtility.SignatureSecret)
|
||||
.MustVerifySignature()
|
||||
.Decode(header);
|
||||
@ -779,8 +779,8 @@ public class FileHandlerService
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
var stringPayload = JwtBuilder.Create()
|
||||
.WithAlgorithm(new HMACSHA256Algorithm())
|
||||
.WithSerializer(new JwtSerializer())
|
||||
.WithSecret(_fileUtility.SignatureSecret)
|
||||
.WithJsonSerializer(new JwtSerializer())
|
||||
.MustVerifySignature()
|
||||
.Decode(header);
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
@ -1516,7 +1516,7 @@ public class FileHandlerService
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
var dataString = JwtBuilder.Create()
|
||||
.WithAlgorithm(new HMACSHA256Algorithm())
|
||||
.WithSerializer(new JwtSerializer())
|
||||
.WithJsonSerializer(new JwtSerializer())
|
||||
.WithSecret(_fileUtility.SignatureSecret)
|
||||
.MustVerifySignature()
|
||||
.Decode(fileData.Token);
|
||||
@ -1551,7 +1551,7 @@ public class FileHandlerService
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
var stringPayload = JwtBuilder.Create()
|
||||
.WithAlgorithm(new HMACSHA256Algorithm())
|
||||
.WithSerializer(new JwtSerializer())
|
||||
.WithJsonSerializer(new JwtSerializer())
|
||||
.WithSecret(_fileUtility.SignatureSecret)
|
||||
.MustVerifySignature()
|
||||
.Decode(header);
|
||||
|
@ -49,7 +49,8 @@ public abstract class PeopleControllerBase : ApiControllerBase
|
||||
_userPhotoManager = userPhotoManager;
|
||||
_httpClientFactory = httpClientFactory;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected async Task<UserInfo> GetUserInfoAsync(string userNameOrId)
|
||||
{
|
||||
|
@ -144,6 +144,19 @@ public class UserController : PeopleControllerBase
|
||||
_usersQuotaSyncOperation = usersQuotaSyncOperation;
|
||||
}
|
||||
|
||||
[HttpGet("tokendiagnostics")]
|
||||
public object GetClaims()
|
||||
{
|
||||
var result = new
|
||||
{
|
||||
Name = User.Identity?.Name ?? "Unknown Name",
|
||||
Claims = (from c in User.Claims select c.Type + ":" + c.Value).ToList()
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
[HttpPost("active")]
|
||||
public async Task<EmployeeFullDto> AddMemberAsActivatedAsync(MemberRequestDto inDto)
|
||||
{
|
||||
|
@ -36,9 +36,7 @@ public class Startup : BaseStartup
|
||||
base.Configure(app, env);
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseAuthentication();
|
||||
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.InitializeHttpHandlers();
|
||||
|
Loading…
Reference in New Issue
Block a user