Hi Thanks for the new release. Looks good! One vulnerability remains in the backend code: System.Text.RegularExpressions 4.3.0 High https://github.com/advisories/GHSA-cmhx-cq75-c4mj It is a transitive package in all projects. The fix is to explicit take a dependency on System.Text.RegularExpressions 4.3.1
I'll create an issue on git hub.
Sure. I've done that with issue #5322. Looking forward to priority and comments.
Thanks for a quick response! Looking forward to the new release. Some of these vulnerabilities are due to transitive dependencies and may need a bit work apart from upgrading the direct dependencies. I'll try the new release next week.
Br. Tom
Hi
I have notice a number of build warning generation with the latest Asp.Net Zero release, see below. In our team we normally treat warnings as errors and we would appreciate a solution without build warnings (and no known code issues/code smells).
Please assist us by fixing these issues for the next release.
Br. Tom
I observed a number of NuGet packages with known, medium and high, vulnerabilities in the latest release 13.2. Do Asp.Net Zero do any vulnerability scanning before a release?
Specifically I detected these vulnerabilities:
Please confirm that you are aware of these NuGet package with vulnerabilities?
Please, let know if or when these packages are updated in the Asp.Net Zero .Net 8 solution?
Br. Tom Andersen
Hello We fixed this problem in a slightly different way using the AppUrlService.appRootUrl service property. But we are still wondering if there are ways to fix this problem without modifying the code? Or should we expect a fix for this problem in future versions of ASP.NET Zero?
We've run into an issue with handling an issue on the angular side when our angular application has a relative path like /application/, for example, http://localhost/application/
The default ASP.NET Zero handler redirects the user according to the following code (zero-template-http-configuration.service.ts):
import { Injectable } from '@angular/core'; import { AbpHttpConfigurationService, LogService, MessageService } from 'abp-ng2-module'; import { Router } from '@angular/router';
@Injectable({ providedIn: 'root', }) export class ZeroTemplateHttpConfigurationService extends AbpHttpConfigurationService { constructor(messageService: MessageService, logService: LogService, private _route: Router) { super(messageService, logService); }
// Override handleUnAuthorizedRequest so it doesn't refresh the page during failed login attempts.
handleUnAuthorizedRequest(messagePromise: any, targetUrl?: string) {
if (this._route.url && (
this._route.url.startsWith('/account/login') || this._route.url.startsWith('/account/session-locked')
)) {
return;
}
const self = this;
if (messagePromise) {
messagePromise.done(() => {
this.handleTargetUrl(targetUrl || '/');
});
} else {
self.handleTargetUrl(targetUrl || '/');
}
}
}
Actual result
The redirect occurs to the domain name and does not take into account the relative path.
Expected result After the redirect, the user is redirected to the correct root URL
So, could you please provide us: 1. How to pass targetUrl argument to this method? In our case it is NULL
2. Or how can we solve the problem with an application whose relative path is different from the domain name = '/'?
We encountered an error when the user's session has expired and the user is trying to log out of the site. It leads to an error message with the inability to log out.
Pre-conditions: User session is expired (AccessTokenExpirationMinutes parameter is used to set session timeout)
Debug steps:
User clicks Logout button
From angular project, the GET request is sent to back-end Logout endpoint - /api/TokenAuth/LogOut Original ASP NET Zero code: logout(reload?: boolean, returnUrl?: string): void { let customHeaders = { [abp.multiTenancy.tenantIdCookieName]: abp.multiTenancy.getTenantIdCookie(), Authorization: 'Bearer ' + abp.auth.getToken(), };
XmlHttpRequestHelper.ajax(
'GET',
AppConsts.remoteServiceBaseUrl + '/api/TokenAuth/LogOut',
customHeaders,
null,
() => {
abp.auth.clearToken();
abp.auth.clearRefreshToken();
new LocalStorageService().removeItem(AppConsts.authorization.encrptedAuthTokenName, () => {
if (reload !== false) {
if (returnUrl) {
location.href = returnUrl;
} else {
location.href = '';
}
}
});
}
);
}
This endpoint looks like: Original ASP NET Zero code:
[HttpGet]
[AbpMvcAuthorize]
public async Task LogOut()
{
if (AbpSession.UserId != null)
{
var tokenValidityKeyInClaims = User.Claims.First(c => c.Type == AppConsts.TokenValidityKey);
await RemoveTokenAsync(tokenValidityKeyInClaims.Value);
var refreshTokenValidityKeyInClaims =
User.Claims.FirstOrDefault(c => c.Type == AppConsts.RefreshTokenValidityKey);
if (refreshTokenValidityKeyInClaims != null)
{
await RemoveTokenAsync(refreshTokenValidityKeyInClaims.Value);
}
if (AllowOneConcurrentLoginPerUser())
{
await _securityStampHandler.RemoveSecurityStampCacheItem(
AbpSession.TenantId,
AbpSession.GetUserId()
);
}
}
}
The main problem is with the AbpMvcAuthorize attribute
[AbpMvcAuthorize]
Since user is already not authenticated, then it leads to 404 response from server(suggest, one more error in ASP NET Zero, it should be 401).
On front-end part this response is handled as:
export class XmlHttpRequestHelper { static ajax(type: string, url: string, customHeaders: any, data: any, success: any) { let xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
let result = JSON.parse(xhr.responseText);
success(result);
} else if (xhr.status !== 0) {
alert(abp.localization.localize('InternalServerError', 'AbpWeb'));
}
}
};
url += (url.indexOf('?') >= 0 ? '&' : '?') + 'd=' + new Date().getTime();
xhr.open(type, url, true);
for (let property in customHeaders) {
if (customHeaders.hasOwnProperty(property)) {
xhr.setRequestHeader(property, customHeaders[property]);
}
}
xhr.setRequestHeader('Content-type', 'application/json');
if (data) {
xhr.send(data);
} else {
xhr.send();
}
}
}
Alert message is shown:
alert(abp.localization.localize('InternalServerError', 'AbpWeb'));
Expected result Alert Internal Server Error message
Actual result No errors, user is logged out, login page is displayed
Do you agree that the expected result is not as desired? Will you fix this in a later release? (we are using 11.4)
Thanks Asp.Net Zero Support
We have now tried adding that attribute, see below. No change experienced, still same issue.
[DisableAuditing]
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = new CancellationToken())
{
…
Any other suggestions or ideas?
Sometimes, during healthchecking, we get error about connection to database. It happens 2-3 times in 2500 healthcheck requests. Could you help us to find possible reason of this error?
Log:
TASWebclient;2022-10-30 23:34:50,139;ERROR;Microsoft.Extensions.Diagnostics.HealthChecks.DefaultHealthCheckService;[.NET ThreadPool Worker];(null);(null);Health check Database Connection with user check with status Unhealthy completed after 4.4974ms with message 'WebClientDbContext could not connect to database.';System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.AspNetCore.Http.DefaultHttpRequest.get_Scheme()
at Microsoft.AspNetCore.Http.Extensions.UriHelper.GetDisplayUrl(HttpRequest request)
at Abp.EntityHistory.EntityHistoryHelper.CreateEntityChangeSet(ICollection1 entityEntries) at Abp.Zero.EntityFrameworkCore.AbpZeroCommonDbContext
3.SaveChangesAsync(CancellationToken cancellationToken)
at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.SaveChangesAsync()
at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.CompleteUowAsync()
at Abp.Domain.Uow.UnitOfWorkBase.CompleteAsync()
at TAS.WebClient.HealthChecks.WebClientDbContextUsersHealthCheck.CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken) in D:\BuildAgents\DevOpsAgent39_Work\4\s\TASWebclient\aspnet-core\src\TAS.WebClient.Application\HealthChecks\WebClientDbContextUsersHealthCheck.cs:line 58
;
WebClientDbContextUsersHealthCheck.cs :
using System;
using System.Threading;
using System.Threading.Tasks;
using Abp.Domain.Uow;
using Abp.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using TAS.WebClient.EntityFrameworkCore;
namespace TAS.WebClient.HealthChecks
{
public class WebClientDbContextUsersHealthCheck : IHealthCheck
{
private readonly IDbContextProvider<WebClientDbContext> _dbContextProvider;
private readonly IUnitOfWorkManager _unitOfWorkManager;
public WebClientDbContextUsersHealthCheck(
IDbContextProvider<WebClientDbContext> dbContextProvider,
IUnitOfWorkManager unitOfWorkManager
)
{
_dbContextProvider = dbContextProvider;
_unitOfWorkManager = unitOfWorkManager;
}
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = new CancellationToken())
{
try
{
using (var uow = _unitOfWorkManager.Begin())
{
// Switching to host is necessary for single tenant mode.
using (_unitOfWorkManager.Current.SetTenantId(null))
{
var dbContext = await _dbContextProvider.GetDbContextAsync();
if (!await dbContext.Database.CanConnectAsync(cancellationToken))
{
return HealthCheckResult.Unhealthy(
"WebClientDbContext could not connect to database"
);
}
var user = await dbContext.Users.AnyAsync(cancellationToken);
await uow.CompleteAsync();
if (user)
{
return HealthCheckResult.Healthy("WebClientDbContext connected to database and checked whether user added");
}
return HealthCheckResult.Unhealthy("WebClientDbContext connected to database but there is no user.");
}
}
}
catch (Exception e)
{
return HealthCheckResult.Unhealthy("WebClientDbContext could not connect to database.", e);
}
}
}
}
AbpZeroHealthCheck.cs:
using Microsoft.Extensions.DependencyInjection;
using TAS.WebClient.HealthChecks;
namespace TAS.WebClient.Web.HealthCheck
{
public static class AbpZeroHealthCheck
{
public static IHealthChecksBuilder AddAbpZeroHealthCheck(this IServiceCollection services)
{
var builder = services.AddHealthChecks();
builder.AddCheck<WebClientDbContextHealthCheck>("Database Connection");
builder.AddCheck<WebClientDbContextUsersHealthCheck>("Database Connection with user check");
builder.AddCheck<CacheHealthCheck>("Cache");
builder.AddCheck<ExternalApiHealthCheck>("ExternalApi Connection");
builder.AddCheck<DocumentApiHealthCheck>("DocumentApi Connection");
// add your custom health checks here
// builder.AddCheck<MyCustomHealthCheck>("my health check");
return builder;
}
}
}
Startup.cs:
if (bool.Parse(_appConfiguration["HealthChecks:HealthChecksEnabled"]))
{
services.AddAbpZeroHealthCheck();
var healthCheckUISection = _appConfiguration.GetSection("HealthChecks")?.GetSection("HealthChecksUI");
if (bool.Parse(healthCheckUISection["HealthChecksUIEnabled"]))
{
services.Configure<HealthChecksUISettings>(settings =>
{
healthCheckUISection.Bind(settings, c => c.BindNonPublicProperties = true);
});
services.AddHealthChecksUI()
.AddInMemoryStorage();
}
}
...
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<AbpCommonHub>("/signalr");
endpoints.MapHub<ChatHub>("/signalr-chat");
endpoints.MapControllerRoute("defaultWithArea", "{area}/{controller=Home}/{action=Index}/{id?}");
endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
if (bool.Parse(_appConfiguration["HealthChecks:HealthChecksEnabled"]))
{
endpoints.MapHealthChecks("/health", new HealthCheckOptions()
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
}
app.ApplicationServices.GetRequiredService<IAbpAspNetCoreConfiguration>().EndpointConfiguration.ConfigureAllEndpoints(endpoints);
});
if (bool.Parse(_appConfiguration["HealthChecks:HealthChecksEnabled"]))
{
if (bool.Parse(_appConfiguration["HealthChecks:HealthChecksUI:HealthChecksUIEnabled"]))
{
app.UseHealthChecksUI();
}
}
appsettings.json:
"HealthChecks": {
"HealthChecksEnabled": true,
"HealthChecksUI": {
"HealthChecksUIEnabled": true,
"HealthChecks": [
{
"Name": "TAS.WebClient.Web.Host",
"Uri": "https://localhost:44301/health"
}
],
"EvaluationTimeOnSeconds": 10,
"MinimumSecondsBetweenFailureNotifications": 60
}
},