ts-prorm-orm 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1111 -0
- package/LICENSE +21 -0
- package/README.md +573 -0
- package/dist/audit/history-query.js +180 -0
- package/dist/audit/index.js +23 -0
- package/dist/audit/logger.js +236 -0
- package/dist/cache/cache-manager.js +84 -0
- package/dist/cache/index.js +16 -0
- package/dist/cache/redis-cluster-cache.js +557 -0
- package/dist/cli.js +2200 -0
- package/dist/compliance/audit-trail.js +68 -0
- package/dist/compliance/backup-verification.js +685 -0
- package/dist/compliance/breach-detector.js +505 -0
- package/dist/compliance/consent-record.js +227 -0
- package/dist/compliance/consent-versioning.js +331 -0
- package/dist/compliance/cross-border-log.js +530 -0
- package/dist/compliance/data-classifier.js +258 -0
- package/dist/compliance/data-lineage.js +565 -0
- package/dist/compliance/data-masker.js +303 -0
- package/dist/compliance/data-portability.js +265 -0
- package/dist/compliance/data-retention.js +195 -0
- package/dist/compliance/dsar-workflow.js +342 -0
- package/dist/compliance/field-encryption.js +223 -0
- package/dist/compliance/immutable-record.js +148 -0
- package/dist/compliance/index.js +260 -0
- package/dist/compliance/privacy-impact-assessment.js +364 -0
- package/dist/compliance/pseudonymization.js +269 -0
- package/dist/compliance/query-firewall.js +1182 -0
- package/dist/compliance/rate-limiter.js +580 -0
- package/dist/compliance/right-to-erasure.js +117 -0
- package/dist/compliance/row-level-security.js +246 -0
- package/dist/compliance/security-decorator.js +574 -0
- package/dist/compliance/security-monitor.js +525 -0
- package/dist/compliance/sensitive-data-discovery.js +476 -0
- package/dist/compliance/session-isolation.js +472 -0
- package/dist/compliance/tls-enforcer.js +108 -0
- package/dist/compliance/worm-storage.js +712 -0
- package/dist/connection-manager.js +198 -0
- package/dist/connection-pool.js +519 -0
- package/dist/decorators/audit.js +136 -0
- package/dist/decorators/belongs-to-many.js +115 -0
- package/dist/decorators/belongs-to.js +115 -0
- package/dist/decorators/check.js +435 -0
- package/dist/decorators/collate.js +329 -0
- package/dist/decorators/comment.js +205 -0
- package/dist/decorators/database-settings.js +236 -0
- package/dist/decorators/default.js +244 -0
- package/dist/decorators/encryption.js +235 -0
- package/dist/decorators/engine.js +97 -0
- package/dist/decorators/fk-constraints.js +594 -0
- package/dist/decorators/foreign-table.js +136 -0
- package/dist/decorators/generated.js +274 -0
- package/dist/decorators/has-many.js +127 -0
- package/dist/decorators/has-one.js +116 -0
- package/dist/decorators/hstore.js +129 -0
- package/dist/decorators/index.js +355 -0
- package/dist/decorators/json-column.js +83 -0
- package/dist/decorators/jsonb.js +101 -0
- package/dist/decorators/orm-decorators.js +425 -0
- package/dist/decorators/permissions.js +294 -0
- package/dist/decorators/procedure.js +210 -0
- package/dist/decorators/query-options.js +556 -0
- package/dist/decorators/range.js +167 -0
- package/dist/decorators/set-column.js +83 -0
- package/dist/decorators/spatial.js +114 -0
- package/dist/decorators/storage.js +580 -0
- package/dist/decorators/timezone.js +512 -0
- package/dist/decorators/trigger.js +90 -0
- package/dist/decorators/uuid.js +135 -0
- package/dist/decorators/view.js +258 -0
- package/dist/diagrams/chen-diagram.js +354 -0
- package/dist/diagrams/class-diagram.js +384 -0
- package/dist/diagrams/dependency-diagram.js +432 -0
- package/dist/diagrams/er-diagram.js +605 -0
- package/dist/diagrams/flow-diagram.js +394 -0
- package/dist/diagrams/gantt-diagram.js +411 -0
- package/dist/diagrams/index-diagram.js +353 -0
- package/dist/diagrams/index.js +184 -0
- package/dist/diagrams/migration-diagram.js +316 -0
- package/dist/diagrams/model-diagram.js +616 -0
- package/dist/diagrams/package-diagram.js +376 -0
- package/dist/diagrams/palette.js +85 -0
- package/dist/diagrams/relational-diagram.js +455 -0
- package/dist/diagrams/schemadoc-diagram.js +309 -0
- package/dist/diagrams/sequence-diagram.js +307 -0
- package/dist/diagrams/state-diagram.js +344 -0
- package/dist/diagrams/svg-dom.js +111 -0
- package/dist/diagrams/tree-diagram.js +250 -0
- package/dist/dialects/clickhouse/index.js +1742 -0
- package/dist/dialects/cockroachdb/index.js +4677 -0
- package/dist/dialects/cratedb/index.js +441 -0
- package/dist/dialects/databricks/index.js +517 -0
- package/dist/dialects/db2/index.js +2225 -0
- package/dist/dialects/dialect.js +720 -0
- package/dist/dialects/duckdb/index.js +2014 -0
- package/dist/dialects/exasol/index.js +357 -0
- package/dist/dialects/firebird/index.js +495 -0
- package/dist/dialects/greenplum/index.js +457 -0
- package/dist/dialects/hana/index.js +1741 -0
- package/dist/dialects/mariadb/index.js +3590 -0
- package/dist/dialects/mssql/index.js +2296 -0
- package/dist/dialects/mysql/index.js +4058 -0
- package/dist/dialects/oracle/index.js +2999 -0
- package/dist/dialects/postgres/index.js +5319 -0
- package/dist/dialects/query-stream-helper.js +152 -0
- package/dist/dialects/questdb/index.js +433 -0
- package/dist/dialects/redshift/index.js +2277 -0
- package/dist/dialects/singlestore/index.js +354 -0
- package/dist/dialects/snowflake/index.js +2082 -0
- package/dist/dialects/spanner/index.js +1768 -0
- package/dist/dialects/sqlite/index.js +3382 -0
- package/dist/dialects/tidb/index.js +377 -0
- package/dist/dialects/timescaledb/index.js +164 -0
- package/dist/dialects/trino/index.js +420 -0
- package/dist/dialects/turso/index.js +227 -0
- package/dist/dialects/vertica/index.js +317 -0
- package/dist/dialects/yugabytedb/index.js +424 -0
- package/dist/errors/index.js +472 -0
- package/dist/errors/utils.js +340 -0
- package/dist/errors.js +21 -0
- package/dist/extensions/catalog/cloud-warehouse-features.js +207 -0
- package/dist/extensions/catalog/mssql-features.js +147 -0
- package/dist/extensions/catalog/mysql-mariadb-plugins.js +229 -0
- package/dist/extensions/catalog/oracle-db2-features.js +196 -0
- package/dist/extensions/catalog/postgres-extensions.js +516 -0
- package/dist/extensions/index.js +71 -0
- package/dist/extensions/types.js +13 -0
- package/dist/foreign-data.js +173 -0
- package/dist/hooks/hooks-manager.js +350 -0
- package/dist/hooks/index.js +37 -0
- package/dist/index.js +463 -0
- package/dist/logging.js +335 -0
- package/dist/migrations/index.js +40 -0
- package/dist/migrations/migration.js +196 -0
- package/dist/migrations/migrator.js +411 -0
- package/dist/migrations/prormmigration.js +275 -0
- package/dist/migrations/query-interface.js +435 -0
- package/dist/migrations/seeder.js +353 -0
- package/dist/models/associations.js +852 -0
- package/dist/models/constraints.js +288 -0
- package/dist/models/data-types.js +2518 -0
- package/dist/models/decorators.js +445 -0
- package/dist/models/index.js +33 -0
- package/dist/models/indexes.js +531 -0
- package/dist/models/methods.js +382 -0
- package/dist/models/model-manager.js +103 -0
- package/dist/models/model.js +5349 -0
- package/dist/models/operators.js +67 -0
- package/dist/models/scopes.js +189 -0
- package/dist/models/typescript-types.js +26 -0
- package/dist/nosql/aerospike/index.js +205 -0
- package/dist/nosql/allegrograph/index.js +169 -0
- package/dist/nosql/arangodb/index.js +364 -0
- package/dist/nosql/azure-blob/index.js +206 -0
- package/dist/nosql/beanstalkd/index.js +231 -0
- package/dist/nosql/beequeue/index.js +210 -0
- package/dist/nosql/bigchaindb/index.js +195 -0
- package/dist/nosql/bigtable/index.js +224 -0
- package/dist/nosql/blazegraph/index.js +173 -0
- package/dist/nosql/bullmq/index.js +191 -0
- package/dist/nosql/cassandra/index.js +174 -0
- package/dist/nosql/chroma/index.js +190 -0
- package/dist/nosql/cloudflare-kv/index.js +220 -0
- package/dist/nosql/coherence/index.js +200 -0
- package/dist/nosql/cosmosdb/index.js +157 -0
- package/dist/nosql/couchbase/index.js +213 -0
- package/dist/nosql/dax/index.js +212 -0
- package/dist/nosql/deno-kv/index.js +206 -0
- package/dist/nosql/dgraph/index.js +171 -0
- package/dist/nosql/doris/index.js +169 -0
- package/dist/nosql/druid/index.js +162 -0
- package/dist/nosql/dynamodb/index.js +937 -0
- package/dist/nosql/elasticsearch/index.js +377 -0
- package/dist/nosql/etcd/index.js +502 -0
- package/dist/nosql/eventhubs/index.js +213 -0
- package/dist/nosql/eventstore/index.js +254 -0
- package/dist/nosql/faunadb/index.js +188 -0
- package/dist/nosql/firestore/index.js +177 -0
- package/dist/nosql/fluree/index.js +148 -0
- package/dist/nosql/fuseki/index.js +170 -0
- package/dist/nosql/gcs/index.js +172 -0
- package/dist/nosql/gearman/index.js +160 -0
- package/dist/nosql/geode/index.js +196 -0
- package/dist/nosql/graphdb/index.js +169 -0
- package/dist/nosql/graylog/index.js +188 -0
- package/dist/nosql/gridgain/index.js +171 -0
- package/dist/nosql/hazelcast/index.js +162 -0
- package/dist/nosql/hbase/index.js +230 -0
- package/dist/nosql/ignite/index.js +173 -0
- package/dist/nosql/immudb/index.js +184 -0
- package/dist/nosql/index.js +232 -0
- package/dist/nosql/infinispan/index.js +200 -0
- package/dist/nosql/influxdb/index.js +0 -0
- package/dist/nosql/kafka/index.js +234 -0
- package/dist/nosql/keyspaces/index.js +189 -0
- package/dist/nosql/kinesis/index.js +253 -0
- package/dist/nosql/leveldb/index.js +153 -0
- package/dist/nosql/lmdb/index.js +160 -0
- package/dist/nosql/loki/index.js +201 -0
- package/dist/nosql/marklogic/index.js +204 -0
- package/dist/nosql/materialize/index.js +145 -0
- package/dist/nosql/meilisearch/index.js +154 -0
- package/dist/nosql/memcached/index.js +223 -0
- package/dist/nosql/milvus/index.js +410 -0
- package/dist/nosql/minio/index.js +265 -0
- package/dist/nosql/momento/index.js +179 -0
- package/dist/nosql/mongodb/index.js +461 -0
- package/dist/nosql/nats/index.js +247 -0
- package/dist/nosql/nedb/index.js +164 -0
- package/dist/nosql/neo4j/index.js +450 -0
- package/dist/nosql/neptune/index.js +470 -0
- package/dist/nosql/nsq/index.js +200 -0
- package/dist/nosql/opensearch/index.js +186 -0
- package/dist/nosql/orientdb/index.js +175 -0
- package/dist/nosql/papertrail/index.js +200 -0
- package/dist/nosql/pinecone/index.js +0 -0
- package/dist/nosql/pinot/index.js +133 -0
- package/dist/nosql/pouchdb/index.js +172 -0
- package/dist/nosql/prometheus/index.js +174 -0
- package/dist/nosql/provendb/index.js +147 -0
- package/dist/nosql/pubsub/index.js +187 -0
- package/dist/nosql/pulsar/index.js +232 -0
- package/dist/nosql/qdrant/index.js +295 -0
- package/dist/nosql/qldb/index.js +192 -0
- package/dist/nosql/r2/index.js +281 -0
- package/dist/nosql/rabbitmq/index.js +237 -0
- package/dist/nosql/ravendb/index.js +175 -0
- package/dist/nosql/redis/index.js +607 -0
- package/dist/nosql/redpanda/index.js +237 -0
- package/dist/nosql/resque/index.js +203 -0
- package/dist/nosql/rethinkdb/index.js +232 -0
- package/dist/nosql/rocksdb/index.js +152 -0
- package/dist/nosql/rockset/index.js +126 -0
- package/dist/nosql/s3/index.js +298 -0
- package/dist/nosql/scylladb/index.js +178 -0
- package/dist/nosql/signoz/index.js +227 -0
- package/dist/nosql/sns/index.js +201 -0
- package/dist/nosql/solr/index.js +231 -0
- package/dist/nosql/splunk/index.js +227 -0
- package/dist/nosql/sqs/index.js +246 -0
- package/dist/nosql/stardog/index.js +169 -0
- package/dist/nosql/starrocks/index.js +170 -0
- package/dist/nosql/store.js +2 -0
- package/dist/nosql/sumologic/index.js +213 -0
- package/dist/nosql/surrealdb/index.js +179 -0
- package/dist/nosql/terminusdb/index.js +151 -0
- package/dist/nosql/tigergraph/index.js +469 -0
- package/dist/nosql/typesense/index.js +148 -0
- package/dist/nosql/unqlite/index.js +157 -0
- package/dist/nosql/upstash/index.js +167 -0
- package/dist/nosql/vercel-kv/index.js +166 -0
- package/dist/nosql/victoriametrics/index.js +213 -0
- package/dist/nosql/virtuoso/index.js +169 -0
- package/dist/nosql/weaviate/index.js +276 -0
- package/dist/operators/index.js +108 -0
- package/dist/operators.js +2690 -0
- package/dist/prisma-migrate/index.js +38 -0
- package/dist/prisma-migrate/migration-generator.js +125 -0
- package/dist/prisma-migrate/model-generator.js +172 -0
- package/dist/prisma-migrate/relations.js +100 -0
- package/dist/prisma-migrate/schema-parser.js +167 -0
- package/dist/prisma-migrate/type-mapper.js +40 -0
- package/dist/prorm.js +6832 -0
- package/dist/query-builders/cte-builder.js +80 -0
- package/dist/query-builders/functions/aggregate.js +390 -0
- package/dist/query-builders/functions/conditional.js +503 -0
- package/dist/query-builders/functions/datetime.js +695 -0
- package/dist/query-builders/functions/fulltext.js +439 -0
- package/dist/query-builders/functions/index.js +93 -0
- package/dist/query-builders/functions/json.js +427 -0
- package/dist/query-builders/functions/math.js +399 -0
- package/dist/query-builders/functions/string.js +518 -0
- package/dist/query-builders/functions/window.js +328 -0
- package/dist/query-builders/include-builder.js +323 -0
- package/dist/query-builders/index-expression-builder.js +242 -0
- package/dist/query-builders/index.js +161 -0
- package/dist/query-builders/insert-builder.js +164 -0
- package/dist/query-builders/model-helpers.js +38 -0
- package/dist/query-builders/order-limit-builder.js +239 -0
- package/dist/query-builders/sql-compiler.js +2040 -0
- package/dist/query-builders/subquery-builder.js +152 -0
- package/dist/query-builders/update-builder.js +182 -0
- package/dist/query-builders/view-builder.js +218 -0
- package/dist/query-builders/where-builder.js +1532 -0
- package/dist/query-interface.js +622 -0
- package/dist/query-optimizers/batch-optimizer.js +426 -0
- package/dist/query-optimizers/explain-plans.js +415 -0
- package/dist/query-optimizers/index.js +51 -0
- package/dist/query-optimizers/prepared-statement-cache.js +423 -0
- package/dist/query-optimizers/query-hints.js +438 -0
- package/dist/query-optimizers/query-optimizer.js +278 -0
- package/dist/query-optimizers/slow-query-logger.js +271 -0
- package/dist/replica-manager.js +507 -0
- package/dist/schema/index.js +15 -0
- package/dist/schema/migration-generator.js +374 -0
- package/dist/schema/schema-differ.js +549 -0
- package/dist/schema/types.js +6 -0
- package/dist/sql-constants.js +300 -0
- package/dist/sqlite-advanced.js +1047 -0
- package/dist/streams/index.js +12 -0
- package/dist/streams/transforms.js +178 -0
- package/dist/transaction.js +456 -0
- package/dist/types/index.js +180 -0
- package/dist/user-management.js +127 -0
- package/dist/utils/date.js +300 -0
- package/dist/utils/index.js +1079 -0
- package/dist/utils/string.js +161 -0
- package/dist/validators/index.js +19 -0
- package/dist/validators/validator.js +911 -0
- package/package.json +190 -0
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* PrivacyImpactAssessment
|
|
4
|
+
*
|
|
5
|
+
* A ready-made model and service for conducting Privacy Impact Assessments (PIA)
|
|
6
|
+
* and Data Protection Impact Assessments (DPIA) for high-risk data processing.
|
|
7
|
+
*
|
|
8
|
+
* Covers compliance frameworks including:
|
|
9
|
+
* GDPR Art. 35 (DPIA), ISO 27701, UK GDPR, CCPA, PIPEDA,
|
|
10
|
+
* LGPD (Brazil), APPI (Japan), PDPA (Thailand, Singapore), and more.
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* // Initialize once (creates the pia_assessments table):
|
|
14
|
+
* await PrivacyImpactAssessment.initialize(prorm);
|
|
15
|
+
*
|
|
16
|
+
* // Create a new PIA:
|
|
17
|
+
* const assessment = await PrivacyImpactAssessment.createAssessment(
|
|
18
|
+
* 'User Analytics Processing',
|
|
19
|
+
* ['email', 'ip_address', 'behavioral_data'],
|
|
20
|
+
* 'analyze_user_behavior_for_product_improvement'
|
|
21
|
+
* );
|
|
22
|
+
*
|
|
23
|
+
* // Record identified risks:
|
|
24
|
+
* await PrivacyImpactAssessment.recordRisk(
|
|
25
|
+
* assessment.id,
|
|
26
|
+
* 'Potential data breach exposing user profiles',
|
|
27
|
+
* 'high',
|
|
28
|
+
* * 'Implement AES-256 encryption at rest and TLS 1.3 in transit'
|
|
29
|
+
* );
|
|
30
|
+
*
|
|
31
|
+
* // Approve the assessment:
|
|
32
|
+
* await PrivacyImpactAssessment.approveAssessment(assessment.id, 'DPO John Smith');
|
|
33
|
+
*
|
|
34
|
+
* // Link to a model:
|
|
35
|
+
* await PrivacyImpactAssessment.linkToModel(assessment.id, UserAnalytics);
|
|
36
|
+
*
|
|
37
|
+
* // Get assessment details:
|
|
38
|
+
* const details = await PrivacyImpactAssessment.getAssessment(assessment.id);
|
|
39
|
+
*/
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.PrivacyImpactAssessment = void 0;
|
|
42
|
+
const data_types_1 = require("../models/data-types");
|
|
43
|
+
const operators_1 = require("../models/operators");
|
|
44
|
+
let PIAModel = null;
|
|
45
|
+
let RiskModel = null;
|
|
46
|
+
class PrivacyImpactAssessment {
|
|
47
|
+
/**
|
|
48
|
+
* Initialize the PrivacyImpactAssessment model and sync the tables.
|
|
49
|
+
* Call once at application startup.
|
|
50
|
+
*
|
|
51
|
+
* @param prorm - The Prorm instance
|
|
52
|
+
* @param tableName - Override the default table name 'pia_assessments'
|
|
53
|
+
*/
|
|
54
|
+
static async initialize(prorm, tableName = 'pia_assessments') {
|
|
55
|
+
PIAModel = prorm.define('PIAContainer', {
|
|
56
|
+
id: {
|
|
57
|
+
type: data_types_1.DataTypes.INTEGER(),
|
|
58
|
+
primaryKey: true,
|
|
59
|
+
autoIncrement: true,
|
|
60
|
+
},
|
|
61
|
+
name: {
|
|
62
|
+
type: data_types_1.DataTypes.STRING(255),
|
|
63
|
+
allowNull: false,
|
|
64
|
+
comment: 'Name/title of the privacy impact assessment',
|
|
65
|
+
},
|
|
66
|
+
dataTypes: {
|
|
67
|
+
type: data_types_1.DataTypes.JSON(),
|
|
68
|
+
allowNull: false,
|
|
69
|
+
comment: 'List of data types being processed',
|
|
70
|
+
},
|
|
71
|
+
processingPurpose: {
|
|
72
|
+
type: data_types_1.DataTypes.TEXT(),
|
|
73
|
+
allowNull: false,
|
|
74
|
+
comment: 'Purpose of the data processing',
|
|
75
|
+
},
|
|
76
|
+
status: {
|
|
77
|
+
type: data_types_1.DataTypes.ENUM('draft', 'in_review', 'approved', 'rejected', 'archived'),
|
|
78
|
+
allowNull: false,
|
|
79
|
+
defaultValue: 'draft',
|
|
80
|
+
comment: 'Current status of the assessment',
|
|
81
|
+
},
|
|
82
|
+
approvedBy: {
|
|
83
|
+
type: data_types_1.DataTypes.STRING(255),
|
|
84
|
+
allowNull: true,
|
|
85
|
+
comment: 'Name/ID of the approver (DPO, compliance officer)',
|
|
86
|
+
},
|
|
87
|
+
approvedAt: {
|
|
88
|
+
type: data_types_1.DataTypes.DATE(),
|
|
89
|
+
allowNull: true,
|
|
90
|
+
comment: 'Timestamp when the assessment was approved',
|
|
91
|
+
},
|
|
92
|
+
linkedModels: {
|
|
93
|
+
type: data_types_1.DataTypes.JSON(),
|
|
94
|
+
allowNull: true,
|
|
95
|
+
comment: 'List of model names linked to this assessment',
|
|
96
|
+
},
|
|
97
|
+
metadata: {
|
|
98
|
+
type: data_types_1.DataTypes.JSON(),
|
|
99
|
+
allowNull: true,
|
|
100
|
+
comment: 'Additional structured metadata',
|
|
101
|
+
},
|
|
102
|
+
}, {
|
|
103
|
+
tableName,
|
|
104
|
+
timestamps: true,
|
|
105
|
+
underscored: true,
|
|
106
|
+
indexes: [
|
|
107
|
+
{ fields: ['status'] },
|
|
108
|
+
{ fields: ['name'] },
|
|
109
|
+
],
|
|
110
|
+
});
|
|
111
|
+
RiskModel = prorm.define('PIARisk', {
|
|
112
|
+
id: {
|
|
113
|
+
type: data_types_1.DataTypes.INTEGER(),
|
|
114
|
+
primaryKey: true,
|
|
115
|
+
autoIncrement: true,
|
|
116
|
+
},
|
|
117
|
+
assessmentId: {
|
|
118
|
+
type: data_types_1.DataTypes.INTEGER(),
|
|
119
|
+
allowNull: false,
|
|
120
|
+
references: {
|
|
121
|
+
model: tableName,
|
|
122
|
+
key: 'id',
|
|
123
|
+
},
|
|
124
|
+
comment: 'Foreign key to the parent PIA assessment',
|
|
125
|
+
},
|
|
126
|
+
risk: {
|
|
127
|
+
type: data_types_1.DataTypes.TEXT(),
|
|
128
|
+
allowNull: false,
|
|
129
|
+
comment: 'Description of the identified risk',
|
|
130
|
+
},
|
|
131
|
+
severity: {
|
|
132
|
+
type: data_types_1.DataTypes.ENUM('low', 'medium', 'high', 'critical'),
|
|
133
|
+
allowNull: false,
|
|
134
|
+
comment: 'Severity level of the risk',
|
|
135
|
+
},
|
|
136
|
+
mitigation: {
|
|
137
|
+
type: data_types_1.DataTypes.TEXT(),
|
|
138
|
+
allowNull: true,
|
|
139
|
+
comment: 'Mitigation measures for the risk',
|
|
140
|
+
},
|
|
141
|
+
}, {
|
|
142
|
+
tableName: `${tableName}_risks`,
|
|
143
|
+
timestamps: true,
|
|
144
|
+
underscored: true,
|
|
145
|
+
indexes: [
|
|
146
|
+
{ fields: ['assessment_id'] },
|
|
147
|
+
{ fields: ['severity'] },
|
|
148
|
+
],
|
|
149
|
+
});
|
|
150
|
+
await PIAModel.sync({ alter: false });
|
|
151
|
+
await RiskModel.sync({ alter: false });
|
|
152
|
+
}
|
|
153
|
+
static requireModels() {
|
|
154
|
+
if (!PIAModel || !RiskModel) {
|
|
155
|
+
throw new Error('[PrivacyImpactAssessment] Not initialized. Call PrivacyImpactAssessment.initialize(prorm) first.');
|
|
156
|
+
}
|
|
157
|
+
return { pia: PIAModel, risk: RiskModel };
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Create a new Privacy Impact Assessment.
|
|
161
|
+
*
|
|
162
|
+
* @param name - Name/title of the assessment
|
|
163
|
+
* @param dataTypes - List of data types being processed
|
|
164
|
+
* @param processingPurpose - Purpose of the processing operation
|
|
165
|
+
* @returns The created PIA record
|
|
166
|
+
*/
|
|
167
|
+
static async createAssessment(name, dataTypes, processingPurpose) {
|
|
168
|
+
const { pia } = PrivacyImpactAssessment.requireModels();
|
|
169
|
+
return pia.create({
|
|
170
|
+
name,
|
|
171
|
+
dataTypes,
|
|
172
|
+
processingPurpose,
|
|
173
|
+
status: 'draft',
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Record a risk associated with a PIA assessment.
|
|
178
|
+
*
|
|
179
|
+
* @param assessmentId - The ID of the assessment to add the risk to
|
|
180
|
+
* @param risk - Description of the identified risk
|
|
181
|
+
* @param severity - Severity level of the risk
|
|
182
|
+
* @param mitigation - Mitigation measures for the risk
|
|
183
|
+
* @returns The created risk record
|
|
184
|
+
*/
|
|
185
|
+
static async recordRisk(assessmentId, risk, severity, mitigation) {
|
|
186
|
+
const { risk: RiskModelClass } = PrivacyImpactAssessment.requireModels();
|
|
187
|
+
return RiskModelClass.create({
|
|
188
|
+
assessmentId,
|
|
189
|
+
risk,
|
|
190
|
+
severity,
|
|
191
|
+
mitigation,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Approve a Privacy Impact Assessment.
|
|
196
|
+
* Sets status to 'approved' and records approver information.
|
|
197
|
+
*
|
|
198
|
+
* @param assessmentId - The ID of the assessment to approve
|
|
199
|
+
* @param approver - Name/ID of the approver (e.g., DPO name)
|
|
200
|
+
* @returns The updated PIA record
|
|
201
|
+
*/
|
|
202
|
+
static async approveAssessment(assessmentId, approver) {
|
|
203
|
+
const { pia } = PrivacyImpactAssessment.requireModels();
|
|
204
|
+
const assessment = await pia.findByPk(assessmentId);
|
|
205
|
+
if (!assessment) {
|
|
206
|
+
throw new Error(`[PrivacyImpactAssessment] Assessment not found: ${assessmentId}`);
|
|
207
|
+
}
|
|
208
|
+
await assessment.update({
|
|
209
|
+
status: 'approved',
|
|
210
|
+
approvedBy: approver,
|
|
211
|
+
approvedAt: new Date(),
|
|
212
|
+
});
|
|
213
|
+
return assessment;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Get a Privacy Impact Assessment by ID.
|
|
217
|
+
* Includes associated risks.
|
|
218
|
+
*
|
|
219
|
+
* @param assessmentId - The ID of the assessment to retrieve
|
|
220
|
+
* @returns The PIA record with associated risks
|
|
221
|
+
*/
|
|
222
|
+
static async getAssessment(assessmentId) {
|
|
223
|
+
const { pia, risk } = PrivacyImpactAssessment.requireModels();
|
|
224
|
+
return pia.findByPk(assessmentId, {
|
|
225
|
+
include: [
|
|
226
|
+
{
|
|
227
|
+
model: risk,
|
|
228
|
+
as: 'risks',
|
|
229
|
+
},
|
|
230
|
+
],
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Link a model to a PIA assessment.
|
|
235
|
+
* Useful for tracking which models are covered by the assessment.
|
|
236
|
+
*
|
|
237
|
+
* @param assessmentId - The ID of the assessment
|
|
238
|
+
* @param model - The model class to link (can be a Prorm model or string name)
|
|
239
|
+
* @returns The updated PIA record
|
|
240
|
+
*/
|
|
241
|
+
static async linkToModel(assessmentId, model) {
|
|
242
|
+
const { pia } = PrivacyImpactAssessment.requireModels();
|
|
243
|
+
const assessment = await pia.findByPk(assessmentId);
|
|
244
|
+
if (!assessment) {
|
|
245
|
+
throw new Error(`[PrivacyImpactAssessment] Assessment not found: ${assessmentId}`);
|
|
246
|
+
}
|
|
247
|
+
const modelName = typeof model === 'string' ? model : (model.tableName ?? model.name ?? 'Unknown');
|
|
248
|
+
const currentLinkedModels = assessment.linkedModels ?? [];
|
|
249
|
+
if (!currentLinkedModels.includes(modelName)) {
|
|
250
|
+
await assessment.update({
|
|
251
|
+
linkedModels: [...currentLinkedModels, modelName],
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
return assessment;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Get all assessments with a specific status.
|
|
258
|
+
*
|
|
259
|
+
* @param status - The status to filter by
|
|
260
|
+
* @returns Array of PIA records matching the status
|
|
261
|
+
*/
|
|
262
|
+
static async getByStatus(status) {
|
|
263
|
+
const { pia } = PrivacyImpactAssessment.requireModels();
|
|
264
|
+
return pia.findAll({
|
|
265
|
+
where: { status },
|
|
266
|
+
order: [['createdAt', 'DESC']],
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Reject a Privacy Impact Assessment.
|
|
271
|
+
*
|
|
272
|
+
* @param assessmentId - The ID of the assessment to reject
|
|
273
|
+
* @param reason - Reason for rejection
|
|
274
|
+
* @returns The updated PIA record
|
|
275
|
+
*/
|
|
276
|
+
static async rejectAssessment(assessmentId, reason) {
|
|
277
|
+
const { pia } = PrivacyImpactAssessment.requireModels();
|
|
278
|
+
const assessment = await pia.findByPk(assessmentId);
|
|
279
|
+
if (!assessment) {
|
|
280
|
+
throw new Error(`[PrivacyImpactAssessment] Assessment not found: ${assessmentId}`);
|
|
281
|
+
}
|
|
282
|
+
await assessment.update({
|
|
283
|
+
status: 'rejected',
|
|
284
|
+
metadata: {
|
|
285
|
+
...(assessment.metadata ?? {}),
|
|
286
|
+
rejectionReason: reason,
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
return assessment;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Submit an assessment for review.
|
|
293
|
+
* Changes status from 'draft' to 'in_review'.
|
|
294
|
+
*
|
|
295
|
+
* @param assessmentId - The ID of the assessment to submit
|
|
296
|
+
* @returns The updated PIA record
|
|
297
|
+
*/
|
|
298
|
+
static async submitForReview(assessmentId) {
|
|
299
|
+
const { pia } = PrivacyImpactAssessment.requireModels();
|
|
300
|
+
const assessment = await pia.findByPk(assessmentId);
|
|
301
|
+
if (!assessment) {
|
|
302
|
+
throw new Error(`[PrivacyImpactAssessment] Assessment not found: ${assessmentId}`);
|
|
303
|
+
}
|
|
304
|
+
if (assessment.status !== 'draft') {
|
|
305
|
+
throw new Error(`[PrivacyImpactAssessment] Only draft assessments can be submitted for review. Current status: ${assessment.status}`);
|
|
306
|
+
}
|
|
307
|
+
await assessment.update({
|
|
308
|
+
status: 'in_review',
|
|
309
|
+
});
|
|
310
|
+
return assessment;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Get all risks for an assessment grouped by severity.
|
|
314
|
+
*
|
|
315
|
+
* @param assessmentId - The ID of the assessment
|
|
316
|
+
* @returns Object with risks grouped by severity level
|
|
317
|
+
*/
|
|
318
|
+
static async getRisksBySeverity(assessmentId) {
|
|
319
|
+
const { risk } = PrivacyImpactAssessment.requireModels();
|
|
320
|
+
const risks = await risk.findAll({
|
|
321
|
+
where: { assessmentId },
|
|
322
|
+
order: [['severity', 'DESC']],
|
|
323
|
+
});
|
|
324
|
+
return {
|
|
325
|
+
critical: risks.filter((r) => r.severity === 'critical'),
|
|
326
|
+
high: risks.filter((r) => r.severity === 'high'),
|
|
327
|
+
medium: risks.filter((r) => r.severity === 'medium'),
|
|
328
|
+
low: risks.filter((r) => r.severity === 'low'),
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Check if an assessment has any high or critical risks without mitigations.
|
|
333
|
+
*
|
|
334
|
+
* @param assessmentId - The ID of the assessment
|
|
335
|
+
* @returns True if there are unresolved high-risk items
|
|
336
|
+
*/
|
|
337
|
+
static async hasUnresolvedHighRisks(assessmentId) {
|
|
338
|
+
const { risk } = PrivacyImpactAssessment.requireModels();
|
|
339
|
+
const count = await risk.count({
|
|
340
|
+
where: {
|
|
341
|
+
[operators_1.Op.and]: [
|
|
342
|
+
{ assessmentId, severity: { [operators_1.Op.in]: ['high', 'critical'] } },
|
|
343
|
+
{ [operators_1.Op.or]: [{ mitigation: { [operators_1.Op.isNull]: true } }, { mitigation: '' }] },
|
|
344
|
+
],
|
|
345
|
+
},
|
|
346
|
+
});
|
|
347
|
+
return count > 0;
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* List all models linked to a PIA assessment.
|
|
351
|
+
*
|
|
352
|
+
* @param assessmentId - The ID of the assessment
|
|
353
|
+
* @returns Array of linked model names
|
|
354
|
+
*/
|
|
355
|
+
static async getLinkedModels(assessmentId) {
|
|
356
|
+
const { pia } = PrivacyImpactAssessment.requireModels();
|
|
357
|
+
const assessment = await pia.findByPk(assessmentId);
|
|
358
|
+
if (!assessment) {
|
|
359
|
+
throw new Error(`[PrivacyImpactAssessment] Assessment not found: ${assessmentId}`);
|
|
360
|
+
}
|
|
361
|
+
return assessment.linkedModels ?? [];
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
exports.PrivacyImpactAssessment = PrivacyImpactAssessment;
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* PseudonymizationService
|
|
4
|
+
*
|
|
5
|
+
* Replaces identifiers with non-reversible tokens for analytics, logging,
|
|
6
|
+
* and cross-system sharing while preserving referential integrity.
|
|
7
|
+
* Supports deterministic tokenization (same input → same token) using HMAC-SHA256.
|
|
8
|
+
*
|
|
9
|
+
* Covers ~34 compliance frameworks including:
|
|
10
|
+
* GDPR Art 25 (Privacy by Design), ISO 27701, ISO 29151, HIPAA Safe Harbor,
|
|
11
|
+
* PIPL (China), KVKK (Turkey), NIST SP 800-122, and more.
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* // Configure once at startup:
|
|
15
|
+
* await PseudonymizationService.configure({
|
|
16
|
+
* secretKey: process.env.PSEUDO_SECRET_KEY,
|
|
17
|
+
* store: 'database', // or 'memory'
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* // Tokenize a value:
|
|
21
|
+
* const token = await PseudonymizationService.tokenize('user-123');
|
|
22
|
+
* // token: 'a1b2c3d4e5f6...' (deterministic, same input = same output)
|
|
23
|
+
*
|
|
24
|
+
* // Detokenize (reverse):
|
|
25
|
+
* const original = await PseudonymizationService.detokenize(token);
|
|
26
|
+
* // original: 'user-123'
|
|
27
|
+
*
|
|
28
|
+
* // Or use the decorator on a model field:
|
|
29
|
+
* @Pseudonymize('nationalId')
|
|
30
|
+
* class Patient extends Model { ... }
|
|
31
|
+
*
|
|
32
|
+
* // Apply hooks to auto-tokenize on write:
|
|
33
|
+
* PseudonymizationService.applyHooks(Patient, 'nationalId');
|
|
34
|
+
*/
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.PseudonymizationService = void 0;
|
|
37
|
+
exports.configure = configure;
|
|
38
|
+
exports.tokenize = tokenize;
|
|
39
|
+
exports.detokenize = detokenize;
|
|
40
|
+
exports.exists = exists;
|
|
41
|
+
exports.removeToken = removeToken;
|
|
42
|
+
exports.removeOriginal = removeOriginal;
|
|
43
|
+
exports.getPseudonymizedFields = getPseudonymizedFields;
|
|
44
|
+
exports.Pseudonymize = Pseudonymize;
|
|
45
|
+
exports.applyHooks = applyHooks;
|
|
46
|
+
const crypto_1 = require("crypto");
|
|
47
|
+
// In-memory storage for tokens
|
|
48
|
+
const tokenStore = new Map();
|
|
49
|
+
const reverseStore = new Map();
|
|
50
|
+
let config = null;
|
|
51
|
+
let tokenModel = null;
|
|
52
|
+
/**
|
|
53
|
+
* Configure the pseudonymization service.
|
|
54
|
+
*
|
|
55
|
+
* @param cfg - Configuration options
|
|
56
|
+
*/
|
|
57
|
+
async function configure(cfg) {
|
|
58
|
+
if (!cfg.secretKey || cfg.secretKey.length < 32) {
|
|
59
|
+
throw new Error('[PseudonymizationService] secretKey must be at least 32 characters. ' +
|
|
60
|
+
'Use a strong, randomly generated key and store it securely.');
|
|
61
|
+
}
|
|
62
|
+
config = {
|
|
63
|
+
store: cfg.store ?? 'memory',
|
|
64
|
+
algorithm: cfg.algorithm ?? 'sha256',
|
|
65
|
+
...cfg,
|
|
66
|
+
};
|
|
67
|
+
if (cfg.store === 'database' && cfg.model) {
|
|
68
|
+
tokenModel = cfg.model;
|
|
69
|
+
await tokenModel.sync?.({ alter: true });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function getConfig() {
|
|
73
|
+
if (!config) {
|
|
74
|
+
throw new Error('[PseudonymizationService] Not configured. Call await PseudonymizationService.configure({ secretKey }) first.');
|
|
75
|
+
}
|
|
76
|
+
return config;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Generate a deterministic HMAC token from a value.
|
|
80
|
+
*/
|
|
81
|
+
function generateToken(value) {
|
|
82
|
+
const cfg = getConfig();
|
|
83
|
+
const hmac = (0, crypto_1.createHmac)(cfg.algorithm, cfg.secretKey);
|
|
84
|
+
hmac.update(value);
|
|
85
|
+
return hmac.digest('hex');
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Tokenize a value (replace with a pseudonym/token).
|
|
89
|
+
* Returns the same token for the same input (deterministic).
|
|
90
|
+
*/
|
|
91
|
+
async function tokenize(value) {
|
|
92
|
+
if (!value)
|
|
93
|
+
return '';
|
|
94
|
+
const cfg = getConfig();
|
|
95
|
+
// Check if we already have a token for this value
|
|
96
|
+
if (cfg.store === 'memory') {
|
|
97
|
+
const existing = reverseStore.get(value);
|
|
98
|
+
if (existing)
|
|
99
|
+
return existing;
|
|
100
|
+
}
|
|
101
|
+
else if (cfg.store === 'database' && tokenModel) {
|
|
102
|
+
const record = await tokenModel.findOne({ where: { original: value } });
|
|
103
|
+
if (record)
|
|
104
|
+
return record.token;
|
|
105
|
+
}
|
|
106
|
+
// Generate new token
|
|
107
|
+
const token = generateToken(value);
|
|
108
|
+
// Store the mapping
|
|
109
|
+
if (cfg.store === 'memory') {
|
|
110
|
+
tokenStore.set(token, { token, original: value, createdAt: new Date() });
|
|
111
|
+
reverseStore.set(value, token);
|
|
112
|
+
}
|
|
113
|
+
else if (cfg.store === 'database' && tokenModel) {
|
|
114
|
+
await tokenModel.findOrCreate({
|
|
115
|
+
where: { token },
|
|
116
|
+
defaults: { token, original: value, createdAt: new Date() },
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
return token;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Detokenize (reverse) a token to get the original value.
|
|
123
|
+
*/
|
|
124
|
+
async function detokenize(token) {
|
|
125
|
+
if (!token)
|
|
126
|
+
return null;
|
|
127
|
+
const cfg = getConfig();
|
|
128
|
+
if (cfg.store === 'memory') {
|
|
129
|
+
const record = tokenStore.get(token);
|
|
130
|
+
return record?.original ?? null;
|
|
131
|
+
}
|
|
132
|
+
else if (cfg.store === 'database' && tokenModel) {
|
|
133
|
+
const record = await tokenModel.findOne({ where: { token } });
|
|
134
|
+
return record?.original ?? null;
|
|
135
|
+
}
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Check if a token exists.
|
|
140
|
+
*/
|
|
141
|
+
async function exists(token) {
|
|
142
|
+
if (!token)
|
|
143
|
+
return false;
|
|
144
|
+
const cfg = getConfig();
|
|
145
|
+
if (cfg.store === 'memory') {
|
|
146
|
+
return tokenStore.has(token);
|
|
147
|
+
}
|
|
148
|
+
else if (cfg.store === 'database' && tokenModel) {
|
|
149
|
+
const count = await tokenModel.count({ where: { token } });
|
|
150
|
+
return count > 0;
|
|
151
|
+
}
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Delete a token (irreversible — useful for right to erasure).
|
|
156
|
+
*/
|
|
157
|
+
async function removeToken(token) {
|
|
158
|
+
if (!token)
|
|
159
|
+
return false;
|
|
160
|
+
const cfg = getConfig();
|
|
161
|
+
if (cfg.store === 'memory') {
|
|
162
|
+
const record = tokenStore.get(token);
|
|
163
|
+
if (record) {
|
|
164
|
+
reverseStore.delete(record.original);
|
|
165
|
+
tokenStore.delete(token);
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
else if (cfg.store === 'database' && tokenModel) {
|
|
170
|
+
const record = await tokenModel.findOne({ where: { token } });
|
|
171
|
+
if (record) {
|
|
172
|
+
await record.destroy();
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Delete tokens for a specific original value.
|
|
180
|
+
*/
|
|
181
|
+
async function removeOriginal(original) {
|
|
182
|
+
if (!original)
|
|
183
|
+
return false;
|
|
184
|
+
const cfg = getConfig();
|
|
185
|
+
if (cfg.store === 'memory') {
|
|
186
|
+
const token = reverseStore.get(original);
|
|
187
|
+
if (token) {
|
|
188
|
+
tokenStore.delete(token);
|
|
189
|
+
reverseStore.delete(original);
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
else if (cfg.store === 'database' && tokenModel) {
|
|
194
|
+
const record = await tokenModel.findOne({ where: { original } });
|
|
195
|
+
if (record) {
|
|
196
|
+
await record.destroy();
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
// ==================== Decorator ====================
|
|
203
|
+
const PSEUDO_FIELDS_KEY = '__orm_pseudo_fields__';
|
|
204
|
+
function getPseudonymizedFields(target) {
|
|
205
|
+
return target[PSEUDO_FIELDS_KEY] ?? [];
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Mark a field for automatic pseudonymization.
|
|
209
|
+
*
|
|
210
|
+
* @Pseudonymize('nationalId')
|
|
211
|
+
* class Patient extends Model { ... }
|
|
212
|
+
*/
|
|
213
|
+
function Pseudonymize(field) {
|
|
214
|
+
return function (target) {
|
|
215
|
+
if (!target[PSEUDO_FIELDS_KEY]) {
|
|
216
|
+
target[PSEUDO_FIELDS_KEY] = [];
|
|
217
|
+
}
|
|
218
|
+
target[PSEUDO_FIELDS_KEY].push(field);
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
// ==================== Hook Wiring ====================
|
|
222
|
+
/**
|
|
223
|
+
* Wire hooks onto a model to auto-tokenize the specified field on write
|
|
224
|
+
* and auto-detokenize on read.
|
|
225
|
+
*
|
|
226
|
+
* @param Model - The model class
|
|
227
|
+
* @param field - The field to pseudonymize
|
|
228
|
+
*/
|
|
229
|
+
function applyHooks(Model, field) {
|
|
230
|
+
const hookName = 'orm:pseudonymize';
|
|
231
|
+
if (typeof Model.addHook === 'function') {
|
|
232
|
+
// Tokenize before create/update
|
|
233
|
+
Model.addHook('beforeCreate', hookName, async (instance) => {
|
|
234
|
+
const value = instance.getDataValue ? instance.getDataValue(field) : instance[field];
|
|
235
|
+
if (value && typeof value === 'string' && !value.startsWith('pseudo:')) {
|
|
236
|
+
const token = await tokenize(value);
|
|
237
|
+
if (instance.setDataValue) {
|
|
238
|
+
instance.setDataValue(field, token);
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
instance[field] = token;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
Model.addHook('beforeUpdate', hookName, async (instance) => {
|
|
246
|
+
const value = instance.getDataValue ? instance.getDataValue(field) : instance[field];
|
|
247
|
+
if (instance.changed(field) && value && typeof value === 'string' && !value.startsWith('pseudo:')) {
|
|
248
|
+
const token = await tokenize(value);
|
|
249
|
+
if (instance.setDataValue) {
|
|
250
|
+
instance.setDataValue(field, token);
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
instance[field] = token;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
// ==================== Class Export ====================
|
|
260
|
+
class PseudonymizationService {
|
|
261
|
+
}
|
|
262
|
+
exports.PseudonymizationService = PseudonymizationService;
|
|
263
|
+
PseudonymizationService.configure = configure;
|
|
264
|
+
PseudonymizationService.tokenize = tokenize;
|
|
265
|
+
PseudonymizationService.detokenize = detokenize;
|
|
266
|
+
PseudonymizationService.exists = exists;
|
|
267
|
+
PseudonymizationService.removeToken = removeToken;
|
|
268
|
+
PseudonymizationService.removeOriginal = removeOriginal;
|
|
269
|
+
PseudonymizationService.applyHooks = applyHooks;
|