Ayende @ Rahien

Refunds available at head office

Entities dependencies best practices

The question came up in the ALT.Net list, and it is fairly common in the DDD circles as well.

I have an User with Email address, which I want to keep encrypted in the database, but visible to the application. How do I handle this scenario in the entity?

We can do it in our service layer, and encrypt / decrypt that outside the entity, but that has two issues. It couple the entity to the service and it means that we can forget to encrypt the email at some point.It also smells.

Another option, which I call the brute force option, is to pass the dependency to the entity and let it handle its own concerns. Here is implementation:

image

Yes, it is ugly, and even using this approach you can improve upon it, but let us focus on the interesting tidbits. First, not only does our entity know about an infrastructure service, it is also aware of how to create it. It has to do so, because we have to provide an empty ctor for NHibernate to use.

We can generalize this a bit, leading us to this scenario:

image

This piece of code is akin to nails on board to me. It isn't how I want to see code written.

A better way exists. We will start by making a slight modification to the User class, we remove the empty ctor. Well, we can't remove it at the moment, there is check in NHibernate for this, which I need to find and remove, so for now, we have this:

image

Note that this also requires that you'll specify the unsaved value of the identifier in the configuration.

Now that we have established that we have no way of creating the User using the empty ctor, we let NHibernate know how it can create it:

public class EntityDependenciesInterceptor : EmptyInterceptor
{
    public override object Instantiate(string clazz, EntityMode entityMode, object id)
    {
        if(entityMode!=EntityMode.Poco)
            return base.Instantiate(clazz, entityMode, id);
        if(clazz != typeof(User).FullName)
            return base.Instantiate(clazz, entityMode, id);
        return new User(CryptoProvider.Instance);
    }
}

Just for User, we override the way NHibernate create entities. Now we can register it in the session, and we are done:

using (ISession session = sessionFactory.OpenSession(new EntityDependenciesInterceptor()))
{
    IList list = session.CreateCriteria(typeof(User)).List();
}

This is still not something that I particularly like. I don't see encryption as part of the responsibilities of the entity. It is just something we need in order to save encrypted values to the DB. So let make it explicit.

NHibernate has the notion of IUserType, which let you take actions on save / load of values from the DB.

First, we make User POCO again, remove all traces of the previous ugliness. Now, we change the mapping for User so the email property is defined so:

<property name="Email" type="Samples.EncryptedStringUserType, Samples" />		

And now we need to create the user type:

public class EncryptedStringUserType : IUserType
{
    public bool Equals(object x, object y)
    {
        return object.Equals(x, y);
    }

    public int GetHashCode(object x)
    {
        return x.GetHashCode();
    }

    public object NullSafeGet(IDataReader rs, string[] names, object owner)
    {
        object r = rs[names[0]];
        if (r == DBNull.Value)
            return null;
        return CryptoProvider.Instance.Decrypt((string) r);
    }

    public void NullSafeSet(IDbCommand cmd, object value, int index)
    {
        object paramVal = DBNull.Value;
        if (value != null)
            paramVal = CryptoProvider.Instance.Encrypt((string) value);
        IDataParameter parameter = (IDataParameter)cmd.Parameters[index];
        parameter.Value = paramVal;
    }

    public object DeepCopy(object value)
    {
        return value;
    }

    public object Replace(object original, object target, object owner)
    {
        return original;
    }

    public object Assemble(object cached, object owner)
    {
        return cached;
    }

    public object Disassemble(object value)
    {
        return value;
    }

    public SqlType[] SqlTypes
    {
        get { return new SqlType[]{new StringSqlType()}; }
    }

    public Type ReturnedType
    {
        get { return typeof(string); }
    }

    public bool IsMutable
    {
        get { return false; }
    }
}

And that is it, you can now get transparent encryption / decryption when you are going to the DB, at no cost to the application design.

Comments

Krzysztof Koźmic
07/31/2008 01:20 PM by
Krzysztof Koźmic

This is more of a NHibernate question, but why NHibernate requires empty ctor on classes? AFAIK it uses Castle Dynamic Proxy for facilitating lazy loading and other stuff... can't it use Windsor to create objects? Or is it something deeply buried in its design that requires that?

A guy who don't like IOCs
07/31/2008 02:13 PM by
A guy who don't like IOCs

