News, examples, tips, ideas and plans.
Thoughts around ORM, .NET and SQL databases.

Sunday, June 26, 2011

DataObjects.Net 4.5 Beta 3

Today DataObjects.Net team is happy to publish the third beta of 4.5 version. This release mostly contains changes in the security system as well as bug fixes merged from the stable branch.

After the Beta 2 was published, we received great feedback concerning the security system and we are very glad for that. Thanks to all of you who dropped us a line concerning this stuff.

Changes from Beta 2

First and the most important one is that roles are made persistent. There were numerous requests for this feature, moreover the idea of Terje Myklebust that he formulated as a templates for roles fits to this pattern very well — a persistent class serves as a template, so you can have as many parameterized instances of it as you need. Here is the updated Role hierarchy:
public interface IRole : IEntity
  {
    [Field]
    string Name { get; }

    [Field]
    [Association(PairTo = "Roles", OnOwnerRemove = OnRemoveAction.Clear, OnTargetRemove = OnRemoveAction.Clear)]
    EntitySet Principals { get; }

    IList Permissions { get; }
  }

  [Index("Name", Unique = true)]
  public abstract class Role : Entity, IRole
  {
    [NotNullConstraint(Mode = ConstrainMode.OnSetValue)]
    [Field(Length = 128)]
    public string Name { get; protected set; }

    [Field]
    public EntitySet Principals { get; private set; }

    public IList Permissions { get; }

    protected void RegisterPermission(Permission permission);

    protected abstract void RegisterPermissions();

    protected Role(Session session)
      : base(session)
    {
      Name = GetType().Name;
    }
  }
Xtensive.Practices.Security contains the abstract class Role that implements all the required infrastructure for permission registration and handling. Each instance of Role must contain unique name. By default, Role class sets name property to name of the concrete type. In case you want more than one instances of a role of the same type, you must give them unique names. Role and Principal now are connected via paired entitysets. So a principal knows all its roles and vice versa.

Note that the Role class doesn't define hierarchy root and key fields. You must do this manually as you do in case of Principal, for example:

[HierarchyRoot(InheritanceSchema = InheritanceSchema.SingleTable)]
  public abstract class EmployeeRole : Role
  {
    [Field, Key]
    public int Id { get; set; }

    protected override void RegisterPermissions()
    {
      // This is base role for every employee

      // All employees can see products
      RegisterPermission(new Permission());
      // All employees can see employees
      RegisterPermission(new Permission());
    }

    protected EmployeeRole(Session session)
      : base(session)
    {
    }
  }
All other roles inherit EmployeeRole. Note that in case of roles the SingleTable inheritance scheme is the best option as all role types are similar and can be effectively placed inside a single table.

Other changes:

Session.ValidatePrincipal method is renamed to Session.Authenticate.
IPrincipalValidationService is renamed to IAuthenticationService
GenericPrincipalValidationService is renamed to GenericAuthenticationService
GenericRoleProvider, PrincipalRole, PrincipalRoleSet, RoleSet are discarded as useless.

Roles as templates

There was a fruitful discussion after the previous post and the idea of role templates was suggested. Here is how it can be implemented in the Beta 3. I'll take the same scenario with branches and office manager roles.

Fisrt, let's define branches
[HierarchyRoot]
  public class Branch : Entity
  {
    [Field, Key]
    public int Id { get; private set; }

    [Field]
    public string Name { get; set; }

    public Branch(Session session)
      : base(session)
    {}
  }
And then, define a branch office manager role
public class BranchOfficeManagerRole : EmployeeRole
  {
    [Field]
    public Branch Branch { get; set; }

    private IQueryable GetCustomers(ImpersonationContext context, QueryEndpoint query)
    {
      return query.All()
        .Where(c => c.Branch == Branch);
    }

    protected override void RegisterPermissions()
    {
      RegisterPermission(new Permission(true, GetCustomers));
    }

    public BranchOfficeManagerRole(Session session, Branch branch)
      : base(session)
    {
      Branch = branch;
      Name = branch.Name + "OfficeManager";
    }
  }

See, the role class is not tied to a concrete branch office instance, it only declares a connection between a role type and a branch type instead. The concrete Branch instance is passed in the constructor. Also note how Name property is formed in the constructor to avoid non-uniqueness.

Having these classes declared we can easily use them in creating the appropriate role instances as many as we need. Moreover, we can create branches and branch-dependent roles in runtime.
// Branches
var southBranch = new Branch(session) { Name = "South"};
var northBranch = new Branch(session) { Name = "North"};

