loopbackio / loopbackio/loopback-next

Best practice for encrypting model properties

オープン
#2,095 コメント 9 件 リアクション 5 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

feature good first issue needs discussion Repository
主要言語
TypeScript
スター
5.1k
フォーク
1.1k
平均マージ
2日 21時間
マージ済み PR(30日)
27

説明

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:

async create(entity: DataObject<MyModel>, options?: AnyObject): Promise<MyModel> {
  for (let propertyName in this.entityClass.definition.properties) {
    if (this.entityClass.definition.properties[propertyName]['encrypted']) {
      entity[propertyName] = encrypted(entity, propertyName)    
    }
  }
  return (await super.create(entity, options));
}

There are a couple of problems with this implementation.

  • This method is not generic, as if tomorrow I would like to implement another logic?
  • It requires to override a lot of methods in the repository.
  • It requires editing per repository meaning that the CLI only covers a small part of the creation process.

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 #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:

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.
}

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

DefaultCrudRepository から始め、提案で説明されている create path と対応する conversion paths をたどります。entityToData フックと dataToEntity フックの動作を定義し、その後、Key Concepts > Repositories のドキュメントをプロパティの暗号化/復号化ガイドで更新します。完了とは、受け入れ基準が満たされていることを意味します。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
typescript
領域
backend-api-design, documentation
issue の種類
機能追加
難易度
4/5
見積もり時間
3〜5日
活発さ
停滞
明瞭さ
おおむね明確
初心者へのやさしさ
35/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。