"at no cost to the application design.... "

and

with the cost of spending so much of development time to just do a email address encryption ... very funny!!

Stiiifff
07/31/2008 02:38 PM by
Stiiifff

I like the solution Ayende ... just one thing that annoys me: I consider user types be part of the Domain Model ... and here, NHibernate is clearly leaking into the Domain Model. While NHibernate usually allows us to have PI in entities, it doesn't give the same level of PI for user types.

Konstantin
07/31/2008 02:50 PM by
Konstantin

Interesting description of the stream of thought! But isn't this more a text on cool NHibernate feature than on loose coupling?

Julian Birch
07/31/2008 03:08 PM by
Julian Birch

Wouldn't a property accessor be more elegant? That way you can deal with the encryption independently of the data access code.

Jimmy Bogard
07/31/2008 03:21 PM by
Jimmy Bogard

Side note, but I'm fairly positive the entity constructor doesn't have to be public. Just like serialization scenarios, it just has to exist. Private would work as well, AFAIK.

John Chapman
07/31/2008 03:41 PM by
John Chapman

I have one issue with the final solution.

Why is it acceptable for the user type know about the infrastructure service? Wouldn't it be preferable here to have the service injected, or even access it via the service locator? I know you don't like using the service locator approach, but why not in this case?

Sneal
07/31/2008 04:05 PM by
Sneal

At first I wondered what you were doing...

I like the user type approach because the User entity doesn't really care anything about encryption. Encryption is ONLY a persistence concern. Using a user type makes the domain model much more flexible and removes the infrastructure concerns.

What I can't remember is can you still declare the Email property as a string even though you've declared it in the mapping as type EncryptedStringUserType?

Corey
07/31/2008 04:09 PM by
Corey

What happens when an entity must be serialized?

Ayende Rahien
07/31/2008 04:23 PM by
Ayende Rahien

That is because NH isn't supposed to force you to take dependencies on other infrastructure concerns.

You can use NH without Ioc,

Ayende Rahien
07/31/2008 04:28 PM by
Ayende Rahien

Stiff,

User Types are a bridge between you and NHibernate, they have nothing to do with the domain

Ayende Rahien
07/31/2008 04:30 PM by
Ayende Rahien

Julian,

What you mean, property accessor?

Eduard Gomolyako
07/31/2008 04:33 PM by
Eduard Gomolyako

To avoid NHibernate checks for empty constructor you can use private empty ctor instead of public with ugly exception :) this approach is working for me still I don't use lazy loading and proxy mechanisms.

Ayende Rahien
07/31/2008 04:36 PM by
Ayende Rahien

Sneal,

Yes, you can define this as a string.

Ayende Rahien
07/31/2008 04:37 PM by
Ayende Rahien

A guy who don't like IOCs,

Um, what long amount of time are you talking about?

Ayende Rahien
07/31/2008 04:38 PM by
Ayende Rahien

Konstantin ,

It is demonstrating loose coupling using an NHibernate feature.

I am not going to build an OR/M just so I could show how loose coupling works, sorry

Ayende Rahien
07/31/2008 04:38 PM by
Ayende Rahien

John,

IUserType is part of the infrastructure

Julian Birch
07/31/2008 04:45 PM by
Julian Birch

I was thinking of NHibernate.Properties.IPropertyAccessor.

I wrote a post about it here: http://www.colourcoding.net/Blog/archive/2008/07/23/denormalize-data-using-nhibernates-property-accessors.aspx

The problem, of course, is that then you need to write the property access code again, although that would be ameliorated by inheriting by an NHibernate standard accessor.

Ayende Rahien
07/31/2008 04:51 PM by
Ayende Rahien

That is a way, but IPA is not something that we intend for people to use.

IUserType is

Krzysztof Koźmic
07/31/2008 05:06 PM by
Krzysztof Koźmic

@Ayende

Yes, I know that it does not require me to use IoC.

but what if I don't want to have default ctor?

For example I want my cannonical Customer Entity to have his Name and Address be read-only and as such - I can set them only in the ctor.

I'm aware that there is some way of doing it, but IIUC it is per-entity-type solution and it requires quite a lot of work.

Does it sound reasonable or am I approaching the problem from the wrong angle?

