ANZ v10, AspNetCore + Angular
We are building in Azure Pipelines, using the following command
gulp build && ng build --prod
Our build has been stable for a couple of years. The last successful build was on October 17, then I tried to build today and build failed.
My suspicion is that a dependency been published, and our template uses it - and is causing the build error.
I have proven this is not due to a code change, by queuing a build for a previously successful branch - it now fails with the exact same error.
Here are my logs build logs:
...trimmed for brevity...
2022-11-02T09:08:50.7712455Z Compiling primeng : es2015 as esm2015
2022-11-02T09:11:32.6360136Z
2022-11-02T09:11:32.6362877Z ERROR in node_modules/@types/lodash/common/object.d.ts:1025:21 - error TS1110: Type expected.
2022-11-02T09:11:32.6363522Z
2022-11-02T09:11:32.6364013Z 1025 : K extends `${number}`
2022-11-02T09:11:32.6364899Z ~~~
2022-11-02T09:11:32.6365743Z node_modules/@types/lodash/common/object.d.ts:1026:19 - error TS1005: ':' expected.
2022-11-02T09:11:32.6366246Z
2022-11-02T09:11:32.6366974Z 1026 ? '0' extends keyof T
2022-11-02T09:11:32.6367513Z ~~~~~~~
2022-11-02T09:11:32.6368336Z node_modules/@types/lodash/common/object.d.ts:1026:33 - error TS1005: ';' expected.
2022-11-02T09:11:32.6368830Z
2022-11-02T09:11:32.6369674Z 1026 ? '0' extends keyof T
2022-11-02T09:11:32.6370392Z ~
2022-11-02T09:11:32.6371194Z node_modules/@types/lodash/common/object.d.ts:1028:22 - error TS1005: ';' expected.
2022-11-02T09:11:32.6371895Z
2022-11-02T09:11:32.6372364Z 1028 : number extends keyof T
2022-11-02T09:11:32.6373028Z ~~~~~~~
2022-11-02T09:11:32.6374281Z node_modules/@types/lodash/common/object.d.ts:1028:36 - error TS1005: ';' expected.
2022-11-02T09:11:32.6374800Z
2022-11-02T09:11:32.6375325Z 1028 : number extends keyof T
2022-11-02T09:11:32.6376348Z ~
2022-11-02T09:11:32.6377703Z node_modules/@types/lodash/common/object.d.ts:1031:13 - error TS1128: Declaration or statement expected.
2022-11-02T09:11:32.6379458Z
2022-11-02T09:11:32.6380388Z 1031 : undefined;
2022-11-02T09:11:32.6381201Z ~
...trimmed for brevity... (just more similar errors as above)...
2022-11-02T09:11:32.6567947Z
2022-11-02T09:11:32.6568021Z
2022-11-02T09:11:32.7143043Z npm ERR! code ELIFECYCLE
2022-11-02T09:11:32.7143478Z npm ERR! errno 1
2022-11-02T09:11:32.7185919Z npm ERR! [email protected] publish: `gulp build && ng build --prod`
2022-11-02T09:11:32.7188823Z npm ERR! Exit status 1
2022-11-02T09:11:32.7189483Z npm ERR!
2022-11-02T09:11:32.7190590Z npm ERR! Failed at the [email protected] publish script.
2022-11-02T09:11:32.7191504Z npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
2022-11-02T09:11:32.7273029Z
2022-11-02T09:11:32.7273658Z npm ERR! A complete log of this run can be found in:
2022-11-02T09:11:32.7275074Z npm ERR! /home/vsts/.npm/_logs/2022-11-02T09_11_32_719Z-debug.log
2022-11-02T09:11:32.7354536Z cp: cannot create regular file './dist/': Not a directory
2022-11-02T09:11:32.7453705Z ##[error]Bash exited with code '1'.
2022-11-02T09:11:32.7523473Z ##[section]Finishing: npm install and build
I'm on v10, AspNetCore + Angular.
I have noticed that an account is put into "password reset" state immediately upon submitting the "forgot password" form.
This does not seem like a very user friendy flow - as once in this state, the user cannot get into their account until they have satisfied the "reset password" form (i.e, set a new password).
Given that anybody can access the "forgot password" page - it allows anonymous people to mess with anyone's account, by forcing them to change their password. This could be pivoted into a denial of service, or used in combination with social engineering to maliciously access protected systems.
Here is the specific scenario.
Suggestion: Just like most websites I have ever used - if I successfully log in, even when a valid password-reset request is underway, the password reset is canceled, and the user is allowed into the account.
Note: This should obvously not allow bypassing of forced password resets, i.e, when a breach of an account password has been suspected. It's a password-reset use-case specific to the 'forgotten password' scenario.
Clarification for @KPCS, @musa.demir,
it appears this "only honor the newest password reset code" was only implement 3 months ago (after this thread was opened).
https://github.com/aspnetzero/aspnet-zero-core/issues/4487
Hi,
Sorry to awaken this old thread - however it is the most sensible place to discuss the behavior of AspNetZero today.
There is a comment above, "the next action they take should trigger a permission check which would direct them away" - this is incorrect as of version 10. I just tested this now - a user account (using Token auth) which is disabled by an admin while that user is logged in - will experience no redirects or errors. The "disabled" account will continue to function, fully authorised - until they voluntarily log out, or expire their token.
Is this the intended behavior of ANZ today? If so, I feel it should be recorded as a bug, as an admin who "disables" a user account, intends to do so to prevent that user from accessing the system. If they need to call them up and ask "could you please log out so you cannot get back in?" then the system is flawed. Note: Requesting the browser to discard the token is likely an insecure solution - as a clever user could reinstate the token on their site
So, 2 questions:
Thanks,
Hi,
Thanks for the offer - interestingly enough, as I have written more infrastructure to support multiple databases (see my other post about how to integration test 2 different database schemas in the same in-memory db file, with some shared entities) - the issue has now gone away. (yay).
Not wanting to derail the subject too much - I have noticed that UnitOfWorkManager.Current appears to be null within all unit tests. Is this not set up by the Abp test base classes somewhere by default? Or are we supposed to do this ourselves?
This would imply that none of the existing tests call methods which use the UnitOfWorkManager - which is surprising - perhaps I'm wrong... but it's definitely null for me - which is breaking some tests.
Hi @ismcagdas - this isn't a question - I have posed a problem, and the solution in the one post - in case anybody else runs into this requirement. I probably should have clicked "close" when I posted it. Thanks - closing now.
Hi,
It would take a while to see if I could get clearance for this. However, I know I could set up a Teams video session with you, and show you the issue / let you drive a debug session?
Thanks
This is a post to store knowledge for future reference.
We have an ANZ application, and have segregated our application entities into its own DbContext - separate from ANZ. We do "share" some entities, however - for example - the AbpUsers table is included in our own DbContext - but we disable migrations for that table so it does not attempt to generate the table a second time.
We have started writing unit/integration tests for our business logic - and came across this problem - how do we mock the database, when we need BOTH the Abp tables, AND our own table schemas to be created into the mocked sqlite dbcontext? If we do nothing, we get an error when running the test, that tables could not be found.
The answer required 2 adjustments to achieve.
public static class ServiceCollectionRegistrar
{
public static void Register(IIocManager iocManager)
{
RegisterIdentity(iocManager);
var builder = new DbContextOptionsBuilder<AnzDbContext>();
var inMemorySqlite = new SqliteConnection("Data Source=:memory:");
builder.UseSqlite(inMemorySqlite);
// register connection for reuse
iocManager.IocContainer.Register(Component.For<SqliteConnection>().Instance(inMemorySqlite));
iocManager.IocContainer.Register(
Component
.For<DbContextOptions<AnzDbContext>>()
.Instance(builder.Options)
.LifestyleSingleton()
);
inMemorySqlite.Open();
new AnzDbContext(builder.Options).Database.EnsureCreated();
}
private static void RegisterIdentity(IIocManager iocManager)
{
var services = new ServiceCollection();
IdentityRegistrar.Register(services);
WindsorRegistrationHelper.CreateServiceProvider(iocManager.IocContainer, services);
}
}
EnsureCreated
( new CustomDbContext(builder.Options).Database.EnsureCreated();
) - but reading the XmlDoc, you will see that if the DB already exists, it will do nothing - not even generate the schema - so we need to use reflection to access the Create()
and CreateTables()
methods.public static void Register(IIocManager iocManager)
{
RegisterIdentity(iocManager);
var builder = new DbContextOptionsBuilder<CustomDbContext>();
var inMemorySqlite = iocManager.Resolve<SqliteConnection>();
builder.UseSqlite(inMemorySqlite);
iocManager.IocContainer.Register(
Component
.For<DbContextOptions<CustomDbContext>>()
.Instance(builder.Options)
.LifestyleSingleton()
);
inMemorySqlite.Open();
var db = new CustomDbContext(builder.Options);
// FORCE the table schema to be created - even though the database already exists, it only contains ANZ tables - none of our own
var propDependencies = typeof(DatabaseFacade).GetProperty("Dependencies", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
var dependencies = (IDatabaseFacadeDependencies)propDependencies.GetValue(db.Database);
if (dependencies.DatabaseCreator is IRelationalDatabaseCreator databaseCreator)
{
databaseCreator.Create();
databaseCreator.CreateTables();
}
}
Hi,
AspNetZero Angular+Core: API: v10.0.0 | Client: v10.0.0 (20220815)
I have scanned the forum, and found older discussions relating to this error, such as: https://support.aspnetzero.com/QA/Questions/4162/transaction-is-not-associated-with-the-current-connection
The solutions proposed do not work for me.
My scenario is that I have the ANZ dbcontext, and I also have a second DbContext. It works at regular runtime, the above error only happens during unit testing.
I have this in both my DbContexts:
Configuration.ReplaceService<IEfCoreTransactionStrategy, DbContextEfCoreTransactionStrategy>(DependencyLifeStyle.Transient);
And the error occurs when testing an AppService which attempts to resolve my custom DbContext
IDbContextProvider<CustomDbContext> _customDbContextProvider
public CustomDbContext DbContext => _customDbContextProvider.GetDbContext(); // error happens here
Yes, turning off transactions fixes the issue - however, my code needs to be transactional.
Thanks,
Hi,
For anyone who is interested, we have been operating with Managed Identity for a while - where the AbpDbContext obtains the Token within its constructor - however, recently we have started adding unit tests - and because the database is mocked with sqlite - this becomes a bit messy because it needs to know whether its running within unit tests or not.
This is what we came up with, which is far cleaner anyway:
public class AzureSqlConnectionTokenInjector : DbConnectionInterceptor
{
private AzureServiceTokenProvider _tokenProvider;
public AzureSqlConnectionTokenInjector()
{
_tokenProvider = new AzureServiceTokenProvider();
}
protected virtual async Task EnsureAccessToken(DbConnection connection)
{
if (connection is SqlConnection sqlConnection
&& connection.ConnectionString.ToUpper().Contains("DATABASE.WINDOWS.NET")
&& string.IsNullOrWhiteSpace(sqlConnection.AccessToken))
sqlConnection.AccessToken = await _tokenProvider.GetAccessTokenAsync("https://database.windows.net/");
}
public override InterceptionResult ConnectionOpening(DbConnection connection, ConnectionEventData eventData, InterceptionResult result)
{
EnsureAccessToken(connection).Wait();
return base.ConnectionOpening(connection, eventData, result);
}
public override async ValueTask<InterceptionResult> ConnectionOpeningAsync(DbConnection connection, ConnectionEventData eventData, InterceptionResult result, CancellationToken cancellationToken = default)
{
await EnsureAccessToken(connection);
return await base.ConnectionOpeningAsync(connection, eventData, result, cancellationToken);
}
}
Then use this in your 'DbContextConfigurer' class:
public static class MyDbContextConfigurer
{
public static void Configure(DbContextOptionsBuilder<MyDbContext> builder, string connectionString)
{
builder.UseSqlServer(connectionString)
.AddInterceptors(new AzureSqlConnectionTokenInjector());
}
public static void Configure(DbContextOptionsBuilder<MyDbContext> builder, DbConnection connection)
{
builder.UseSqlServer(connection);
}
}