// Roles
var southBranchOfficeManagerRole =  new BranchOfficeManagerRole(session, southBranch);
var northBranchOfficeManagerRole =  new BranchOfficeManagerRole(session, northBranch);

// Employees
var user1 = new Employee(session);
user1.Roles.Add(southBranchOfficeManagerRole);

var user2 = new Employee(session);
user2.Roles.Add(northBranchOfficeManagerRole);

// By adding both roles to an employee we can give him a superset of permissions
var user3 = new Employee(session);
user3.Roles.Add(southBranchOfficeManagerRole);
user3.Roles.Add(northBranchOfficeManagerRole);

Download

DataObjects.Net 4.5 Beta 3 can be downloaded from the website.

P.S.
Dear DataObjects.Net users and contributors. The Beta 3 is very close to the Release Candidate. But before the main branch is frozen, I want to clarify
a question: should permissions be persistent or not? Is it vital? If yes, how filter expressions should be stored?

If the question is given a sensible answer, we'll continue implementing persistent permissions and make Beta 4; otherwise we will proceed to RC 1.

Thank you.

P.P.S.
Dear Malisa Ncube,
It seems that after these alterations you'll have to update the SalesPoint database creation script for MySQL. Sorry for that, hope this won't tale too much time.

Wednesday, June 22, 2011

DataObjects.Net on Twitter

Hello everybody,

Finally DataObjects.Net reached Twitter. =)
Follow us for the latest news and updates.


Wednesday, June 15, 2011

SalesPoint sample database for MySQL

This is an addition to the previous post, for those who prefer using MySQL instead of Microsoft SQL Server. Malisa Ncube made a conversion of salespoint.sql file from DataObjects.Net 4.5 Beta 2 package to MySQL syntax and very generously published in his blog.

You will also find there some moving impressions about the SalesPoint sample running on MySQL because he is the official 'father' of MySQL driver for DataObjects.Net.

Thanks, Malisa!

Monday, June 13, 2011

DataObjects.Net 4.5 Beta 2

Today the DataObjects.Net Team is releasing the second beta of the upcoming 4.5 version of DataObjects.Net. The beta is mostly dedicated to the security system.

All security-related classes are located in a separate assembly called Xtensive.Practices.Security. Let's explore what types to be aware of the assembly includes:
  • IPrincipal, Principal & GenericPrincipal
  • Role & RoleSet
  • Permission & PermissionSet
  • IHashingService with several implementations
  • IPrincipalValidationService with a generic implementation
  • ImpersonationContext
  • some Session extension methods
Most of them have been already discussed in the previous posts on the topic. However, let's recall the main principals & usage scenarios on a security sample called SalesPoint.

Adding the security system to your project
1. Add reference to Xtensive.Practices.Security assembly

2. Register types from Xtensive.Practices.Security in your domain.
<domain connectionurl="sqlserver://./SalesPoint"upgrademode="Skip">
    <types>
        <add assembly="SalesPoint">
        <add assembly="Xtensive.Practices.Security">
    </types>
</domain>
Xtensive.Practices.Security contains the following persistent types: Principal, GenericPrincipal (both are abstract) and PrincipalRole which is used for storing role names for a principal.

3. Make your own persistent type that describe a user of your application. Inherit it from Principal or GenericPrincipal classes.

Principal class defines the minimum IPrincipal implementation and can be used for all kind of users, no matter whether you use login/password authorization scheme, Windows-based one or your own.

GenericPrincipal class is specifically designed for login/password authorization scheme. It already contains all the required properties and methods for storing password hash, for setting password, etc. Moreover, generic principal validation service is oriented only on GenericPrincipal class and its inheritors. So, in the overwhelming majority of scenarios you should inherit from GenericPrincipal class.

In the sample the login/password authorization scheme is used, so the Employee class is inherited from the GenericPrincipal one. Note that you have total control on properties of user hierarchy: you define the hierarchy inheritance scheme as well as the structure of Key fields.

[HierarchyRoot]
public class Employee : GenericPrincipal
{
  [Field, Key]
  public int Id { get; private set; }

  // Other fields
}

More about principal types: part 5.

4. Define which hashing algorithm will be used. In this version md5, sha1, sha256, sha384, sha512 hashing algorithms are provided plus plain one that allows to store passwords as is, without hashing. This might be useful for testing purposes but it is strongly recommended to use true hashing one in the real life applications. In the sample the plain one is used, here is how it can be configured:
<configSections>
    <section name="Xtensive.Security" type="Xtensive.Practices.Security.Configuration.ConfigurationSection, Xtensive.Practices.Security" />
  </configSections>

  <Xtensive.Security>
    <hashingService name="plain"/>
  </Xtensive.Security>
