-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Best practice for encrypting model properties #2095
Comments
Hey @bajtos, |
Loosely related: #1919 I think this feature would be much easier to implement if we had load/persist hooks in LB4. The "load" hook could be implemented by overriding I think we should add Hopefully, such hooks will address your concern It requires to override a lot of methods in the repository. As for the third concern, It requires editing per repository meaning that the CLI only covers a small part of the creation process:
I am not sure if our CLI allows developers to pick a custom Repository class as the base. If it does not, then I think we should enhance it to do so.
I am afraid I don't understand, could you please explain in more details? |
First of all, I agree with you. About the overriding and inheritance solution, it means that the CLI is not enough by its own. And the last part, I feel I didn't explain myself well. Let me clarify:
The thing is, in the repository, I need to override 9 methods (create, createAll, find, findone, findbyid, update, updateall, udpdatebyid, replacebyid); While I only need to perform this action after any POST, PATCH (same logic) and GET. It might seem a minor difference, but in case there will be a need to implement extra logic it adds up. |
👍
Operation hooks are unfortunately not on our current roadmap for 2019 Q1, see #1839 There are many areas where LB4 is behind LB3 in feature parity, see #1920. We are regularly reviewing this list to decide what to work on next. BUT! LoopBack is an open project and we encourage our community to contribute features they are interested in. As an author of Operation Hooks in LB2/LB3, I am happy to help you myself along the way if you decide to work on this feature.
In my view, this is a missing feature of LB4 CLI that should be (hopefully) easy to implement. Few ideas to consider:
Sure, I agree with you. The solution I described in my comment was a workaround based on what LB4 provides right now. It has more downsides besides what you wrote, for example when So what I would like to see instead: (1) A mechanism allowing Repository classes to execute custom code whenever the repository is trying to convert model instance into raw data to be stored and also from the raw data to model instance. This is basically an Operation Hook, and it should be implemented by DefaultCrudRepository. For example, we can leverage a protected repository method. class DefaultCrudRepository<T, ID> /*...*/{
protected async entityToData(entity: DataObject<T>, options?: Options): DataObject<T> {
// "persist hook" - no-op by default
return entity;
}
protected async dataToEntity(data: DataObject<T>, options?: Options): DataObject<T> {
// "load hook" - no-op by default
return this.toEntity(data);
}
async create(entity: DataObject<T>, options?: Options): Promise<T> {
const data = await this.entityToData(entity, options);
const model = await ensurePromise(this.modelClass.create(data, options));
const result = await this.dataToEntity(model);
return result;
}
// etc.
} (2) A convention describing how 3rd-party extensions should contribute different hook implementations. We can leverage class inheritance but also mixins. An example of a mixin-based approach: export EncryptedPropertiesMixin(Repository: typeof DefaultCrudRepository) {
return class extends Repository {
protected async entityToData(entity: DataObject<T>, options?: Options): DataObject<T> {
const data = super.entityToData(entity, options);
// encrypt selected properties
}
protected async dataToEntity(data: DataObject<T>, options?: Options): DataObject<T> {
// decrypt selected properties in data
return this.toEntity(data);
}
};
} (I am not sure if protected methods can be used with mixins, I vaguely remember there was some TypeScript limitation related to this.) (3) Integration with
In my mind, this approach is solving the problem at a wrong place. Encryption/decryption needs to happen at data access level, to ensure that properties are correctly encrypted/decrypted when changing data from TypeScript code. For example, when seeding the database with sample data from a test setup, there is no HTTP request and no HTTP verb like POST/GET. @strongloop/loopback-next what are your thoughts on Operation hooks, especially the pair "load" and "persist"? |
@bajtos since the custom repository is a good solution (until hooks will be implemented), I guess we can close this issue, right? edit - |
This should be available soon, see #2235
Not really. While you can implement encryption/decryption in a custom repository, it would mean overriding every persistence method and possibly missing newly added methods in the future. I'd prefer to keep this issue open at least until we have the following mechanism in place & have an official documentation describing how to implement property encryption.
See #2095 (comment) for more details and a code snippet. |
@TomerSalton are you still interested in this feature? Would you like to contribute the improvement of our DefaultCrudRepository yourself? See https://loopback.io/doc/en/contrib/ to get you started, I am happy to help you along the way. |
I am glad to hear that. I'd still prefer to keep this issue open so that we don't forget to make property encryption feel more like a first-class citizen. It's also a good opportunity for people looking for places where to start contributing. Good luck with your work on hasAndBelongsToMany 🤞 |
Description / Steps to reproduce / Feature proposal
In my application, I have models with properties that need to be encrypted before saved in the database.
I'm trying to decide what's the best practice to implement that use-case.
Current implementation
What I'm currently doing is set an attribute in the property itself:
encrypted: true
In my repository, I override the relevant methods. e.g. create:
There are a couple of problems with this implementation.
Is there any better implementation for this issue?
Acceptance criteria
A mechanism allowing Repository classes to execute custom code whenever the repository is trying to convert model instance into raw data to be stored and also from the raw data to model instance. This is basically an Operation Hook, and it should be implemented by DefaultCrudRepository. See Best practice for encrypting model properties #2095 (comment) for more details, the code snippet is cross-posted below.
A section in our documentation (e.g. in Key Concepts >> Repositories) explaining how to use these new mechanism.
A guide in our documentation showing how can applications implement property encryption/decryption.
Proposed implementation:
The text was updated successfully, but these errors were encountered: