generalpiston / typeorm-encrypted

Encrypted field for typeorm.
MIT License
74 stars 19 forks source link

Only works with Active Record pattern #12

Closed chimon2000 closed 4 years ago

chimon2000 commented 6 years ago

When attempting to use this library with the Data Mapper pattern, the encryption options are ignored. This is because of the following line in the transformer: entity.constructor === target && mode == "regular". When an object is passed into the save/create function, constructor does not exist.

Example:

@Entity()
class User {
    @Column(<ExtendedColumnOptions>{ type: 'varchar', name: 'name_first',
    encrypt: {
      key: "e41c966f21f9e1577802463f8924e6a3fe3e9751f201304213b2f845d8841d61",
      algorithm: "aes-256-cbc",
      ivLength: 16,
      iv: "ff5ac19190424b1d88f9419ef949ae56"
    }}) 
    firstName: string

    @Column('text', {name: 'name_last'}) 
    lastName: string
}

const userRepository = connection.getRepository(User);

await userRepository.save({ firstName: 'John', lastName: 'Smith'}) 

console.log(await userRepository.find()) //No results
generalpiston commented 6 years ago

Repository metadata isn't passed to subscribers. This makes the data mapper pattern more difficult to support. As a work around, I'll add looseMatching to the ExtendedColumnOptions interface.

@pleerock could you advise on passing metadata to the subscribers interface?

pleerock commented 6 years ago

@abec can you explain what to you mean and what do you need from typeorm in more details? (remember I don't know typeorm-encrypted internals)

generalpiston commented 6 years ago

The auto-encrypt mechanism is using an EventSubscriber @pleerock:

@EventSubscriber()
export class AutoEncryptSubscriber implements EntitySubscriberInterface {
  /**
   * Encrypt before insertion.
   */
  beforeInsert(event: InsertEvent<any>): void {
    encrypt(event.entity);
  }

  /**
   * Encrypt before update.
   */
  beforeUpdate(event: UpdateEvent<any>): void {
    encrypt(event.entity);
  }

  /**
   * Decrypt after find.
   */
  afterLoad(entity: any): void {
    decrypt(entity);
  }
}

The Event objects have at most to attributes: Entity and EntityManager. I don't believe the EntityManager object has a target or repository associated with it. The encrypt and decrypt messages are being passed plain objects when using the data mapper patter like so: userRepository.save({ firstName: 'John', lastName: 'Smith'}). These plain objects are missing a valid target for filtering:

export function encrypt<T extends ObjectLiteral>(entity: T): T {
  for (let columnMetadata of getMetadataArgsStorage().columns) {
    let { propertyName, mode, target } = columnMetadata;
    let options: ExtendedColumnOptions = columnMetadata.options;
    let encrypt = options.encrypt;
    if (encrypt && mode === "regular" && (encrypt.looseMatching || entity.constructor === target)) {
      if (entity[propertyName]) {
        entity[propertyName] = encryptData(Buffer.from(entity[propertyName], "utf8"), encrypt).toString("base64");
      }
    }
  }
  return entity;
}

Does it make sense to have the EventSubscriber include a valid target (ie: https://github.com/typeorm/typeorm/blob/0.1.9/src/repository/Repository.ts#L132)? In the above code, entity.constructor === target would become entityTarget === target. I believe to do so, SubjectOperationExecutor would have to be modified.

generalpiston commented 6 years ago

@chimon2000 just had a thought... you don't have to use the subscriber to autoencrypt. You can also use the transformers directly (https://github.com/abec/typeorm-encrypted/blob/470da0c7fd23eb1660d26bbb44c5562354a12dfa/src/transformers/index.ts) and use listeners to achieve a similar effect (http://typeorm.io/#/listeners-and-subscribers/what-is-an-entity-listener).

et commented 6 years ago

Did anyone find a solution this? I really like the subscriber pattern because it keeps the model clean.

ghost commented 6 years ago

@et I haven't had time to tackle this. If you really need it, we can work together and get this going :).

et commented 6 years ago

@abeathos - No worries. I used the hooks and they worked just fine. Thanks!

ghost commented 6 years ago

@et do you mean listeners?

et commented 6 years ago

Ah yup. That's what I meant.

generalpiston commented 4 years ago

Added transformers in #14.