diff --git a/common/ASC.Api.Core/ASC.Api.Core.csproj b/common/ASC.Api.Core/ASC.Api.Core.csproj
index 1be535aed0..8bacc9e7c1 100644
--- a/common/ASC.Api.Core/ASC.Api.Core.csproj
+++ b/common/ASC.Api.Core/ASC.Api.Core.csproj
@@ -20,7 +20,7 @@
-
+
diff --git a/common/ASC.Api.Core/Auth/AuthorizationExtension.cs b/common/ASC.Api.Core/Auth/AuthorizationExtension.cs
new file mode 100644
index 0000000000..02419c6285
--- /dev/null
+++ b/common/ASC.Api.Core/Auth/AuthorizationExtension.cs
@@ -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();
+ services.AddSingleton();
+
+ 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();
+
+ var logger = scope.ServiceProvider.GetService>();
+
+ 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>();
+
+ 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(this TBuilder builder) where TBuilder : IEndpointConventionBuilder
+ {
+ builder.Add(endpointBuilder =>
+ {
+ var httpMethodMetadata = endpointBuilder.Metadata.OfType().FirstOrDefault();
+ var authorizeAttribute = endpointBuilder.Metadata.OfType().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;
+ }
+}
diff --git a/common/ASC.Api.Core/Auth/AuthorizationPolicyProvider.cs b/common/ASC.Api.Core/Auth/AuthorizationPolicyProvider.cs
new file mode 100644
index 0000000000..dea088c4ef
--- /dev/null
+++ b/common/ASC.Api.Core/Auth/AuthorizationPolicyProvider.cs
@@ -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 options)
+ {
+ FallbackPolicyProvider = new DefaultAuthorizationPolicyProvider(options);
+ }
+
+ public DefaultAuthorizationPolicyProvider FallbackPolicyProvider { get; }
+
+ public Task GetDefaultPolicyAsync()
+ {
+ var basePolicy = new AuthorizationPolicyBuilder()
+ .RequireAuthenticatedUser();
+
+ basePolicy.AddRequirements(new ScopesRequirement(AuthConstants.Claim_ScopeRootWrite.Value));
+
+ return Task.FromResult(basePolicy.Build());
+ }
+
+ public Task GetFallbackPolicyAsync() => FallbackPolicyProvider.GetFallbackPolicyAsync();
+
+ public Task GetPolicyAsync(string policyName)
+ {
+ var policy = new AuthorizationPolicyBuilder();
+
+ policy.AddRequirements(new ScopesRequirement(policyName));
+
+ return Task.FromResult(policy.Build());
+ }
+}
\ No newline at end of file
diff --git a/common/ASC.Api.Core/Auth/BasicAuthHandler.cs b/common/ASC.Api.Core/Auth/BasicAuthHandler.cs
index c70bb1c8fb..4e0a453101 100644
--- a/common/ASC.Api.Core/Auth/BasicAuthHandler.cs
+++ b/common/ASC.Api.Core/Auth/BasicAuthHandler.cs
@@ -85,10 +85,14 @@ public class BasicAuthHandler : AuthenticationHandler()
+ {
+ AuthConstants.Claim_ScopeRootWrite
+ };
+ await _securityContext.AuthenticateMeAsync(userInfo.Email, passwordHash, null, claims);
}
catch (Exception)
{
diff --git a/common/ASC.Api.Core/Auth/ConfirmAuthHandler.cs b/common/ASC.Api.Core/Auth/ConfirmAuthHandler.cs
index 822fd7f5e2..18c37fafca 100644
--- a/common/ASC.Api.Core/Auth/ConfirmAuthHandler.cs
+++ b/common/ASC.Api.Core/Auth/ConfirmAuthHandler.cs
@@ -81,9 +81,10 @@ public class ConfirmAuthHandler : AuthenticationHandler()
{
- new Claim(ClaimTypes.Role, emailValidationKeyModel.Type.ToString())
+ new Claim(ClaimTypes.Role, emailValidationKeyModel.Type.ToString()),
+ AuthConstants.Claim_ScopeRootWrite
};
-
+
if (checkKeyResult == EmailValidationKeyProvider.ValidationResult.Ok)
{
Guid userId;
diff --git a/common/ASC.Api.Core/Auth/ScopesAuthorizationHandler.cs b/common/ASC.Api.Core/Auth/ScopesAuthorizationHandler.cs
new file mode 100644
index 0000000000..0a5e565ee2
--- /dev/null
+++ b/common/ASC.Api.Core/Auth/ScopesAuthorizationHandler.cs
@@ -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
+{
+ 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())
+ {
+ 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;
+ }
+}
+
diff --git a/common/ASC.Api.Core/Auth/ScopesRequirement.cs b/common/ASC.Api.Core/Auth/ScopesRequirement.cs
new file mode 100644
index 0000000000..4d2e1f85be
--- /dev/null
+++ b/common/ASC.Api.Core/Auth/ScopesRequirement.cs
@@ -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));
+ }
+}
\ No newline at end of file
diff --git a/common/ASC.Api.Core/Core/BaseStartup.cs b/common/ASC.Api.Core/Core/BaseStartup.cs
index 3790b52911..01ef3815b5 100644
--- a/common/ASC.Api.Core/Core/BaseStartup.cs
+++ b/common/ASC.Api.Core/Core/BaseStartup.cs
@@ -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(CookieAuthenticationDefaults.AuthenticationScheme, a => { })
.AddScheme(BasicAuthScheme, a => { })
.AddScheme("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();
-
- 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()
diff --git a/common/ASC.Api.Core/Core/CustomEndpointDataSource.cs b/common/ASC.Api.Core/Core/CustomEndpointDataSource.cs
index 2338a98aa4..8a98150df0 100644
--- a/common/ASC.Api.Core/Core/CustomEndpointDataSource.cs
+++ b/common/ASC.Api.Core/Core/CustomEndpointDataSource.cs
@@ -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 MapCustomAsync(this IEndpointRouteBuilder endpoints, bool webhooksEnabled = false, IServiceProvider serviceProvider = null)
{
- endpoints.MapControllers().RequireAuthorization();
+ endpoints.MapControllers()
+ .WithRequirementAuthorization();
if (webhooksEnabled && serviceProvider != null)
{
diff --git a/common/ASC.Api.Core/Log/BaseStartupLogger.cs b/common/ASC.Api.Core/Log/BaseStartupLogger.cs
new file mode 100644
index 0000000000..034666efb1
--- /dev/null
+++ b/common/ASC.Api.Core/Log/BaseStartupLogger.cs
@@ -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 logger);
+
+ [LoggerMessage(Level = LogLevel.Debug, Message = "OnTokenValidatedCallback: {claimType} - {claimValue}")]
+ public static partial void DebugOnTokenValidatedCallback(this ILogger logger, string claimType, string claimValue);
+
+ [LoggerMessage(Level = LogLevel.Debug, Message = "OnMessageReceived: Access token from {url}")]
+ public static partial void DebugOnMessageReceivedCallback(this ILogger logger, string url);
+
+ [LoggerMessage(Level = LogLevel.Debug, Message = "Access token: {token}")]
+ public static partial void DebugOnMessageReceivedCallbackAccessToken(this ILogger logger, String token);
+
+ [LoggerMessage(Level = LogLevel.Debug, Message = "Token: No access token provided")]
+ public static partial void DebugOnMessageReceivedCallbackNoAccessToken(this ILogger logger);
+}
diff --git a/common/ASC.Common/ASC.Common.csproj b/common/ASC.Common/ASC.Common.csproj
index c84ddae3cb..c57ca0ab98 100644
--- a/common/ASC.Common/ASC.Common.csproj
+++ b/common/ASC.Common/ASC.Common.csproj
@@ -36,7 +36,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/common/ASC.Common/Security/Authorizing/Constants.cs b/common/ASC.Common/Security/Authorizing/AuthConstants.cs
similarity index 94%
rename from common/ASC.Common/Security/Authorizing/Constants.cs
rename to common/ASC.Common/Security/Authorizing/AuthConstants.cs
index 9654f48651..98a1933f1d 100644
--- a/common/ASC.Common/Security/Authorizing/Constants.cs
+++ b/common/ASC.Common/Security/Authorizing/AuthConstants.cs
@@ -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");
+
}
diff --git a/common/ASC.Common/Utils/JsonWebToken.cs b/common/ASC.Common/Utils/JsonWebToken.cs
index 18b5409087..65e350b239 100644
--- a/common/ASC.Common/Utils/JsonWebToken.cs
+++ b/common/ASC.Common/Utils/JsonWebToken.cs
@@ -60,7 +60,7 @@ public static class JsonWebToken
}
}
-public class DictionaryStringObjectJsonConverter : JsonConverter>
+public class DictionaryStringObjectJsonConverter : System.Text.Json.Serialization.JsonConverter>
{
public override Dictionary Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
diff --git a/common/ASC.Core.Common/Context/Impl/SubscriptionManager.cs b/common/ASC.Core.Common/Context/Impl/SubscriptionManager.cs
index 25e87c02e2..675660d8dd 100644
--- a/common/ASC.Core.Common/Context/Impl/SubscriptionManager.cs
+++ b/common/ASC.Core.Common/Context/Impl/SubscriptionManager.cs
@@ -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 Groups = Groups = new List
{
- 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)
diff --git a/common/ASC.Core.Common/Context/SecurityContext.cs b/common/ASC.Core.Common/Context/SecurityContext.cs
index e3f9cfcd7b..1ea1dfafcf 100644
--- a/common/ASC.Core.Common/Context/SecurityContext.cs
+++ b/common/ASC.Core.Common/Context/SecurityContext.cs
@@ -84,7 +84,7 @@ public class SecurityContext
}
- public async Task AuthenticateMeAsync(string login, string passwordHash, Func> funcLoginEvent = null)
+ public async Task AuthenticateMeAsync(string login, string passwordHash, Func> funcLoginEvent = null, List 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 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()
+ {
+ 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)
{
diff --git a/common/ASC.Core.Common/Security/Authorizing/AzManager.cs b/common/ASC.Core.Common/Security/Authorizing/AzManager.cs
index 973f133c11..3d95c55210 100644
--- a/common/ASC.Core.Common/Security/Authorizing/AzManager.cs
+++ b/common/ASC.Core.Common/Security/Authorizing/AzManager.cs
@@ -56,7 +56,7 @@ public class AzManager
internal async Task 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;
diff --git a/common/ASC.Core.Common/Security/Authorizing/RoleProvider.cs b/common/ASC.Core.Common/Security/Authorizing/RoleProvider.cs
index 31f6ba2fe0..ead75ebbea 100644
--- a/common/ASC.Core.Common/Security/Authorizing/RoleProvider.cs
+++ b/common/ASC.Core.Common/Security/Authorizing/RoleProvider.cs
@@ -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;
diff --git a/common/ASC.Core.Common/Security/Security.cs b/common/ASC.Core.Common/Security/Security.cs
index 342ae4d753..9ebd8ec6b1 100644
--- a/common/ASC.Core.Common/Security/Security.cs
+++ b/common/ASC.Core.Common/Security/Security.cs
@@ -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>> Rules = new()
{
{
- Constants.RoomAdmin.ID, new Dictionary>
+ AuthConstants.RoomAdmin.ID, new Dictionary>
{
{
- Constants.User.ID, new HashSet
+ AuthConstants.User.ID, new HashSet
{
- new(UserConstants.Action_EditGroups.ID, Constants.User),
+ new(UserConstants.Action_EditGroups.ID, AuthConstants.User),
new(UserConstants.Action_AddRemoveUser.ID),
}
},
{
- Constants.RoomAdmin.ID, new HashSet
+ AuthConstants.RoomAdmin.ID, new HashSet
{
- new(UserConstants.Action_EditGroups.ID, Constants.User),
+ new(UserConstants.Action_EditGroups.ID, AuthConstants.User),
new(UserConstants.Action_AddRemoveUser.ID),
}
},
{
- Constants.Collaborator.ID, new HashSet
+ AuthConstants.Collaborator.ID, new HashSet
{
- new(UserConstants.Action_EditGroups.ID, Constants.Collaborator),
+ new(UserConstants.Action_EditGroups.ID, AuthConstants.Collaborator),
new(UserConstants.Action_AddRemoveUser.ID),
}
}
diff --git a/common/ASC.Core.Common/Security/UserGroupObject.cs b/common/ASC.Core.Common/Security/UserGroupObject.cs
index 18b314194c..c259959ddc 100644
--- a/common/ASC.Core.Common/Security/UserGroupObject.cs
+++ b/common/ASC.Core.Common/Security/UserGroupObject.cs
@@ -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;
diff --git a/common/ASC.Core.Common/Security/UserSecurityProvider.cs b/common/ASC.Core.Common/Security/UserSecurityProvider.cs
index 0821ca7e31..e342e92737 100644
--- a/common/ASC.Core.Common/Security/UserSecurityProvider.cs
+++ b/common/ASC.Core.Common/Security/UserSecurityProvider.cs
@@ -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;
diff --git a/common/ASC.Core.Common/Users/Constants.cs b/common/ASC.Core.Common/Users/Constants.cs
index cd2f126a91..28436d1a78 100644
--- a/common/ASC.Core.Common/Users/Constants.cs
+++ b/common/ASC.Core.Common/Users/Constants.cs
@@ -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[]
diff --git a/common/ASC.FederatedLogin/ASC.FederatedLogin.csproj b/common/ASC.FederatedLogin/ASC.FederatedLogin.csproj
index 7ddabedbaa..b69553d5f9 100644
--- a/common/ASC.FederatedLogin/ASC.FederatedLogin.csproj
+++ b/common/ASC.FederatedLogin/ASC.FederatedLogin.csproj
@@ -31,7 +31,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/config/nlog.config b/config/nlog.config
index 59fec170c0..8d9a3fd498 100644
--- a/config/nlog.config
+++ b/config/nlog.config
@@ -44,6 +44,8 @@
-
+
+
+
\ No newline at end of file
diff --git a/products/ASC.Files/Core/HttpHandlers/FileHandler.ashx.cs b/products/ASC.Files/Core/HttpHandlers/FileHandler.ashx.cs
index 773d9d3991..c092387861 100644
--- a/products/ASC.Files/Core/HttpHandlers/FileHandler.ashx.cs
+++ b/products/ASC.Files/Core/HttpHandlers/FileHandler.ashx.cs
@@ -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);
diff --git a/products/ASC.People/Server/Api/PeopleControllerBase.cs b/products/ASC.People/Server/Api/PeopleControllerBase.cs
index 3bdcd06a7d..b335ea2da4 100644
--- a/products/ASC.People/Server/Api/PeopleControllerBase.cs
+++ b/products/ASC.People/Server/Api/PeopleControllerBase.cs
@@ -49,7 +49,8 @@ public abstract class PeopleControllerBase : ApiControllerBase
_userPhotoManager = userPhotoManager;
_httpClientFactory = httpClientFactory;
_httpContextAccessor = httpContextAccessor;
- }
+ }
+
protected async Task GetUserInfoAsync(string userNameOrId)
{
diff --git a/products/ASC.People/Server/Api/UserController.cs b/products/ASC.People/Server/Api/UserController.cs
index 3d7fcc7a66..6bbb2d5668 100644
--- a/products/ASC.People/Server/Api/UserController.cs
+++ b/products/ASC.People/Server/Api/UserController.cs
@@ -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 AddMemberAsActivatedAsync(MemberRequestDto inDto)
{
diff --git a/web/ASC.Web.Studio/Startup.cs b/web/ASC.Web.Studio/Startup.cs
index 19f1590396..ebcdc14aca 100644
--- a/web/ASC.Web.Studio/Startup.cs
+++ b/web/ASC.Web.Studio/Startup.cs
@@ -36,9 +36,7 @@ public class Startup : BaseStartup
base.Configure(app, env);
app.UseRouting();
-
- app.UseAuthentication();
-
+
app.UseEndpoints(endpoints =>
{
endpoints.InitializeHttpHandlers();