Ayende Rahien
07/31/2008 05:41 PM by
Ayende Rahien

IInterceptor.Instantiate is how you do it, doesn't take a lot of work

Mike Thomas
07/31/2008 05:54 PM by
Mike Thomas

What about creating an interceptor that encrypts / decrypts the fields for you? The interceptor could be driven either off of some configuration if you wanted to keep your POCOs clean or an [Encrypt] attribute if you didn't mind putting that sort of thing in your domain model.

Would this be a reasonable solution?

Ayende Rahien
07/31/2008 05:55 PM by
Ayende Rahien

That is not what an interceptor is for.

You could do it at the proxy factory level, though

Mike Thomas
07/31/2008 06:20 PM by
Mike Thomas

Interesting, in one project I am using AR and the AR Base and use events hooked in OnFlushDirty/BeforeSave to intercept properties and change their values before something is committed to the database. (I do this without putting these events in the entity classes themselves so it isn't that messy)

I was under the impression that these methods in ARBase were fired via an NH interceptor. Is what I am doing a bad way of doing this?

Ayende Rahien
07/31/2008 06:26 PM by
Ayende Rahien

I would recommend changing that to a user type, yes

Stefan Lieser
07/31/2008 06:27 PM by
Stefan Lieser

Why not make the email address a class of its own with encrypted and decrypted property getters? The NH mapping would map to the encrypted property, the application would access the decrypted one.

This way the behaviour of the email address would be made explicit in the business doamin.

Ayende Rahien
07/31/2008 06:31 PM by
Ayende Rahien

Stefan,

That is an excellent suggestion!

Frans Bouma
07/31/2008 07:04 PM by
Frans Bouma

Why doesn't NHibernate have the feature like a typeconverter ? Then you can specify it in the mapping. The value is passed to the type converter by the o/r core the value returned is sent to the db .When the entity is fetched, the value is passed to the type converter and the value returned is placed in the entity.

You can do encryption/decryption but also all kinds of nasty conversions you don't want to deal with at a class level, like 'Y'/N conversions to boolean if you want to work with booleans but have to work with Oracle for example.

Gary
07/31/2008 07:14 PM by
Gary

If you did what Stefan suggests, wouldn't you still need the ICryptoProvider dependency within an Entity. It would be in the EmailAddress entity instead of the User one.

Ayende Rahien
07/31/2008 07:17 PM by
Ayende Rahien

Frans,

Yes, it has. IUserType :-)

Krzysztof Koźmic
07/31/2008 08:10 PM by
Krzysztof Koźmic

@Ayende

I'm still unsure about this approach. If I understand it correctly from looking at method's signature and your above example all I get is string telling me the name of the type I'm asked to create, it's entity mode and id.

If I have Customer data fetched from DB and I'm about to create one (assuming I have only one ctor: the one that takes CustomerName and Address) I can't create the customer since I don't have those two properties.

The way I understand NHibernate's instantiation works is:

  1. Get data from the DB

  2. Do the IUserType conversions the way you showed in the last example

  3. Create the object with default ctor.

  4. Set fetched data to properties

What I was asking about, is if it's possible to replace step 3 and 4 with other logic, like registering fetched dependencies and properties with Windsor and let it take care of instantiation (and selecting ctor that should be used).

Does it make sense?

Ayende Rahien
07/31/2008 08:11 PM by
Ayende Rahien

Krzysztof,

Yes, that is the way it works, and no, there isn't such option.

consider the case of lazy loading an entity, for example

Krzysztof Koźmic
07/31/2008 08:26 PM by
Krzysztof Koźmic

Hmmmm,

For last 10 minutes i've been trying hard to find a counter argument for that, but the more I thing about it, the more I see that given constraints current design is the best choice available.

Thanks.

And as a sidenote, it's yet another time when you answer a question fully and completely with one few-words sentence and there's not really much more to say after that.

Geoff Bennett
08/01/2008 04:17 AM by
Geoff Bennett

@Krzysztof,

Isn't there an option in the mapping files to tell it to populate an entity using the private backing fields? Mixed with Ayende's solution, would this not give you the behaviour you're looking for?

Krzysztof Ko&#197;&#186;mic
08/01/2008 05:42 AM by
Krzysztof Koźmic

@Geoff

