Update history - Archive

Version 1.6.5.0 - Sept 22, 2015

Critical bug fix - SecureSession.CanDelete was failing with exception

Version 1.6.4.0 - Sept 21, 2015

Bug fixes and internal refactoring
  1. Fixed index creation order for materialized DB Views - unlike indexes for tables, order matters - clustered index should be created first/dropped last
  2. Added refreshing materialized views for Postgres DB views test.
  3. Got rid of stored procedures SelectByIndex-TopOne, used in detecting if entity row can be deleted; now uses dynamic LINQ queries.
  4. Some performance improvements in LINQ (evaluation of parameters simplified and streamlined)

Version 1.6.3.0 - Sept 16, 2015

  1. Multiple bug fixes and minor enhancements
  2. Scheduling LINQ non-query commands - you can now execute an update based on LINQ query not immediately, but with session.SaveChanges() call. You can additionally specify whether to execute it at the beginning or the end of update transaction. The method is (extenstion): session.ScheduleNonQuery(query, commandType, schedule); see unit test TestLinqNonQueryScheduledCommands

Version 1.6.0.0 - Aug 12, 2015

  1. DB VIEWS are in! Support for DB views is implemented, including materialized views with indexes. You define the view using an output entity and a query, register it in module constructor, and VITA will create a view in the database. In your code you can run LINQ query against the view, the same way you'd do against regular table-like entity. See BooksModule.cs in book store sample app for an example. For servers that do not support materialized view, VITA creates 'normal' views (MySql). Handling views is painful - in schema updates, detecting that view is changing or not; MySql and Postgres are real trouble; they do not save original view definition, but save compiled version and reconstruct the definition when you try to read it in information_schema queries - and it is completely different from original. I've solved it with workaround for Postgres (saving original definition in comment attribute), but for MySql gave up. So for now Views in MySql will be re-created on each restart with schema update.
  2. Smtp Email redirect for testing - Smtp/email service has an extra option to automatically redirect all outgoing emails to specified test account, very handy in testing environment.

Version 1.5.8.0 - July 21, 2015

  1. LoggingApp - an out-of-the-box, ready-to-use entity app class in Vita.Modules assembly. It groups all logging modules in the assembly. You can "link it" to the main app and it will perform all logging for the main app, and log the info into the same or different "log" database. The main enhancement added in the core functionality is this 'linking' ability, when one application (log app) is providing logging services for another, main app. In the real application based on VITA that we are currently building we decided to split away logs into a separate database, because log data has completely different dynamics than regular app data. Log data is mostly write-only, growing very quickly, and needs to be maintained (archived and cleared) in a different way than main data. BookStore sample app now uses this LoggingApp. For MS SQL there is a separate connection string in config files, so you need to create VitaBooksLogs database before running the tests. For other servers the tests use the same connection string, so although there is a separate logging app, the data ends up in the main database. If you want, you can add log databases and connection strings for other servers.
  2. Refactoring of direct DB access - not much changes for client code; now the direct db object is called IDirectDbConnector, and is returned by session.GetDirectDbConnector() extension method. The connector provides direct control over db connection. If you use LINQ non-query operations (updates with LINQ), you can control connection/transaction using this connector: direct connection is one per entity session, so when you open direct connection, LINQ non-query operations are using the same connection. This even includes session.SaveChanges() call. So you can enclose multiple update operations into one transaction controlled through direct connection.
  3. Authorization failure in Web operations now returns Forbidden HTTP status (403) - previously it was Unauthorized which was a wrong choice obviously. Unauthorized is essentially for 'need to login' situations.
  4. Proper handling of bad URL error - when server fails to match HTTP method and URL to a controller, now server will return BadRequest status. Previously it was simply failing with exception that resulted in InternalServerError.
  5. AuthorizationEnabled flag in ApiConfig - allows to disable authorization checks. This is very handy in debugging in teams. Setting up authorization roles and permissions is a tricky process, and it takes many iterations. QA might encounter erroneous AccessDenied response while testing some functionality meaning that authorization setup is missing some explicit permissions for a given user. In order to avoid blocking QA people and let them continue testing core functionality, you can provide a flag in config file that would allow them to disable authorization checks until authorization setup is fixed and pushed. So QAs just change the flag and restart the application. At application startup your code should read the value from config and set AuthorizationEnabled flag in ApiConfiguration. Note that you also need to switch your code that opens sessions - you should open regular non-secure session if authorization is disabled.
  6. Minor bug fixes in LINQ - properly handle parameters of 'entity' type, when value of parameter is null - previously was throwing exception. Add unit test for this too.