If no hashing service is set, then the system will fall back to the 'plain' one.

Managing passwords and validating users

Having the above-mentioned steps done, you are getting the ability to manage users, set their passwords and validate them. Here is how:
using (var session = domain.OpenSession()) {
  using (var trx = session.OpenTransaction()) {

    var employee = new Employee(session);
    employee.Name = "Steve Ballmer";
    employee.SetPassword("developers, developers, developers, developers");
    trx.Complete();
  }
}

using (var session = domain.OpenSession()) {
  using (var trx = session.OpenTransaction()) {

    var employee = session.ValidatePrincipal("Steve Ballmer", "developers, developers, developers, developers");
    Assert.IsNotNull(employee);
    trx.Complete();
  }
}

Defining roles

Whereas user management is the essential part of any security system, roles sometimes are used as an optional addition, thus loosing all their power. But it's you who decide whether to employ them or not. In Xtensive.Practices.Security roles are optional too.
Roles are ordinary classes, not persistent ones, and so are permissions. In order to utilize the roles mechanism you inherit from base Role<T> class and declare permissions for domain model types, like here:

public class EmployeeRole : Role
{
  public EmployeeRole()
  {
    // This is base role for every employee.
    // It defines read-only access to products and employees for all staff.

    // All employees can see products
    RegisterPermission(new Permission<Product>());
    // All employees can see employees
    RegisterPermission(new Permission<Employee>());
  }
}

public class StockManagerRole : EmployeeRole
{
  public StockManagerRole()
  {
    // Stock manager inherits all permissions from Employee role
    // In addition, it declares write access to products

    // Stock managers can see and edit products
    RegisterPermission(new Permission<Product>(canWrite:true));
  }
}

Here is more advanced role declaration. You can declare secure queries for persistent types and use you own permission classes as well:

public class SalesRepresentativeRole : EmployeeRole
{
  private static IQueryable<Order> GetOrdersQuery(ImpersonationContext context, QueryEndpoint query)
  {
    // Sales representative role has access to its own orders only
    return query.All<Order>()
      .Where(o => o.Employee == context.Principal);
  }

  public SalesRepresentativeRole()
  {
    // Sales representative inherits Employee permissions

    // Sales representative can see and edit customers
    RegisterPermission(new Permission<Customer>(canWrite:true));
    // Sales representative can see and edit sale orders but not approve
    RegisterPermission(new OrderPermission(canWrite:true, canApprove:false, GetOrdersQuery));
  }
}
Important: for the ease of use give your roles parameterless constructors. If this is unacceptable, then you should provide to a framework a list of role instances. I'll describe this this later.

More about roles and permissions: part 2, part 3, part 4.

Impersonation

The next question after we've successfully defined users, authentication scheme, roles and permissions is "How the hell will these parts work together?". Let's see.

You already know how to validate user by the pair of name and password.

If the validation is successful, you may impersonate current Session with user's account. After this is done, the security framework will provide you with all necessary infrastructure on what permission the user has, which roles he is in.

using (var session = domain.OpenSession()) {
  using (var trx = session.OpenTransaction()) {

    var employee = session.ValidatePrincipal("Steve Ballmer", "developers, developers, developers, developers");

    // Opening an impersonation context
    var context = session.Impersonate(employee);

    // Checking permissions
    context.Permissions.Contains<Permission<Customer>>(p => p.CanRead);
    context.Permissions.Contains<OrderPermission>(p => p.CanApprove);

    // Closing the context
    context.Undo();

    trx.Complete();
  }
}

Here is how permissions are used to restrict screens of the sample application:


Note that currently logged in employee is in StockManager role that prohibits access to Customers and Orders. The main menu just checks whether current impersonation context contains the appropriate permission and acts accordingly by enabling or disabling controls.

Probably, the most exciting and important feature of the framework is automatic application of secure filters to every query that is being executed through Session.Query endpoint.

For example, if we log in with an employee who is in SalesRepresentative role, which restricts all orders to his own orders only, we will see the following picture:


We logged in as a Robert King, a sales representative. He can see only his own orders as the filter is declared in SalesRepresentative role and is applied automatically. The application doesn't know about users, permissions, roles, etc. All this stuff is provided by Xtensive.Practices.Security layer. Note that Customers and Orders views are available to him, but the "Approve" button is not, because the role doesn't declare access to "Approve" action.

What will happen if we log in as SalesManager? Let's see:


