backend: update JWT dll. Added authorize via JWT token

This commit is contained in:
Alexey Bannov 2023-06-29 20:47:47 +03:00
parent eefb4b5e2e
commit d5b009554e
27 changed files with 514 additions and 97 deletions

View File

@ -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" />

View 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;
}
}

View 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());
}
}

View File

@ -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)
{

View File

@ -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;

View 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;
}
}

View 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));
}
}

View File

@ -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()

View File

@ -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)
{

View 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);
}

View File

@ -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" />

View File

@ -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");
}

View File

@ -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)
{

View File

@ -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)

View File

@ -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)
{

View File

@ -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;

View File

@ -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;

View File

@ -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),
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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[]

View File

@ -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>

View File

@ -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>

View File

@ -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);

View File

@ -49,7 +49,8 @@ public abstract class PeopleControllerBase : ApiControllerBase
_userPhotoManager = userPhotoManager;
_httpClientFactory = httpClientFactory;
_httpContextAccessor = httpContextAccessor;
}
}
protected async Task<UserInfo> GetUserInfoAsync(string userNameOrId)
{

View File

@ -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)
{

View File

@ -36,9 +36,7 @@ public class Startup : BaseStartup
base.Configure(app, env);
app.UseRouting();
app.UseAuthentication();
app.UseEndpoints(endpoints =>
{
endpoints.InitializeHttpHandlers();