C#Bot Release 2.4.0.0

Features

Redis Integration

In this version of C#Bot we have integrated Redis for high speed caching of data. If a Redis node is detected then it will be used to store Hangfire Jobs, cache data between requests, store currently active cookies and IP rate-limiting settings. Redis configuration settings can be configured in serverside/src/appsettings.xml. If there is no Redis database detected, then the application will fall back to caching data in memory and will maintain full functionality.

Security Improvements

As a part of our ongoing commitment to security, we have implemented a series of changes to improve the security of cookie based logins.

  • Added secure attributes to cookies when running on HTTPS environments.
  • Made session cookies have a strict same site policy.
  • Made session cookies host cookies when running on HTTPS environments.
  • When a user logs out of a session, their cookie for that session will be invalidated.

Rate Limiting

In addition to work done on cookie security, we have also implemented a rate-limiting framework into the server. This is implemented using the AspNetCoreRateLimit library. The configuration of the rate limiting can be configured in serverside/src/appsettings.xml.

Improvements

  • An appsetting has been added to flag whether the server is running in a HTTPS environment.
    • This will alter the cookie security settings if it is enabled.
  • Sign in methods have been moved from the IUserService to a new ISignInService class.
    • This removes all dependencies on the HttpContext from the class.
  • When generating a ClaimsPrincipal for a user, it is now sourced from the IUserClaimsPrincipalFactory service.
    • All existing claims that were generated previously will still be generated.
  • Hangfire will now use Redis if there is an existing connection. If it cannot connect to Redis, it will fall back to the main database for persistence.
  • The DbContext no longer depends on the HttpContext for auditing purposes.
    • The necessary data is now injected via a middleware service.
  • The GraphQL schema will now have more correct types with regard to nullable values.
  • The IdentityService will now retrieve the users’ roles from the users’ claims and not from the database.
  • Protected regions added to resolve:

Resolved defects

  • The security stamp for a user is now correctly verified against their cookie on an interval.
  • The XSRF token will now have the same lifetime as the session cookie when the defaults have changed.
  • The XSRF token is now correctly injected in all responses.
  • Correctly applied client-side security to pages.
  • Fixed the date time questions used in the Forms extension not correctly handling null values.
  • Fixed empty form validation errors rendering when they should not.
  • Fixed timeline extension Selenium tests failing if they were the first tests run.
  • Fixed gradual build up of file based audit logs in the server-side test folder.

Migration Path

  • The Exchange and CheckCredentials methods on the IUserService have been made obsolete and will be removed in a future release. Please use the corresponding methods on the new ISignInManager. The functionality of the methods on this new class is the same.
  • The UserPrincipal on the ServerBuilderOptions class has been removed and replaced with a UserPrincipalFactory method that provides an IServiceProvider and expects a ClaimsPrincipal to be returned.
  • The static CreateUserPrincipal method on ServerBuilder has been removed. For creating a custom user principal for testing, it is recommended to use the IUserService to generate one instead. This user must be created in the database for this method to succeed. For example:
var host = ServerBuilder.CreateServer(new ServerBuilderOptions
{
	UserPrincipalFactory = async sp =>
	{
		var userManager = sp.GetRequiredService<UserManager<User>>();
		var userService = sp.GetRequiredService<IUserService>();
		return await userService.CreateUserPrincipal(await userManager.FindByNameAsync("testUser@example.com"));
	};
});

var scope = host.Services.CreateScope();
var serviceProvider = scope.ServiceProvider;

var user = new User
{
	Email = "testUser@example.com",
	Discriminator = "User"
};

await serviceProvider.GetRequiredService<IUserService>().RegisterUser(
	user,
	Guid.NewGuid().ToString(),
	new [] { groupName });
2 Likes