The user also has access to Customers and Orders as SalesManager role inherits it from SalesRepresentative one. Moreover, the role doesn't have such restriction for Orders, the employee see not only his own orders but the orders of employees from his department. In addition, the button "Approve" is enabled.

More on applying security filters to queries: part 6

A few notes about the impersonation context
ImpersonationContext class implements IDisposable, so you can employ using pattern. The context also supports nesting.
Current impersonation context instance can be accessed through Session, so you don't have to pass the reference to the context everywhere you might need it.
var context = session.GetImpersonationContext();

How to build the SalesPoint sample application
SQL script that builds the required "SalesPoint" database is included into the solution "Samples" and is located in the root folder of the SalesPoint project. Run the script against your database server and check that the database is successfully created. After that update the connectionUrl in app.config file according to the
path of your database server.

Download

DataObjects.Net 4.5 Beta 2 can be downloaded from the website.

P.S.
Dear users of DataObjects.Net!
This is DataObjects.Net Beta 2, so I'm asking for your feedback. Play with the sample or try creating your own one, try applying the security framework to your real projects, do anything you want with it including the most weird scenarios (I know you can), I'll be happy to receive any comment no matter good or bad from you. Any of these will definitely help to make the product better and keep our requirements for high quality product.

Thank you.

Wednesday, June 08, 2011

Report about Uganda .NET Usergroup

Malisa Ncube has written a nice post about the event that took place on Friday, 27th May 2011. Malisa presented DataObjects.Net on the meeting and demonstrated how DataObjects.Net fits into Microsoft technology stack.

Don't miss the downloadable presentation. The link is located in the end of his report. Really nice one!

Tuesday, June 07, 2011

DataObjects.Net Entity Designer update

Talented Peter Ĺ ulek announced the major update of the Entity Designer project.

The update includes list of bug fixes along with the new important features like POCO/DTO generation support, optional DataContract and DataMember attributes generation, redesigned "Add association" dialog and much more.

Redesigned Association dialog

Read complete announcement in Peter's blog.

So, grab the bits and start playing with the tool. Any feedback is highly appreciated. The sooner the designer is tested in various scenarios, the sooner it will be released in production. So, DataObjects.Net community members, participate! Help making the tool perfect.

Saturday, June 04, 2011

On security system, part 6

It seems that in the end of the previous post I was a bit wrong. One more topic left for detailed discussion. And its name is "Query conflicts resolving".

Let me explain it a bit. Say, there is a consulting firm that is managed by 2 managers. First of them, Dan, is responsible for negotiations with automobile companies, while the second one, John, works with aircraft enterprises. According to the approach that is described in the previous set of posts the security domain model would contains of two roles: AutomobileManagerRole and AircraftManagerRole, each of them would include a correspondent restrictive query.

public class AutomobileManagerRole : Role
  {
    private static IQueryable<Customer> GetCustomers(ImpersonationContext context, QueryEndpoint query)
    {
      return query.All<Customer>()
        .Where(c => c.IsAutomobileIndustry);
    }

    public AutomobileManagerRole()
    {
      RegisterPermission(new CustomerPermission(true, GetCustomers));
    }
  }

  public class AircraftManagerRole : Role
  {
    private static IQueryable<Customer> GetCustomers(ImpersonationContext context, QueryEndpoint query)
    {
      return query.All<Customer>()
        .Where(c => c.IsAircraftIndustry);
    }

    public AircraftManagerRole()
    {
      RegisterPermission(new CustomerPermission(true, GetCustomers));
    }
  }

Dan would be associated with AutomobileManagerRole and John - with AircraftManagerRole. The roles effectively avoid both of them to see customers that belong to industry other than a manager is responsible for.

But imagine a situation when one of the managers, let it be Dan, left on a vacation. To continue automobile customers management process John should get access to customers of Dan for the vacation period. To achieve this, a system security administrator goes to a security administration console and temporarily adds AutomobileManagerRole to John's account. As a result, John's account starts containing 2 roles, both of them with restrictive query for customers.

So, what should happen when John will execute a query for customers? How the conflict between 2 restrictive queries should be resolved?

The answer is that if the first role grants access to X set of entities and the second role grants it to Y set of entities then the result of combination of these roles should be a union of X and Y, not intersect or except.

Applying this approach to the situation with the managers, John will get access to the result of the following query:

query.All<Customer>()
  .Where(c => c.IsAutomobileIndustry)
  .Union(query.All<Customer>()
        .Where(c => c.IsAircraftIndustry));

This approach will be used always when impersonation context is active and user account contains more then one role with a restrictive query for a querying persistent type.