Version 1.5.7.0 - July 13, 2015

Update/Insert/Delete statements using LINQ expressions. First draft, not everything is polished, but core functionality is working for all 3 non-query command types and all servers. One ugly thing - updating entity references, you have to specify FK column explicitly in the output anonymous object (instead of assigning entity references. Ex: 'Book_Id = bk.Id', instead of simply ' Book = bk '.
See unit tests for each command type in Extended unit tests project.
Actual operations are available through extension methods for query object ExecuteXXX:
var updateCount = query.ExecuteUpdate<IBook>();

Version 1.5.6.0 - July 1, 2015

Authorization bug fix; deep authorization refactoring (internal) - should have no effect on client code.

Version 1.5.5.0 - June 29, 2015

Some hot fixes. Logging module API (data view controller), Logging module authorization setup (pre-defined Role for admin access to logs)

Version 1.5.4.0 - June 25, 2015

Minor update, but with some handy improvements:
  1. Entity/Query filters - made a full split between the two kinds of filters. EntityFilters are predicate expressions executed over loaded entities (in c# code), while QueryFilters are additional WHERE clauses automatically added to LINQ queries. DataFilter (used in Authorization, now renamed to AuthorizationFilter) now internally contains both kinds of filters, completely separated. You still can designate a lambda expression to be used in both (Queries and Entities), or you can add different lambdas for each type.
  2. Automatic handling of null values in filter predicates - this is a handy improvement. When filter predicate (lambda) contains an operation over nullable property, it goes OK in LINQ queries - it is translated into a join which handles nullable references without problems. But for in-memory entity filter executing expression like (author => author.User.Id) when author.User is nullable might result in null-ref exception. Now all such expressions are automatically rewritten to include conditional expression. So the result of author.User.Id is empty Guid if author.User is null. This saves you from need to add extra safe functions - like GetAuthorUserId() helper method in BookAuthorizatioHelper, now gone. The same happens when LINQ expressions against fully-cached entities are rewritten to Linq-to-objects lambdas - safe checks are injected, so LINQ query runs OK against cached lists.
  3. DataFilter is obsolete - use AuthorizationFilter class instead.
  4. OperationContext.QueryFilter - you can add extra WHERE clauses here for any entity type, and they will be applied automatically in LINQ queries. One suggested use is in Web service apps, at start of request processing you can set extra filters in OperationContext, based on current user or some other conditions.

Version 1.5.3.0 - June 21, 2015

1. Api Controllers Authorization - Access to Api controllers (ability to make a REST-ful call) can now be restricted using authorization setup. (Note: this functionality is available only for 'SlimApi' controllers, not derived from Web API's ApiController class). If a controller is marked with [Secured] attribute, then access to it must be explicitly granted in the user's authorization roles. As an example, look at LoginAdministrationController class in Vita.Modules.Login module. The controller handles login administration, and this functionality must be available only power users. Login module provides a ready-to-use LoginAdministrator user role (available through static singleton LoginModule.AuthorizationRoles, full path to role: LoginModule.AuthorizationRoles.LoginAdministrator). You should add this role to your site's admin user setup (see BooksAuthorizationHelper.cs for an example). If user is not granted this role, any attempt to hit URL like 'GET: api/logins' will be rejected with Unuathorized code. The call will never reach the controller and will be cut-off at the routing stage.
Implementation - there is a new type of permission ObjectAccessPermission which encapsulates the permission to access an API controller. The access type enumeration has several new values with Api- prefix (ApiGet, ApiPost, ApiAll etc). The access is granted to controller type and specific HTTP method(s) like GET or POST, or to all methods using AccessType.ApiAll set of flags.
With this facility you can now authorize access at Api level; you can still enable all authorization rules at entity level. A good practice would be to define standard roles with an entity module, which should include authorization to access controllers - which should also be part of module definition. Login module is an example - it defines entities, services, API controllers and authorization rules setup.

2. Authorization Data Filters refactoring and improvements. Previously, the data filters used in authorization setup relied on so-called ContextValueReader objects that provided values from the OperationContext. The lambda expressions in data filters were supposed to compare the ID value derived from entity with this ID value taken from context. I believe I found a better, more flexible and intuitive way to define data filter expressions. Values are now injected into expressions automatically based on parameter name in lambda expression:
      // User can access documents only from his/her department
      dataFilter.Add<ISomeDoc, Guid>((doc, departmentId) => 
           doc.Department.Id == departmentId);
The 'doc' parameter of type ISomeDoc is the document under access check (as in previous VITA version). The departmentId parameter (Guid) is coming from the current OperationContext - it will be retrieved by the authorization engine and 'injected' into the call. The operation context's Values dictionary should contain the current user's department ID. You can use from 0 to 4 extra parameters. Parameter with name 'userId' is special - you do not have to put it into operation context explicitly, it is taken from the context.User.UserId field (which always contains current user ID). See BooksAuthorizationHelper class for an example of filters setup.

3. Automatic Query Filters from authorization lambdas - long desired feature. Previously, all of the authorization framework facilities were working over entities - data delivered from the database. The system executed lambda expressions from data filters when evaluating if the user can access the data. There was one problem with querying data: if the user is restricted to see only some records in the table, still all of them, regardless of authorization rules were delivered from the database, restricted only by query conditions, and then these records (as entities) were filtered out using lambdas in data filters. In many cases the selected data automatically conforms to authorization rules (manager selects customer orders, and because manager has access to customer info and all the data, the delivered records pass through authorization filter without problems). However, for search-like queries, when there is no clear business rule-binding criteria, the initially selected records would include those that will be filtered out by the authorization checks. This results in too-much data selected, and also disrupts the paging mechanism - you ask for 10 rows but after authorization filter you get less, or don't get any at all. To deal with this, the developer had to programmatically add an extra restricting criteria to filter out non-passing rows at database level. I mentioned this problem in the final comments in Authorization Guide document.
Now we have a solution. You can tell the authorization framework to automatically apply the authorization lambda expression to any LINQ query against the entity. Continuing with the previous example:
      // User can access documents only from his/her department
      dataFilter.Add<ISomeDoc, Guid>((doc, departmentId) => 
	   doc.Department.Id == departmentId, FilterUse.Sql | FilterUse.Entities);
The last parameter (FilterUse) tells the system to use the criteria both for entities filtering in CLR, and for querying the data. Now any LINQ query made by the user against ISomeDoc table will include a restriction on documentId criteria. The FilterUse parameter is optional, with default value of FilterUse.Entities. Why not both? First, not every lambda expression executable against entities can be translated into SQL predicate - for example, if you use some c# function in the expression. Secondly, not every table/entity actually needs this extra filter in queries - only those that are involved in free-form searches. So you can use this extra parameter to enable query filtering for some entities. Note that there is a shortcut for this flag combination: FilterUse.All.
As simple as it might seem to enable this feature, the implementation is quite challenging. The problem is that authorization roles are hierarchical, and for a given user/entity combination there might be several restrictive clauses coming from different roles/activity grants. See CustomerSupport user role in BookStore sample. The customer support person is allowed to access user information only for users that are customers OR authors, but not employees. Query restrictions for customers and authors come from two different filters. What authorization framework does is combining two filters using OR.
Look at AuthorizationTests method - it now has a section for CustomerSupport user:
  var secureSession = OpenSecureSession(jessyCustSupport);
  // CustomerSupport can view Users, but only those that are Customers or Authors
  var allSupportedUsers = secureSession.EntitySet<IUser>().ToList();
  /* SQL: 
    SELECT "Id", "UserName", "UserNameHash", "DisplayName", "Type", "IsActive"
      FROM "books"."User"
      WHERE ("Type" = 1 OR "Type" = 2)          
  */
Notice the LINQ query - we are selecting all users, without any explicit criteria. However, the system automatically injects WHERE clause with a check for user type - which is an OR combination of expressions from two filters.

4. Entity registration is now with EntityModule - not with EntityArea. The old EntityArea.RegisterEntities method is still there, but it is marked with [Obsolete] attribute for backward compatibility. You should change the calls in your code to base.RegisterEntities(). This is actually going back to earlier design, I changed registration to be with areas because of some design needs, but now these reasons are gone, so going back to more natural arrangement.
5. Fixing Index attribute. Several fixes made to Index attribute. IndexName property now works as a name of the index in the database. For automatic names (when IndexName is not specified), the system normally concatenates column names in the index name - but now it is aware of length limitation and cuts of too long names. New property Index.Alias is a friendly name to refer to index in the exception thrown in unique index violation (ex: exc.Data{"[DataAccessException.KeyIndexAlias]}") - it used to be IndexName property and this was confusing.
6. Using 'fake' ORDER BY clause in paging queries. When you use SQL paging facilities some servers (MS SQL, SQL CE, Postgres) require that you provide ORDER BY clause. Previously, if there was no default order by specified for an entity, VITA was adding ordering by primary key. This is not very desirable situation, especially considering that PKs are GUIDs in most cases, which are very random. It turns out there is a nice workaround: you can specify a 'fake' ordering: 'ORDER BY (SELECT 1)'. This works for MS SQL and Postgres, and my guess is that it will make queries easier to execute for servers (this is my 'educated' guess, did not find any detailed information about this). SQL CE does not accept this fake clause, so it is still by PK. MySql and SQLite do not require ORDER BY for paging. Note: forgot to do it in XX_SelectAllPaged procedures, will fix in the next update.
7. LINQ bit/bool handling fix - quite painful fix, for some fancy bool expressions like (ent.PropA & param = ent.PropB). The original problem is that for some servers (MS SQL, SQLite) bool value is represented by BIT type in the database, which is like bool, but is actually int, so you cannot write something like 'WHERE user.IsActive'. In addition, it turns out MS SQL cannot handle equality of bool values (bit data type is OK, but not BOOL: 'WHERE (1=1) = (0=0)' - invalid in MS SQL). As a result the LINQ translator has to account for these oddities for certain server types.
8. Improved naming of CRUD stored procedures. By default names are like 'BookInsert' - table name followed by the operation. Now there is an underscore symbol in between: Book_Insert. The result is that when viewing procedures in list/tree sorted by name they are not intermixed and are clearly grouped by table.

Version 1.5.1.0 - June 2, 2015

Hot fix - fixed bug in LoginFailedTrigger, added back unit test for it.
Minor fix in LINQ translator

Version 1.5.0.0 - June 1, 2015

This update was initially planned to be focused on ORM-related things like Db views and other things, but reality interfered, the focus shifted, and I had to take care of things more important for the application we develop at my daytime job. The result is fortunately very beneficial.
The major advancement in this release is a Slim API technology - an ability to create RESTful API controllers without dependencies on ASP.NET Web API packages. This allows you to create API controllers right in the core business logic entity modules/assemblies, so the resulting functionality is Web/UI ready, ready for direct use in Web app projects. As one of the consequences, the Vita.Modules assembly that contains standard entity modules now hosts a few API controllers making it UI-ready. One comprehensive example is a set of API controllers for full implementation of Login functionality.
But let's start with a list of minor improvements, before we talk about SlimApi and Login in more details.
  1. Warning - if you are using Login module in an app, and do not want to wipe out the database, drop the table 'ILoginWorkflow' in the database before you launch the updated app. This table (if it has old data) might create problems with schema update. The data in this table is temporary anyway.
  2. Debugger.Break() statements removed - minor cleanup.
  3. DbCapabilities renamed to DbFeatures* - minor update in DB Drivers area; the new name better reflects the nature of the enumeration.
  4. ThrowValidation and similar extension methods now clear buffered ClientFaults - they are all copied into the client fault exception being thrown.
  5. LoginLog is moved to a separate entity module, to avoid all these manipulations with entity areas (schemas) to place it into 'log' schema. You should now add this module explicitly in you entity app constructor if you use Login module.
  6. LINQ - comparing nullable values - fix for LINQ SQL generator, to properly handle '==' operator when two nullable variables/columns are involved. Previously, because 'NULL==NULL' in SQL is false, the queries failed to match 'WHERE myTbl.NullableColumn == @Prm1' for parameter value NULL.
  7. Smtp client service for sending email notifications
  8. Settings dictionary - global set dictionary (by settings type) of settings objects in a property of EntityApp, making it easy to reach settings/configs of different modules from anywhere in the app.
  9. Google reCaptcha implementation added to Vita.Web. Warning - not thoroughly tested. This is a part of overall Login module improvement effort - more on this below.
  10. ClientError API implementation - allows saving client errors (errors in JavaScript) in the server error log.
  11. SlimApi - a number of attributes and classes allowing creation of API controllers without referencing ASP.NET Web Api packages - more details below.
  12. Login entity module - significant improvements in functionality, with implementation of SlimApi controllers that make the module directly usable in a web application. More on this below.

Slim API technology
This is BIG - and I am really excited about opportunities that this functionality opens for future development. The main opportunity here is ability to create full-stack entity modules that implement the entire stack of functionality - from database tables to the UI-exposed services. Login module is an early example of this concept.
I wanted to implement something like this for quite a while, but did not see how it could be possible. The initial motivation is the artificial split of business logic code that I observed in VITA-based applications. Part of it, lower level entity manipulation code, lived in entity modules/app assemblies. The other big chunk was in API controllers in data service projects that had to reference Web API packages to implement API controllers callable from the web. This dual nature and split of business logic did not feel right. Another thing - Vita.Modules assembly provides a lot of useful pre-built functionality that can be embedded into your app. But to actually use it in a Web application, you have to build API controllers that expose the functionality as a Web API. Possible solution is creating another assembly like Vita.Modules.Web with API controllers, but this results in the same business logic split. The trouble with this split is that you often find yourself in need to use the logic in upper level API assembly from the lower level assembly, and this is very frustrating.
Fortunately, Web API is open source now, so after spending some time inside it, I figured out the way to do it. Essentially VITA's Web code hijacks the routing mechanism of Web API and redirects the calls to methods of 'regular' classes/objects that are not derived from the ApiController class as required by traditional Web API architecture. I am thinking of submitting a request to Web API team to provide a support for such an arrangement (using any class as a controller) out-of-the-box.
Now about implementation. There are 2 sets of classes, one in each of framework assemblies (Vita.dll and Vita.Web.dll).
The core VITA assembly defines a number of 'slim' attributes that are equivalent of Web API's Route, HttpGet, HttpPost etc attributes that are used for decorating API controller methods. The equivalents in VITA all start with 'Api' prefix: ApiRoute, ApiGet, ApiPost etc. The other base class is a SlimApiController - a very light base class for controllers. Use of this class is optional - you can use ANY class as a controller.
You must register controller types/instances using methods in EntityApp.ApiConfiguration object (see BooksEntityApp constructor for an example).
The other set of classes for Slim API are in the Vita.Web assembly - this is the implementation of action router that directs the web calls to the slim controllers you defined. You need to call WebHelper.ConfigureWebApi method at web app startup to enable all SlimApi functionality.
Just like in Web API, you can use either instance-per-call, or a singleton instance of the API controller. The two types differ in two aspects:
  • Registration - you register controller either as a class (per-call) or you register the instance itself.
  • Access to the current web call context (operation context and logged in user). For per-call controllers, the Context property of SlimApiController is set right after an instance is created, so you can use it in the action method. For singleton controller, we cannot use a property on controller - there may be multiple calls active at the same time, each within its own context. For this, VITA performs an automatic injection of an extra parameter - if you add a parameter of type OperationContext to the action method, VITA will automatically inject the current operation context when it calls the action method.
See examples of Slim API controllers in BooksStore sample app, and also in Vita.Modules.Login folder. There are also examples of singleton and per-call api controllers.
Notice one change - BookStore data service project in samples folder is gone, we no longer need it. Api controllers are defined in the main BooksStore assembly!

Login Module RESTful API
The Login entity module in Vita.Modules assembly had been refactored and extended, and it is now the first example of full-stack module that implements Web/UI-usable API controllers.
A general note. It might seem to a newcomer that Login functionality is not much to talk about - just a table, with username/password(hash), and a method to lookup the record to verify the user's credentials. But it turns out, it is much more. In a real world app, to be really useful and usable, the login module should implement a lot. For a start, if you haven't read it before, read the following excellent must-read discussion of password reset functionality:
http://www.troyhunt.com/2012/05/everything-you-ever-wanted-to-know.html
While it talks mostly about password reset process, the author illuminates many important aspects that apply to login functionality in general - security, identity protection, defenses against abuse, etc. It turns out logins are not so simple after all.
VITA login module is a real-life implementation of login, password reset and other processes related to login functionality. What is exciting now is that it provides a number of UI-ready API controllers that implement a number of processes directly usable in a Web application:
  • Login storage, with strong password hashing.
  • Login/logout methods, with multi-factor login and secret questions/answers management
  • Logging of all login activities, including flagged accidents like multiple failed logins and automatic suspension of account
  • Storage for extra login factors (email, phone) in encrypted form; the process of factor confirmation (proving user owns email account through pin sending)
  • Password reset process
  • Password strength verification with flexible settings
  • Login information management (self-service or by administrator), including changing password, entering and confirming extra factors (email, phone).
See detailed unit tests in Extended unit tests project for full test/demo of each of the login processes. Note that all is needed to start using Login API in the bookStore sample app is to register login API controllers in the BookEntityApp constructors - this make the controllers 'callable' through RESTful API.
Note: Right now one thing is missing in Login API - authorization. As an example, the login administration controller that allows resetting user's password by a site admin is directly callable from the Web - there is no protection (it should be available only to site administrators).
But this will be coming in the next code update, this is my next high-priority item.

Version 1.4.1/1.4.2 - April 13, 2015

  1. Nuget packaging changes. Vita.Modules.dll is moved to a separate package. Data driver for MS SQL Server is merged into main Vita.dll assembly. Now to start development on MS SQL Server you need to reference just one assembly located in a single package VITA.
  2. BCrypt implementation (used in strong password hashing) had been merged directly into Vita.Modules.dll, so there is no more dependency on external BCrypt package. The main reason is convenience and low risk of coming updates or patches in BCrypt. BCrypt code is relatively small (a few hundred lines) and stable - there were no updates for 3 years.
  3. Module Dependencies list. EntityModule can now explicitly identify its dependencies (other modules it relies on) using Requires<TOtherModule> in its constructor. For example, LoginModule declares dependency on EncryptedDataModule - meaning this module should be included into the app. If dependency is missing, a clear error message is logged. Previously, the error was confusing, saying that IEncryptedData entity type is unknown and is not registered as an entity.
  4. HeapTable attribute is dropped. It was used previously to explicitly identify an entity/table as having no clustered index. The behavior was similar to MS SQL Server - when you do not specify 'Clustered/NonClustered' tag on primary key, it is assumed to be clustered key. HeapTable attribute was not very intuitive, so new behavior is straightforward - if an entity has no ClusteredIndex attribute, it is assumed to be a heap table. Note: ClusteredIndex attribute creates a separate index, even if it is identical to the primary key. To make a clustered primary key, use PrimaryKey(IsClustered=true) attribute specification.
  5. DbModel Update notifications - this functionality was refactored. Previously the events DbModelUpdating/-ed were raised by Database object, and it was tricky to use in the application code. Now a single event DataSourceChange is raised by IDataSourceManagementService. The event has a type (Connecting, Connected, HigherVersionDetected, DbModelUpdating, DbModelUpdated). The event allows you to intercept in the process on connecting to the database and updating the database schema.
  6. DbModelUpdate options - DbSettings class has an additional options field to fine-tune the schema update process. One useful option is DropUnknownObjects, it is off by default. If this flag is not set, the system will NOT drop any tables that are unknown to it (that have no corresponding entities in entity model). This allows an application to run against a small part of bigger database model in the same schema (dbo), without disturbing other tables. Previously the system behaved as if the flag was on - so it was trying to drop all unknown tables, thinking they used to have entities but now don't so they have to be deleted.
  7. HttpClientWrapper improvements. Small but useful improvement - you can now get raw HttpResponseMessage from the call (without deserializing content) by specifying HttpResponseMessage as a value for result parameter type in GET, POST, PUT methods.
  8. Async processing refactoring. I refactored handling Async methods to use await keyword instead of specifying task continuations. The net result in functionality is the same, just using more clear modern syntax.
  9. SearchParams.DefaultIfNull() extension method - now has optional parameter for default sort order, if the property is empty in the original object. Just a convenience.
  10. Bug fix - disable batch mode if stored procedures are not enabled. There was a way previously to enable batch even though stored procedures were not supported. Future plan is to allow using batch mode with plain SQLs - when SQL generation supports it properly.
  11. Login module improvements:
    1. Password hashing. Login module now uses BCrypt strong hashing algorithm by default, with workFactor value of 10. This value results in about 100ms hashing run time per password. It is computationally expensive (as it should be), and you should consider this factor in your application performance analysis. There is a 'test-like' method TestPasswordHashers that evaluates hashing time for BCrypt and PBKDF2 hashers and reports the time - run the test to see the values for your machine. Login module also provides Pbkdf2PasswordHasher class, you can use it too. You can implement custom algorithm of your choice by injecting hasher implementing IPasswordHasher interface,
    2. Hashing work factor (# of iterations) is now saved with the corresponding login, so it might be increased over time (when processors become faster), and this is properly handled by the code - older hashes are calculated correctly, and system signals AskPasswordReset on login with older hash.
    3. DDOS attack prevention. Password hashing is computationally expensive (100ms for Bcrypt), so it would be an easy target for attack - just flood the site with bogus login attempt. Login code was modified to delay and avoid computing hash in case of bogus login. Weak password hash (just 10-bit hash based on simple fast hashing algorithm) is added to login records, and system first looks up the login record based on this easy hash. Only if record(s) is found, it proceeds to compute strong hash and compare with stored value.
    4. Actions list. LoginResult object returned as a result of successful login has new property Actions - string list. It contains codes identifying additional actions/alerts reported by Login module, Example: expiring password warning (timeframe is specified in settings, defaults to 7 days), need to reset password, need to verify email, etc. UI code can redirect the user to appropriate screen right after login.
    5. Login Management improvements ILoginManagementService has 2 new methods. GetPasswordResetAttemptCount returns the number of recent password reset attempts (even if not completed), to help block flood of requests in case of attack. CheckPasswordWasUsed helps you to implement policy of not reusing old passwords when you require users to reset password regularly (every 3 months for example). Old passwords are stored as weak hashes, there is a small probability of hash collision, but it is below 1% if you check the last 5 passwords.

Last edited Oct 30, 2015 at 8:08 PM by rivantsov, version 2

Comments

No comments yet.