would that work with readonly fields as well? i mean readonly as in 'c# readonly' keyword, not 'without public setter'. I'm afraid not.

Geoff Bennett
08/01/2008 06:22 AM by
Geoff Bennett

@Krzysztof,

Well, you're right there. :) However, what is the actual desired behaviour you're trying to achieve? Is it that a consumer of the class can't alter the property value or reference? Then relaxing the readonly requirement and adding the private setter might get you the desired result.

Krzysztof Koźmic
08/01/2008 07:35 AM by
Krzysztof Koźmic

@Geoff

Are you suggesting code like this?

public class Customer

{

public Name Name { get; private set; }

private Customer(){} //to satisfy NHibernate

public Customer(Name name) {Name = name; }

}

Yes, this would get the job done, in this case.

However it smells. I don't want to throw buzzwords at you, but now I have to design my entity having limitation of specific persisting framework in mind, whereas it should really be persistence agnostic.

What will the next guy do, when he comes to change the code two years from now, when I'm long gone from that project? This approach simply can't guaranty that he won't change the field somewhere in his new added code, breaking my whole assumptions about it.

Sure I can add NDepend rule to check that the field is modified only from within NHibernate and fail the build but it's really a lot of overhead to satisfy 3rd party framework in my domain model.

No flames please :)

Letting 3rd parties in would really be not a good idea, but maybe extending NHibernate to handle simple cases like that is a goal worth giving a thought? As for lazy loading, well, maybe I don't care about it that much. Maybe I always want to use Name wherever I use Customer so lazy loading does not make sense for it anyway?

Paul Cowan
08/01/2008 07:41 AM by
Paul Cowan

Should encryption not be only one way?

Is it not bad practice to have an algorithm that can be decrypted?

Ayende Rahien
08/01/2008 10:16 AM by
Ayende Rahien

Krzysztof ,

Extending NH is the way to go in this scenario

Ayende Rahien
08/01/2008 10:17 AM by
Ayende Rahien

Paul,

One way encryption is fairly useless.

How do I get the email back ?

Paul Cowan
08/01/2008 10:27 AM by
Paul Cowan

How do I get the email back

You cannot get the email back but it is the safest way.

It all depends on the level of security you are after.

You store the users password as encrypted in the database and when they come to log in, you encrypt their password input and check it against the database.

It is not reversible and therefore the safest way.

Saying that SHA1 is no longer deemed safe.

We use SHA256.

Ayende Rahien
08/01/2008 10:36 AM by
Ayende Rahien

Paul,

You seem to be confusing the issue of needing to find a match and needing to get the data.

Password vs. Email is the simple case.

I don't care what your password is, I just need to match it: hash it.

I don't want your email in clear text in my DB, but I still need to access it so I can send you stuff: encrypt it.

Chadly
08/03/2008 09:43 PM by
Chadly

What if I wanted to pass in the CryptoProvider instance to the IUserType class via dependency injection instead of through static singleton? Does NHibernate support this?

Or in this particular case, would having the IUserType class have a reference to the dependency container be "ok" practice?

Ayende Rahien
08/03/2008 09:53 PM by
Ayende Rahien

No, NHibernate doesn't support this.

And I consider this an OK solution, because IUserType is part of the infrastructure

Chris Ortman
08/04/2008 12:50 PM by
Chris Ortman

Ayende, what are your thoughts on using this approach versus setting up the dependencies in an implementation of IUserRepository?

Ayende Rahien
08/04/2008 07:38 PM by
Ayende Rahien

No issue with that, the UserRepositoryImpl is another infrastructure service

Riley Taylor
08/07/2008 02:06 PM by
Riley Taylor

Ayende, are you sure your below method is complete? Looks like it never actually persists anything:

public void NullSafeSet(IDbCommand cmd, object value, int index)

{

    object paramVal = DBNull.Value;

    if (value != null)

        paramVal = CryptoProvider.Instance.Encrypt((string) value);

    IDataParameter parameter = (IDataParameter)cmd.Parameters[index];

    parameter.Value = paramVal;

}
Riley Taylor
08/07/2008 02:18 PM by
Riley Taylor

Ah - never mind - reflecting around, I see that the job of this method is just to mutate the IDbCommand appropriately.

Comments have been closed on this topic.