@etcsec-com/etc-collector 1.4.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/.env.example +60 -0
- package/.env.test.example +33 -0
- package/.github/workflows/ci.yml +83 -0
- package/.github/workflows/release.yml +246 -0
- package/.prettierrc.json +10 -0
- package/CHANGELOG.md +15 -0
- package/Dockerfile +57 -0
- package/LICENSE +190 -0
- package/README.md +194 -0
- package/dist/api/controllers/audit.controller.d.ts +21 -0
- package/dist/api/controllers/audit.controller.d.ts.map +1 -0
- package/dist/api/controllers/audit.controller.js +179 -0
- package/dist/api/controllers/audit.controller.js.map +1 -0
- package/dist/api/controllers/auth.controller.d.ts +16 -0
- package/dist/api/controllers/auth.controller.d.ts.map +1 -0
- package/dist/api/controllers/auth.controller.js +146 -0
- package/dist/api/controllers/auth.controller.js.map +1 -0
- package/dist/api/controllers/export.controller.d.ts +27 -0
- package/dist/api/controllers/export.controller.d.ts.map +1 -0
- package/dist/api/controllers/export.controller.js +80 -0
- package/dist/api/controllers/export.controller.js.map +1 -0
- package/dist/api/controllers/health.controller.d.ts +5 -0
- package/dist/api/controllers/health.controller.d.ts.map +1 -0
- package/dist/api/controllers/health.controller.js +16 -0
- package/dist/api/controllers/health.controller.js.map +1 -0
- package/dist/api/controllers/jobs.controller.d.ts +13 -0
- package/dist/api/controllers/jobs.controller.d.ts.map +1 -0
- package/dist/api/controllers/jobs.controller.js +125 -0
- package/dist/api/controllers/jobs.controller.js.map +1 -0
- package/dist/api/controllers/providers.controller.d.ts +15 -0
- package/dist/api/controllers/providers.controller.d.ts.map +1 -0
- package/dist/api/controllers/providers.controller.js +112 -0
- package/dist/api/controllers/providers.controller.js.map +1 -0
- package/dist/api/dto/AuditRequest.dto.d.ts +6 -0
- package/dist/api/dto/AuditRequest.dto.d.ts.map +1 -0
- package/dist/api/dto/AuditRequest.dto.js +3 -0
- package/dist/api/dto/AuditRequest.dto.js.map +1 -0
- package/dist/api/dto/AuditResponse.dto.d.ts +17 -0
- package/dist/api/dto/AuditResponse.dto.d.ts.map +1 -0
- package/dist/api/dto/AuditResponse.dto.js +3 -0
- package/dist/api/dto/AuditResponse.dto.js.map +1 -0
- package/dist/api/dto/TokenRequest.dto.d.ts +6 -0
- package/dist/api/dto/TokenRequest.dto.d.ts.map +1 -0
- package/dist/api/dto/TokenRequest.dto.js +3 -0
- package/dist/api/dto/TokenRequest.dto.js.map +1 -0
- package/dist/api/dto/TokenResponse.dto.d.ts +12 -0
- package/dist/api/dto/TokenResponse.dto.d.ts.map +1 -0
- package/dist/api/dto/TokenResponse.dto.js +3 -0
- package/dist/api/dto/TokenResponse.dto.js.map +1 -0
- package/dist/api/middlewares/authenticate.d.ts +12 -0
- package/dist/api/middlewares/authenticate.d.ts.map +1 -0
- package/dist/api/middlewares/authenticate.js +141 -0
- package/dist/api/middlewares/authenticate.js.map +1 -0
- package/dist/api/middlewares/errorHandler.d.ts +3 -0
- package/dist/api/middlewares/errorHandler.d.ts.map +1 -0
- package/dist/api/middlewares/errorHandler.js +30 -0
- package/dist/api/middlewares/errorHandler.js.map +1 -0
- package/dist/api/middlewares/rateLimit.d.ts +3 -0
- package/dist/api/middlewares/rateLimit.d.ts.map +1 -0
- package/dist/api/middlewares/rateLimit.js +34 -0
- package/dist/api/middlewares/rateLimit.js.map +1 -0
- package/dist/api/middlewares/validate.d.ts +4 -0
- package/dist/api/middlewares/validate.d.ts.map +1 -0
- package/dist/api/middlewares/validate.js +31 -0
- package/dist/api/middlewares/validate.js.map +1 -0
- package/dist/api/routes/audit.routes.d.ts +5 -0
- package/dist/api/routes/audit.routes.d.ts.map +1 -0
- package/dist/api/routes/audit.routes.js +24 -0
- package/dist/api/routes/audit.routes.js.map +1 -0
- package/dist/api/routes/auth.routes.d.ts +6 -0
- package/dist/api/routes/auth.routes.d.ts.map +1 -0
- package/dist/api/routes/auth.routes.js +22 -0
- package/dist/api/routes/auth.routes.js.map +1 -0
- package/dist/api/routes/export.routes.d.ts +5 -0
- package/dist/api/routes/export.routes.d.ts.map +1 -0
- package/dist/api/routes/export.routes.js +16 -0
- package/dist/api/routes/export.routes.js.map +1 -0
- package/dist/api/routes/health.routes.d.ts +4 -0
- package/dist/api/routes/health.routes.d.ts.map +1 -0
- package/dist/api/routes/health.routes.js +11 -0
- package/dist/api/routes/health.routes.js.map +1 -0
- package/dist/api/routes/index.d.ts +10 -0
- package/dist/api/routes/index.d.ts.map +1 -0
- package/dist/api/routes/index.js +20 -0
- package/dist/api/routes/index.js.map +1 -0
- package/dist/api/routes/providers.routes.d.ts +5 -0
- package/dist/api/routes/providers.routes.d.ts.map +1 -0
- package/dist/api/routes/providers.routes.js +13 -0
- package/dist/api/routes/providers.routes.js.map +1 -0
- package/dist/api/validators/audit.schemas.d.ts +60 -0
- package/dist/api/validators/audit.schemas.d.ts.map +1 -0
- package/dist/api/validators/audit.schemas.js +55 -0
- package/dist/api/validators/audit.schemas.js.map +1 -0
- package/dist/api/validators/auth.schemas.d.ts +17 -0
- package/dist/api/validators/auth.schemas.d.ts.map +1 -0
- package/dist/api/validators/auth.schemas.js +21 -0
- package/dist/api/validators/auth.schemas.js.map +1 -0
- package/dist/app.d.ts +3 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +62 -0
- package/dist/app.js.map +1 -0
- package/dist/config/config.schema.d.ts +65 -0
- package/dist/config/config.schema.d.ts.map +1 -0
- package/dist/config/config.schema.js +95 -0
- package/dist/config/config.schema.js.map +1 -0
- package/dist/config/index.d.ts +4 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +75 -0
- package/dist/config/index.js.map +1 -0
- package/dist/container.d.ts +47 -0
- package/dist/container.d.ts.map +1 -0
- package/dist/container.js +137 -0
- package/dist/container.js.map +1 -0
- package/dist/data/database.d.ts +13 -0
- package/dist/data/database.d.ts.map +1 -0
- package/dist/data/database.js +68 -0
- package/dist/data/database.js.map +1 -0
- package/dist/data/jobs/token-cleanup.job.d.ts +23 -0
- package/dist/data/jobs/token-cleanup.job.d.ts.map +1 -0
- package/dist/data/jobs/token-cleanup.job.js +96 -0
- package/dist/data/jobs/token-cleanup.job.js.map +1 -0
- package/dist/data/migrations/migration.runner.d.ts +13 -0
- package/dist/data/migrations/migration.runner.d.ts.map +1 -0
- package/dist/data/migrations/migration.runner.js +136 -0
- package/dist/data/migrations/migration.runner.js.map +1 -0
- package/dist/data/models/Token.model.d.ts +30 -0
- package/dist/data/models/Token.model.d.ts.map +1 -0
- package/dist/data/models/Token.model.js +3 -0
- package/dist/data/models/Token.model.js.map +1 -0
- package/dist/data/repositories/token.repository.d.ts +16 -0
- package/dist/data/repositories/token.repository.d.ts.map +1 -0
- package/dist/data/repositories/token.repository.js +97 -0
- package/dist/data/repositories/token.repository.js.map +1 -0
- package/dist/providers/azure/auth.provider.d.ts +5 -0
- package/dist/providers/azure/auth.provider.d.ts.map +1 -0
- package/dist/providers/azure/auth.provider.js +13 -0
- package/dist/providers/azure/auth.provider.js.map +1 -0
- package/dist/providers/azure/azure-errors.d.ts +40 -0
- package/dist/providers/azure/azure-errors.d.ts.map +1 -0
- package/dist/providers/azure/azure-errors.js +121 -0
- package/dist/providers/azure/azure-errors.js.map +1 -0
- package/dist/providers/azure/azure-retry.d.ts +41 -0
- package/dist/providers/azure/azure-retry.d.ts.map +1 -0
- package/dist/providers/azure/azure-retry.js +85 -0
- package/dist/providers/azure/azure-retry.js.map +1 -0
- package/dist/providers/azure/graph-client.d.ts +26 -0
- package/dist/providers/azure/graph-client.d.ts.map +1 -0
- package/dist/providers/azure/graph-client.js +146 -0
- package/dist/providers/azure/graph-client.js.map +1 -0
- package/dist/providers/azure/graph.provider.d.ts +23 -0
- package/dist/providers/azure/graph.provider.d.ts.map +1 -0
- package/dist/providers/azure/graph.provider.js +161 -0
- package/dist/providers/azure/graph.provider.js.map +1 -0
- package/dist/providers/azure/queries/app.queries.d.ts +6 -0
- package/dist/providers/azure/queries/app.queries.d.ts.map +1 -0
- package/dist/providers/azure/queries/app.queries.js +9 -0
- package/dist/providers/azure/queries/app.queries.js.map +1 -0
- package/dist/providers/azure/queries/policy.queries.d.ts +6 -0
- package/dist/providers/azure/queries/policy.queries.d.ts.map +1 -0
- package/dist/providers/azure/queries/policy.queries.js +9 -0
- package/dist/providers/azure/queries/policy.queries.js.map +1 -0
- package/dist/providers/azure/queries/user.queries.d.ts +7 -0
- package/dist/providers/azure/queries/user.queries.d.ts.map +1 -0
- package/dist/providers/azure/queries/user.queries.js +10 -0
- package/dist/providers/azure/queries/user.queries.js.map +1 -0
- package/dist/providers/interfaces/IGraphProvider.d.ts +31 -0
- package/dist/providers/interfaces/IGraphProvider.d.ts.map +1 -0
- package/dist/providers/interfaces/IGraphProvider.js +3 -0
- package/dist/providers/interfaces/IGraphProvider.js.map +1 -0
- package/dist/providers/interfaces/ILDAPProvider.d.ts +37 -0
- package/dist/providers/interfaces/ILDAPProvider.d.ts.map +1 -0
- package/dist/providers/interfaces/ILDAPProvider.js +3 -0
- package/dist/providers/interfaces/ILDAPProvider.js.map +1 -0
- package/dist/providers/ldap/acl-parser.d.ts +8 -0
- package/dist/providers/ldap/acl-parser.d.ts.map +1 -0
- package/dist/providers/ldap/acl-parser.js +157 -0
- package/dist/providers/ldap/acl-parser.js.map +1 -0
- package/dist/providers/ldap/ad-mappers.d.ts +8 -0
- package/dist/providers/ldap/ad-mappers.d.ts.map +1 -0
- package/dist/providers/ldap/ad-mappers.js +162 -0
- package/dist/providers/ldap/ad-mappers.js.map +1 -0
- package/dist/providers/ldap/ldap-client.d.ts +33 -0
- package/dist/providers/ldap/ldap-client.d.ts.map +1 -0
- package/dist/providers/ldap/ldap-client.js +195 -0
- package/dist/providers/ldap/ldap-client.js.map +1 -0
- package/dist/providers/ldap/ldap-errors.d.ts +48 -0
- package/dist/providers/ldap/ldap-errors.d.ts.map +1 -0
- package/dist/providers/ldap/ldap-errors.js +120 -0
- package/dist/providers/ldap/ldap-errors.js.map +1 -0
- package/dist/providers/ldap/ldap-retry.d.ts +14 -0
- package/dist/providers/ldap/ldap-retry.d.ts.map +1 -0
- package/dist/providers/ldap/ldap-retry.js +102 -0
- package/dist/providers/ldap/ldap-retry.js.map +1 -0
- package/dist/providers/ldap/ldap-sanitizer.d.ts +12 -0
- package/dist/providers/ldap/ldap-sanitizer.d.ts.map +1 -0
- package/dist/providers/ldap/ldap-sanitizer.js +104 -0
- package/dist/providers/ldap/ldap-sanitizer.js.map +1 -0
- package/dist/providers/ldap/ldap.provider.d.ts +21 -0
- package/dist/providers/ldap/ldap.provider.d.ts.map +1 -0
- package/dist/providers/ldap/ldap.provider.js +165 -0
- package/dist/providers/ldap/ldap.provider.js.map +1 -0
- package/dist/providers/ldap/queries/computer.queries.d.ts +6 -0
- package/dist/providers/ldap/queries/computer.queries.d.ts.map +1 -0
- package/dist/providers/ldap/queries/computer.queries.js +9 -0
- package/dist/providers/ldap/queries/computer.queries.js.map +1 -0
- package/dist/providers/ldap/queries/group.queries.d.ts +6 -0
- package/dist/providers/ldap/queries/group.queries.d.ts.map +1 -0
- package/dist/providers/ldap/queries/group.queries.js +9 -0
- package/dist/providers/ldap/queries/group.queries.js.map +1 -0
- package/dist/providers/ldap/queries/user.queries.d.ts +7 -0
- package/dist/providers/ldap/queries/user.queries.d.ts.map +1 -0
- package/dist/providers/ldap/queries/user.queries.js +10 -0
- package/dist/providers/ldap/queries/user.queries.js.map +1 -0
- package/dist/providers/smb/smb.provider.d.ts +68 -0
- package/dist/providers/smb/smb.provider.d.ts.map +1 -0
- package/dist/providers/smb/smb.provider.js +382 -0
- package/dist/providers/smb/smb.provider.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +44 -0
- package/dist/server.js.map +1 -0
- package/dist/services/audit/ad-audit.service.d.ts +70 -0
- package/dist/services/audit/ad-audit.service.d.ts.map +1 -0
- package/dist/services/audit/ad-audit.service.js +1019 -0
- package/dist/services/audit/ad-audit.service.js.map +1 -0
- package/dist/services/audit/attack-graph.service.d.ts +62 -0
- package/dist/services/audit/attack-graph.service.d.ts.map +1 -0
- package/dist/services/audit/attack-graph.service.js +702 -0
- package/dist/services/audit/attack-graph.service.js.map +1 -0
- package/dist/services/audit/audit.service.d.ts +4 -0
- package/dist/services/audit/audit.service.d.ts.map +1 -0
- package/dist/services/audit/audit.service.js +10 -0
- package/dist/services/audit/audit.service.js.map +1 -0
- package/dist/services/audit/azure-audit.service.d.ts +37 -0
- package/dist/services/audit/azure-audit.service.d.ts.map +1 -0
- package/dist/services/audit/azure-audit.service.js +153 -0
- package/dist/services/audit/azure-audit.service.js.map +1 -0
- package/dist/services/audit/detectors/ad/accounts.detector.d.ts +37 -0
- package/dist/services/audit/detectors/ad/accounts.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/accounts.detector.js +881 -0
- package/dist/services/audit/detectors/ad/accounts.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/adcs.detector.d.ts +21 -0
- package/dist/services/audit/detectors/ad/adcs.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/adcs.detector.js +227 -0
- package/dist/services/audit/detectors/ad/adcs.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/advanced.detector.d.ts +63 -0
- package/dist/services/audit/detectors/ad/advanced.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/advanced.detector.js +867 -0
- package/dist/services/audit/detectors/ad/advanced.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/attack-paths.detector.d.ts +16 -0
- package/dist/services/audit/detectors/ad/attack-paths.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/attack-paths.detector.js +369 -0
- package/dist/services/audit/detectors/ad/attack-paths.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/compliance.detector.d.ts +28 -0
- package/dist/services/audit/detectors/ad/compliance.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/compliance.detector.js +896 -0
- package/dist/services/audit/detectors/ad/compliance.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/computers.detector.d.ts +30 -0
- package/dist/services/audit/detectors/ad/computers.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/computers.detector.js +799 -0
- package/dist/services/audit/detectors/ad/computers.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/gpo.detector.d.ts +17 -0
- package/dist/services/audit/detectors/ad/gpo.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/gpo.detector.js +257 -0
- package/dist/services/audit/detectors/ad/gpo.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/groups.detector.d.ts +19 -0
- package/dist/services/audit/detectors/ad/groups.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/groups.detector.js +488 -0
- package/dist/services/audit/detectors/ad/groups.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/index.d.ts +15 -0
- package/dist/services/audit/detectors/ad/index.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/index.js +51 -0
- package/dist/services/audit/detectors/ad/index.js.map +1 -0
- package/dist/services/audit/detectors/ad/kerberos.detector.d.ts +17 -0
- package/dist/services/audit/detectors/ad/kerberos.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/kerberos.detector.js +293 -0
- package/dist/services/audit/detectors/ad/kerberos.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/monitoring.detector.d.ts +23 -0
- package/dist/services/audit/detectors/ad/monitoring.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/monitoring.detector.js +328 -0
- package/dist/services/audit/detectors/ad/monitoring.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/network.detector.d.ts +39 -0
- package/dist/services/audit/detectors/ad/network.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/network.detector.js +257 -0
- package/dist/services/audit/detectors/ad/network.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/password.detector.d.ts +14 -0
- package/dist/services/audit/detectors/ad/password.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/password.detector.js +235 -0
- package/dist/services/audit/detectors/ad/password.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/permissions.detector.d.ts +20 -0
- package/dist/services/audit/detectors/ad/permissions.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/permissions.detector.js +392 -0
- package/dist/services/audit/detectors/ad/permissions.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/trusts.detector.d.ts +11 -0
- package/dist/services/audit/detectors/ad/trusts.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/trusts.detector.js +186 -0
- package/dist/services/audit/detectors/ad/trusts.detector.js.map +1 -0
- package/dist/services/audit/detectors/azure/app-security.detector.d.ts +11 -0
- package/dist/services/audit/detectors/azure/app-security.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/azure/app-security.detector.js +184 -0
- package/dist/services/audit/detectors/azure/app-security.detector.js.map +1 -0
- package/dist/services/audit/detectors/azure/conditional-access.detector.d.ts +10 -0
- package/dist/services/audit/detectors/azure/conditional-access.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/azure/conditional-access.detector.js +130 -0
- package/dist/services/audit/detectors/azure/conditional-access.detector.js.map +1 -0
- package/dist/services/audit/detectors/azure/privilege-security.detector.d.ts +8 -0
- package/dist/services/audit/detectors/azure/privilege-security.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/azure/privilege-security.detector.js +113 -0
- package/dist/services/audit/detectors/azure/privilege-security.detector.js.map +1 -0
- package/dist/services/audit/detectors/azure/user-security.detector.d.ts +14 -0
- package/dist/services/audit/detectors/azure/user-security.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/azure/user-security.detector.js +198 -0
- package/dist/services/audit/detectors/azure/user-security.detector.js.map +1 -0
- package/dist/services/audit/detectors/index.d.ts +2 -0
- package/dist/services/audit/detectors/index.d.ts.map +1 -0
- package/dist/services/audit/detectors/index.js +38 -0
- package/dist/services/audit/detectors/index.js.map +1 -0
- package/dist/services/audit/response-formatter.d.ts +176 -0
- package/dist/services/audit/response-formatter.d.ts.map +1 -0
- package/dist/services/audit/response-formatter.js +240 -0
- package/dist/services/audit/response-formatter.js.map +1 -0
- package/dist/services/audit/scoring.service.d.ts +15 -0
- package/dist/services/audit/scoring.service.d.ts.map +1 -0
- package/dist/services/audit/scoring.service.js +139 -0
- package/dist/services/audit/scoring.service.js.map +1 -0
- package/dist/services/auth/crypto.service.d.ts +19 -0
- package/dist/services/auth/crypto.service.d.ts.map +1 -0
- package/dist/services/auth/crypto.service.js +135 -0
- package/dist/services/auth/crypto.service.js.map +1 -0
- package/dist/services/auth/errors.d.ts +19 -0
- package/dist/services/auth/errors.d.ts.map +1 -0
- package/dist/services/auth/errors.js +46 -0
- package/dist/services/auth/errors.js.map +1 -0
- package/dist/services/auth/token.service.d.ts +41 -0
- package/dist/services/auth/token.service.d.ts.map +1 -0
- package/dist/services/auth/token.service.js +208 -0
- package/dist/services/auth/token.service.js.map +1 -0
- package/dist/services/config/config.service.d.ts +6 -0
- package/dist/services/config/config.service.d.ts.map +1 -0
- package/dist/services/config/config.service.js +64 -0
- package/dist/services/config/config.service.js.map +1 -0
- package/dist/services/export/export.service.d.ts +28 -0
- package/dist/services/export/export.service.d.ts.map +1 -0
- package/dist/services/export/export.service.js +28 -0
- package/dist/services/export/export.service.js.map +1 -0
- package/dist/services/export/formatters/csv.formatter.d.ts +8 -0
- package/dist/services/export/formatters/csv.formatter.d.ts.map +1 -0
- package/dist/services/export/formatters/csv.formatter.js +46 -0
- package/dist/services/export/formatters/csv.formatter.js.map +1 -0
- package/dist/services/export/formatters/json.formatter.d.ts +40 -0
- package/dist/services/export/formatters/json.formatter.d.ts.map +1 -0
- package/dist/services/export/formatters/json.formatter.js +58 -0
- package/dist/services/export/formatters/json.formatter.js.map +1 -0
- package/dist/services/jobs/azure-job-runner.d.ts +38 -0
- package/dist/services/jobs/azure-job-runner.d.ts.map +1 -0
- package/dist/services/jobs/azure-job-runner.js +199 -0
- package/dist/services/jobs/azure-job-runner.js.map +1 -0
- package/dist/services/jobs/index.d.ts +4 -0
- package/dist/services/jobs/index.d.ts.map +1 -0
- package/dist/services/jobs/index.js +20 -0
- package/dist/services/jobs/index.js.map +1 -0
- package/dist/services/jobs/job-runner.d.ts +64 -0
- package/dist/services/jobs/job-runner.d.ts.map +1 -0
- package/dist/services/jobs/job-runner.js +952 -0
- package/dist/services/jobs/job-runner.js.map +1 -0
- package/dist/services/jobs/job-store.d.ts +27 -0
- package/dist/services/jobs/job-store.d.ts.map +1 -0
- package/dist/services/jobs/job-store.js +261 -0
- package/dist/services/jobs/job-store.js.map +1 -0
- package/dist/services/jobs/job.types.d.ts +67 -0
- package/dist/services/jobs/job.types.d.ts.map +1 -0
- package/dist/services/jobs/job.types.js +36 -0
- package/dist/services/jobs/job.types.js.map +1 -0
- package/dist/types/ad.types.d.ts +74 -0
- package/dist/types/ad.types.d.ts.map +1 -0
- package/dist/types/ad.types.js +3 -0
- package/dist/types/ad.types.js.map +1 -0
- package/dist/types/adcs.types.d.ts +58 -0
- package/dist/types/adcs.types.d.ts.map +1 -0
- package/dist/types/adcs.types.js +38 -0
- package/dist/types/adcs.types.js.map +1 -0
- package/dist/types/attack-graph.types.d.ts +135 -0
- package/dist/types/attack-graph.types.d.ts.map +1 -0
- package/dist/types/attack-graph.types.js +58 -0
- package/dist/types/attack-graph.types.js.map +1 -0
- package/dist/types/audit.types.d.ts +34 -0
- package/dist/types/audit.types.d.ts.map +1 -0
- package/dist/types/audit.types.js +3 -0
- package/dist/types/audit.types.js.map +1 -0
- package/dist/types/azure.types.d.ts +61 -0
- package/dist/types/azure.types.d.ts.map +1 -0
- package/dist/types/azure.types.js +3 -0
- package/dist/types/azure.types.js.map +1 -0
- package/dist/types/config.types.d.ts +63 -0
- package/dist/types/config.types.d.ts.map +1 -0
- package/dist/types/config.types.js +3 -0
- package/dist/types/config.types.js.map +1 -0
- package/dist/types/error.types.d.ts +33 -0
- package/dist/types/error.types.d.ts.map +1 -0
- package/dist/types/error.types.js +70 -0
- package/dist/types/error.types.js.map +1 -0
- package/dist/types/finding.types.d.ts +133 -0
- package/dist/types/finding.types.d.ts.map +1 -0
- package/dist/types/finding.types.js +3 -0
- package/dist/types/finding.types.js.map +1 -0
- package/dist/types/gpo.types.d.ts +39 -0
- package/dist/types/gpo.types.d.ts.map +1 -0
- package/dist/types/gpo.types.js +15 -0
- package/dist/types/gpo.types.js.map +1 -0
- package/dist/types/token.types.d.ts +26 -0
- package/dist/types/token.types.d.ts.map +1 -0
- package/dist/types/token.types.js +3 -0
- package/dist/types/token.types.js.map +1 -0
- package/dist/types/trust.types.d.ts +45 -0
- package/dist/types/trust.types.d.ts.map +1 -0
- package/dist/types/trust.types.js +71 -0
- package/dist/types/trust.types.js.map +1 -0
- package/dist/utils/entity-converter.d.ts +17 -0
- package/dist/utils/entity-converter.d.ts.map +1 -0
- package/dist/utils/entity-converter.js +285 -0
- package/dist/utils/entity-converter.js.map +1 -0
- package/dist/utils/graph.util.d.ts +66 -0
- package/dist/utils/graph.util.d.ts.map +1 -0
- package/dist/utils/graph.util.js +382 -0
- package/dist/utils/graph.util.js.map +1 -0
- package/dist/utils/logger.d.ts +7 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +86 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/type-name-normalizer.d.ts +5 -0
- package/dist/utils/type-name-normalizer.d.ts.map +1 -0
- package/dist/utils/type-name-normalizer.js +218 -0
- package/dist/utils/type-name-normalizer.js.map +1 -0
- package/docker-compose.yml +26 -0
- package/docs/api/README.md +178 -0
- package/docs/api/openapi.yaml +1524 -0
- package/eslint.config.js +54 -0
- package/jest.config.js +38 -0
- package/package.json +97 -0
- package/scripts/fetch-ad-cert.sh +142 -0
- package/src/.gitkeep +0 -0
- package/src/api/.gitkeep +0 -0
- package/src/api/controllers/.gitkeep +0 -0
- package/src/api/controllers/audit.controller.ts +313 -0
- package/src/api/controllers/auth.controller.ts +258 -0
- package/src/api/controllers/export.controller.ts +153 -0
- package/src/api/controllers/health.controller.ts +16 -0
- package/src/api/controllers/jobs.controller.ts +187 -0
- package/src/api/controllers/providers.controller.ts +165 -0
- package/src/api/dto/.gitkeep +0 -0
- package/src/api/dto/AuditRequest.dto.ts +8 -0
- package/src/api/dto/AuditResponse.dto.ts +19 -0
- package/src/api/dto/TokenRequest.dto.ts +8 -0
- package/src/api/dto/TokenResponse.dto.ts +14 -0
- package/src/api/middlewares/.gitkeep +0 -0
- package/src/api/middlewares/authenticate.ts +203 -0
- package/src/api/middlewares/errorHandler.ts +54 -0
- package/src/api/middlewares/rateLimit.ts +35 -0
- package/src/api/middlewares/validate.ts +32 -0
- package/src/api/routes/.gitkeep +0 -0
- package/src/api/routes/audit.routes.ts +77 -0
- package/src/api/routes/auth.routes.ts +71 -0
- package/src/api/routes/export.routes.ts +34 -0
- package/src/api/routes/health.routes.ts +14 -0
- package/src/api/routes/index.ts +40 -0
- package/src/api/routes/providers.routes.ts +24 -0
- package/src/api/validators/.gitkeep +0 -0
- package/src/api/validators/audit.schemas.ts +59 -0
- package/src/api/validators/auth.schemas.ts +59 -0
- package/src/app.ts +87 -0
- package/src/config/.gitkeep +0 -0
- package/src/config/config.schema.ts +108 -0
- package/src/config/index.ts +82 -0
- package/src/container.ts +221 -0
- package/src/data/.gitkeep +0 -0
- package/src/data/database.ts +78 -0
- package/src/data/jobs/token-cleanup.job.ts +166 -0
- package/src/data/migrations/.gitkeep +0 -0
- package/src/data/migrations/001_initial_schema.sql +47 -0
- package/src/data/migrations/migration.runner.ts +125 -0
- package/src/data/models/.gitkeep +0 -0
- package/src/data/models/Token.model.ts +35 -0
- package/src/data/repositories/.gitkeep +0 -0
- package/src/data/repositories/token.repository.ts +160 -0
- package/src/providers/.gitkeep +0 -0
- package/src/providers/azure/.gitkeep +0 -0
- package/src/providers/azure/auth.provider.ts +14 -0
- package/src/providers/azure/azure-errors.ts +189 -0
- package/src/providers/azure/azure-retry.ts +168 -0
- package/src/providers/azure/graph-client.ts +315 -0
- package/src/providers/azure/graph.provider.ts +294 -0
- package/src/providers/azure/queries/app.queries.ts +9 -0
- package/src/providers/azure/queries/policy.queries.ts +9 -0
- package/src/providers/azure/queries/user.queries.ts +10 -0
- package/src/providers/interfaces/.gitkeep +0 -0
- package/src/providers/interfaces/IGraphProvider.ts +117 -0
- package/src/providers/interfaces/ILDAPProvider.ts +142 -0
- package/src/providers/ldap/.gitkeep +0 -0
- package/src/providers/ldap/acl-parser.ts +231 -0
- package/src/providers/ldap/ad-mappers.ts +280 -0
- package/src/providers/ldap/ldap-client.ts +259 -0
- package/src/providers/ldap/ldap-errors.ts +188 -0
- package/src/providers/ldap/ldap-retry.ts +267 -0
- package/src/providers/ldap/ldap-sanitizer.ts +273 -0
- package/src/providers/ldap/ldap.provider.ts +293 -0
- package/src/providers/ldap/queries/computer.queries.ts +9 -0
- package/src/providers/ldap/queries/group.queries.ts +9 -0
- package/src/providers/ldap/queries/user.queries.ts +10 -0
- package/src/providers/smb/smb.provider.ts +653 -0
- package/src/server.ts +60 -0
- package/src/services/.gitkeep +0 -0
- package/src/services/audit/.gitkeep +0 -0
- package/src/services/audit/ad-audit.service.ts +1481 -0
- package/src/services/audit/attack-graph.service.ts +1104 -0
- package/src/services/audit/audit.service.ts +12 -0
- package/src/services/audit/azure-audit.service.ts +286 -0
- package/src/services/audit/detectors/ad/accounts.detector.ts +1232 -0
- package/src/services/audit/detectors/ad/adcs.detector.ts +449 -0
- package/src/services/audit/detectors/ad/advanced.detector.ts +1270 -0
- package/src/services/audit/detectors/ad/attack-paths.detector.ts +600 -0
- package/src/services/audit/detectors/ad/compliance.detector.ts +1421 -0
- package/src/services/audit/detectors/ad/computers.detector.ts +1188 -0
- package/src/services/audit/detectors/ad/gpo.detector.ts +485 -0
- package/src/services/audit/detectors/ad/groups.detector.ts +685 -0
- package/src/services/audit/detectors/ad/index.ts +84 -0
- package/src/services/audit/detectors/ad/kerberos.detector.ts +424 -0
- package/src/services/audit/detectors/ad/monitoring.detector.ts +501 -0
- package/src/services/audit/detectors/ad/network.detector.ts +538 -0
- package/src/services/audit/detectors/ad/password.detector.ts +324 -0
- package/src/services/audit/detectors/ad/permissions.detector.ts +637 -0
- package/src/services/audit/detectors/ad/trusts.detector.ts +315 -0
- package/src/services/audit/detectors/azure/app-security.detector.ts +246 -0
- package/src/services/audit/detectors/azure/conditional-access.detector.ts +186 -0
- package/src/services/audit/detectors/azure/privilege-security.detector.ts +176 -0
- package/src/services/audit/detectors/azure/user-security.detector.ts +280 -0
- package/src/services/audit/detectors/index.ts +18 -0
- package/src/services/audit/response-formatter.ts +604 -0
- package/src/services/audit/scoring.service.ts +234 -0
- package/src/services/auth/.gitkeep +0 -0
- package/src/services/auth/crypto.service.ts +230 -0
- package/src/services/auth/errors.ts +47 -0
- package/src/services/auth/token.service.ts +420 -0
- package/src/services/config/.gitkeep +0 -0
- package/src/services/config/config.service.ts +75 -0
- package/src/services/export/.gitkeep +0 -0
- package/src/services/export/export.service.ts +99 -0
- package/src/services/export/formatters/csv.formatter.ts +124 -0
- package/src/services/export/formatters/json.formatter.ts +160 -0
- package/src/services/jobs/azure-job-runner.ts +312 -0
- package/src/services/jobs/index.ts +9 -0
- package/src/services/jobs/job-runner.ts +1280 -0
- package/src/services/jobs/job-store.ts +384 -0
- package/src/services/jobs/job.types.ts +182 -0
- package/src/types/.gitkeep +0 -0
- package/src/types/ad.types.ts +91 -0
- package/src/types/adcs.types.ts +107 -0
- package/src/types/attack-graph.types.ts +260 -0
- package/src/types/audit.types.ts +42 -0
- package/src/types/azure.types.ts +68 -0
- package/src/types/config.types.ts +79 -0
- package/src/types/error.types.ts +69 -0
- package/src/types/finding.types.ts +284 -0
- package/src/types/gpo.types.ts +72 -0
- package/src/types/smb2.d.ts +73 -0
- package/src/types/token.types.ts +32 -0
- package/src/types/trust.types.ts +140 -0
- package/src/utils/.gitkeep +0 -0
- package/src/utils/entity-converter.ts +453 -0
- package/src/utils/graph.util.ts +609 -0
- package/src/utils/logger.ts +111 -0
- package/src/utils/type-name-normalizer.ts +302 -0
- package/tests/.gitkeep +0 -0
- package/tests/e2e/.gitkeep +0 -0
- package/tests/fixtures/.gitkeep +0 -0
- package/tests/integration/.gitkeep +0 -0
- package/tests/integration/README.md +156 -0
- package/tests/integration/ad-audit.integration.test.ts +216 -0
- package/tests/integration/api/.gitkeep +0 -0
- package/tests/integration/api/endpoints.integration.test.ts +431 -0
- package/tests/integration/auth/jwt-authentication.integration.test.ts +358 -0
- package/tests/integration/providers/.gitkeep +0 -0
- package/tests/integration/providers/azure-basic.integration.test.ts +167 -0
- package/tests/integration/providers/ldap-basic.integration.test.ts +152 -0
- package/tests/integration/providers/ldap-connectivity.test.ts +44 -0
- package/tests/integration/providers/ldap-provider.integration.test.ts +347 -0
- package/tests/mocks/.gitkeep +0 -0
- package/tests/setup.ts +16 -0
- package/tests/unit/.gitkeep +0 -0
- package/tests/unit/api/middlewares/authenticate.test.ts +446 -0
- package/tests/unit/providers/.gitkeep +0 -0
- package/tests/unit/providers/azure/azure-errors.test.ts +193 -0
- package/tests/unit/providers/azure/azure-retry.test.ts +254 -0
- package/tests/unit/providers/azure/graph-provider.test.ts +313 -0
- package/tests/unit/providers/ldap/ad-mappers.test.ts +392 -0
- package/tests/unit/providers/ldap/ldap-provider.test.ts +376 -0
- package/tests/unit/providers/ldap/ldap-retry.test.ts +377 -0
- package/tests/unit/providers/ldap/ldap-sanitizer.test.ts +301 -0
- package/tests/unit/sample.test.ts +19 -0
- package/tests/unit/services/.gitkeep +0 -0
- package/tests/unit/services/audit/detectors/ad/accounts.detector.test.ts +393 -0
- package/tests/unit/services/audit/detectors/ad/advanced.detector.test.ts +380 -0
- package/tests/unit/services/audit/detectors/ad/computers.detector.test.ts +440 -0
- package/tests/unit/services/audit/detectors/ad/groups.detector.test.ts +276 -0
- package/tests/unit/services/audit/detectors/ad/kerberos.detector.test.ts +215 -0
- package/tests/unit/services/audit/detectors/ad/password.detector.test.ts +226 -0
- package/tests/unit/services/audit/detectors/ad/permissions.detector.test.ts +244 -0
- package/tests/unit/services/audit/detectors/azure/app-security.detector.test.ts +349 -0
- package/tests/unit/services/audit/detectors/azure/conditional-access.detector.test.ts +374 -0
- package/tests/unit/services/audit/detectors/azure/privilege-security.detector.test.ts +374 -0
- package/tests/unit/services/audit/detectors/azure/user-security.detector.test.ts +297 -0
- package/tests/unit/services/auth/crypto.service.test.ts +296 -0
- package/tests/unit/services/auth/token.service.test.ts +579 -0
- package/tests/unit/services/export/export.service.test.ts +241 -0
- package/tests/unit/services/export/formatters/csv.formatter.test.ts +270 -0
- package/tests/unit/services/export/formatters/json.formatter.test.ts +258 -0
- package/tests/unit/utils/.gitkeep +0 -0
- package/tsconfig.json +50 -0
package/src/container.ts
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
import { getConfig } from './config';
|
|
3
|
+
import { DatabaseManager } from './data/database';
|
|
4
|
+
import { TokenRepository } from './data/repositories/token.repository';
|
|
5
|
+
import { CryptoService } from './services/auth/crypto.service';
|
|
6
|
+
import { TokenService } from './services/auth/token.service';
|
|
7
|
+
import { HealthController } from './api/controllers/health.controller';
|
|
8
|
+
import { AuthController } from './api/controllers/auth.controller';
|
|
9
|
+
import { AuditController } from './api/controllers/audit.controller';
|
|
10
|
+
import { ExportController } from './api/controllers/export.controller';
|
|
11
|
+
import { ProvidersController } from './api/controllers/providers.controller';
|
|
12
|
+
import { LDAPProvider } from './providers/ldap/ldap.provider';
|
|
13
|
+
import { GraphProvider } from './providers/azure/graph.provider';
|
|
14
|
+
import type { Logger } from 'winston';
|
|
15
|
+
import { logger } from './utils/logger';
|
|
16
|
+
import { InfoEndpointsConfig } from './types/config.types';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Dependency Injection Container
|
|
20
|
+
*
|
|
21
|
+
* Manages all application dependencies with singleton pattern.
|
|
22
|
+
* Handles proper initialization order:
|
|
23
|
+
* 1. Config
|
|
24
|
+
* 2. Database + Repositories
|
|
25
|
+
* 3. CryptoService (loads RSA keys)
|
|
26
|
+
* 4. TokenService
|
|
27
|
+
* 5. LDAP Provider
|
|
28
|
+
* 6. Azure Graph Provider
|
|
29
|
+
* 7. Controllers
|
|
30
|
+
*
|
|
31
|
+
* Updated: Story 1.4 (JWT), Story 1.5 (LDAP), Story 1.6 (Azure)
|
|
32
|
+
*/
|
|
33
|
+
export class DIContainer {
|
|
34
|
+
private static instance: DIContainer | null = null;
|
|
35
|
+
private logger: Logger;
|
|
36
|
+
|
|
37
|
+
// Database
|
|
38
|
+
private db!: Database.Database;
|
|
39
|
+
private dbManager!: DatabaseManager;
|
|
40
|
+
|
|
41
|
+
// Repositories
|
|
42
|
+
private tokenRepository!: TokenRepository;
|
|
43
|
+
|
|
44
|
+
// Services
|
|
45
|
+
private cryptoService!: CryptoService;
|
|
46
|
+
private tokenService!: TokenService;
|
|
47
|
+
|
|
48
|
+
// Providers
|
|
49
|
+
private ldapProvider!: LDAPProvider;
|
|
50
|
+
private graphProvider!: GraphProvider;
|
|
51
|
+
|
|
52
|
+
// Controllers
|
|
53
|
+
private healthController!: HealthController;
|
|
54
|
+
private authController!: AuthController;
|
|
55
|
+
private auditController!: AuditController;
|
|
56
|
+
private exportController!: ExportController;
|
|
57
|
+
private providersController!: ProvidersController;
|
|
58
|
+
|
|
59
|
+
// Config
|
|
60
|
+
private infoEndpointsConfig!: InfoEndpointsConfig;
|
|
61
|
+
|
|
62
|
+
private constructor() {
|
|
63
|
+
this.logger = logger;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get singleton instance
|
|
68
|
+
*
|
|
69
|
+
* @throws Error if instance not initialized (call initialize() first)
|
|
70
|
+
*/
|
|
71
|
+
static getInstance(): DIContainer {
|
|
72
|
+
if (!DIContainer.instance) {
|
|
73
|
+
throw new Error('DIContainer not initialized. Call DIContainer.initialize() first.');
|
|
74
|
+
}
|
|
75
|
+
return DIContainer.instance;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Initialize DI container with async dependencies
|
|
80
|
+
*
|
|
81
|
+
* This method must be called before using the container.
|
|
82
|
+
* It loads configuration, initializes database, loads RSA keys, and creates all services.
|
|
83
|
+
*
|
|
84
|
+
* @returns Promise<DIContainer> Initialized container instance
|
|
85
|
+
*/
|
|
86
|
+
static async initialize(): Promise<DIContainer> {
|
|
87
|
+
if (DIContainer.instance) {
|
|
88
|
+
return DIContainer.instance;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const container = new DIContainer();
|
|
92
|
+
await container.init();
|
|
93
|
+
DIContainer.instance = container;
|
|
94
|
+
return container;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Reset singleton instance (for testing)
|
|
99
|
+
*/
|
|
100
|
+
static reset(): void {
|
|
101
|
+
DIContainer.instance = null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Internal initialization logic
|
|
106
|
+
*/
|
|
107
|
+
private async init(): Promise<void> {
|
|
108
|
+
this.logger.info('Initializing DI container');
|
|
109
|
+
|
|
110
|
+
// 1. Load configuration
|
|
111
|
+
const config = getConfig();
|
|
112
|
+
this.logger.debug('Configuration loaded', {
|
|
113
|
+
port: config.server.port,
|
|
114
|
+
env: config.server.nodeEnv,
|
|
115
|
+
dbPath: config.database.path,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// 2. Initialize database and repositories
|
|
119
|
+
this.dbManager = DatabaseManager.getInstance();
|
|
120
|
+
this.db = this.dbManager.connect(config.database.path);
|
|
121
|
+
this.tokenRepository = new TokenRepository(this.db);
|
|
122
|
+
this.logger.debug('Database and repositories initialized');
|
|
123
|
+
|
|
124
|
+
// 3. Initialize crypto service and load RSA keys
|
|
125
|
+
this.cryptoService = new CryptoService(
|
|
126
|
+
config.jwt.privateKeyPath,
|
|
127
|
+
config.jwt.publicKeyPath
|
|
128
|
+
);
|
|
129
|
+
await this.cryptoService.loadOrGenerateKeys();
|
|
130
|
+
this.logger.debug('Crypto service initialized and keys loaded');
|
|
131
|
+
|
|
132
|
+
// 4. Initialize token service
|
|
133
|
+
this.tokenService = new TokenService(this.tokenRepository, this.cryptoService);
|
|
134
|
+
this.logger.debug('Token service initialized');
|
|
135
|
+
|
|
136
|
+
// 5. Initialize LDAP provider
|
|
137
|
+
this.ldapProvider = new LDAPProvider(config.ldap);
|
|
138
|
+
this.logger.debug('LDAP provider initialized', {
|
|
139
|
+
url: config.ldap.url,
|
|
140
|
+
baseDN: config.ldap.baseDN,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// 6. Initialize Azure Graph provider (only if enabled)
|
|
144
|
+
if (config.azure.enabled && config.azure.tenantId && config.azure.clientId && config.azure.clientSecret) {
|
|
145
|
+
this.graphProvider = new GraphProvider({
|
|
146
|
+
tenantId: config.azure.tenantId,
|
|
147
|
+
clientId: config.azure.clientId,
|
|
148
|
+
clientSecret: config.azure.clientSecret,
|
|
149
|
+
});
|
|
150
|
+
this.logger.debug('Azure Graph provider initialized', {
|
|
151
|
+
tenantId: config.azure.tenantId,
|
|
152
|
+
clientId: config.azure.clientId,
|
|
153
|
+
});
|
|
154
|
+
} else {
|
|
155
|
+
this.logger.info('Azure provider disabled (AZURE_ENABLED=false or missing credentials)');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// 7. Initialize controllers
|
|
159
|
+
this.healthController = new HealthController();
|
|
160
|
+
this.authController = new AuthController(this.tokenService, config.jwt);
|
|
161
|
+
this.auditController = new AuditController();
|
|
162
|
+
this.exportController = new ExportController();
|
|
163
|
+
this.providersController = new ProvidersController(config.ldap, config.azure, this.graphProvider);
|
|
164
|
+
this.logger.debug('Controllers initialized');
|
|
165
|
+
|
|
166
|
+
// 8. Store config for routes
|
|
167
|
+
this.infoEndpointsConfig = config.infoEndpoints;
|
|
168
|
+
|
|
169
|
+
this.logger.info('DI container initialization complete');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// === Getters ===
|
|
173
|
+
|
|
174
|
+
getDatabase(): Database.Database {
|
|
175
|
+
return this.db;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
getTokenRepository(): TokenRepository {
|
|
179
|
+
return this.tokenRepository;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
getCryptoService(): CryptoService {
|
|
183
|
+
return this.cryptoService;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
getTokenService(): TokenService {
|
|
187
|
+
return this.tokenService;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
getLDAPProvider(): LDAPProvider {
|
|
191
|
+
return this.ldapProvider;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
getGraphProvider(): GraphProvider {
|
|
195
|
+
return this.graphProvider;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
getHealthController(): HealthController {
|
|
199
|
+
return this.healthController;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
getAuthController(): AuthController {
|
|
203
|
+
return this.authController;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
getAuditController(): AuditController {
|
|
207
|
+
return this.auditController;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
getExportController(): ExportController {
|
|
211
|
+
return this.exportController;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
getProvidersController(): ProvidersController {
|
|
215
|
+
return this.providersController;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
getInfoEndpointsConfig(): InfoEndpointsConfig {
|
|
219
|
+
return this.infoEndpointsConfig;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
import { mkdirSync, existsSync } from 'fs';
|
|
3
|
+
import { dirname } from 'path';
|
|
4
|
+
import { logInfo, logError } from '../utils/logger';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* SQLite Database Connection Manager
|
|
8
|
+
* Singleton pattern for database access
|
|
9
|
+
*/
|
|
10
|
+
export class DatabaseManager {
|
|
11
|
+
private static instance: DatabaseManager;
|
|
12
|
+
private db: Database.Database | null = null;
|
|
13
|
+
private dbPath: string | null = null;
|
|
14
|
+
|
|
15
|
+
private constructor() {}
|
|
16
|
+
|
|
17
|
+
static getInstance(): DatabaseManager {
|
|
18
|
+
if (!DatabaseManager.instance) {
|
|
19
|
+
DatabaseManager.instance = new DatabaseManager();
|
|
20
|
+
}
|
|
21
|
+
return DatabaseManager.instance;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
connect(dbPath: string): Database.Database {
|
|
25
|
+
if (this.db) {
|
|
26
|
+
return this.db;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
// Create parent directory if not exists
|
|
31
|
+
const dir = dirname(dbPath);
|
|
32
|
+
if (!existsSync(dir)) {
|
|
33
|
+
mkdirSync(dir, { recursive: true });
|
|
34
|
+
logInfo('Created database directory', { path: dir });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Connect to database
|
|
38
|
+
this.db = new Database(dbPath);
|
|
39
|
+
this.dbPath = dbPath;
|
|
40
|
+
|
|
41
|
+
// Configure pragmas for performance and safety
|
|
42
|
+
this.db.pragma('journal_mode = WAL'); // Write-Ahead Logging
|
|
43
|
+
this.db.pragma('synchronous = NORMAL'); // Balance safety/performance
|
|
44
|
+
this.db.pragma('foreign_keys = ON'); // Enable foreign key constraints
|
|
45
|
+
this.db.pragma('busy_timeout = 5000'); // 5 second timeout on locks
|
|
46
|
+
|
|
47
|
+
logInfo('Database connected successfully', {
|
|
48
|
+
path: dbPath,
|
|
49
|
+
journalMode: this.db.pragma('journal_mode', { simple: true }),
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
return this.db;
|
|
53
|
+
} catch (error) {
|
|
54
|
+
logError('Failed to connect to database', error as Error, { path: dbPath });
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
getDatabase(): Database.Database {
|
|
60
|
+
if (!this.db) {
|
|
61
|
+
throw new Error('Database not connected. Call connect() first.');
|
|
62
|
+
}
|
|
63
|
+
return this.db;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
close(): void {
|
|
67
|
+
if (this.db) {
|
|
68
|
+
logInfo('Closing database connection', { path: this.dbPath });
|
|
69
|
+
this.db.close();
|
|
70
|
+
this.db = null;
|
|
71
|
+
this.dbPath = null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
isConnected(): boolean {
|
|
76
|
+
return this.db !== null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { TokenRepository } from '../repositories/token.repository';
|
|
2
|
+
import { logInfo, logError } from '../../utils/logger';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Token Cleanup Job
|
|
6
|
+
* Removes expired and old revoked tokens according to retention policy
|
|
7
|
+
*
|
|
8
|
+
* Retention Policy:
|
|
9
|
+
* - Expired tokens (expires_at < now): Deleted immediately if not revoked
|
|
10
|
+
* - Expired + Revoked tokens (expires_at < now AND revoked_at < now - 90 days): Deleted after 90 days
|
|
11
|
+
* - Active tokens: Never deleted
|
|
12
|
+
*
|
|
13
|
+
* This job should be scheduled to run periodically (e.g., daily at 3 AM)
|
|
14
|
+
*/
|
|
15
|
+
export class TokenCleanupJob {
|
|
16
|
+
constructor(private tokenRepo: TokenRepository) {}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Execute the cleanup job
|
|
20
|
+
* @returns Number of tokens deleted
|
|
21
|
+
*/
|
|
22
|
+
run(): number {
|
|
23
|
+
logInfo('Starting token cleanup job');
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const before = this.tokenRepo.count();
|
|
27
|
+
|
|
28
|
+
// Delete expired tokens (90-day retention for revoked)
|
|
29
|
+
const deletedCount = this.deleteExpiredTokens();
|
|
30
|
+
|
|
31
|
+
const after = this.tokenRepo.count();
|
|
32
|
+
|
|
33
|
+
logInfo('Token cleanup job completed', {
|
|
34
|
+
tokensBefore: before,
|
|
35
|
+
tokensAfter: after,
|
|
36
|
+
deletedCount,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
return deletedCount;
|
|
40
|
+
} catch (error) {
|
|
41
|
+
logError('Token cleanup job failed', error as Error);
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Delete expired tokens with 90-day retention for revoked tokens
|
|
48
|
+
* @private
|
|
49
|
+
* @returns Number of tokens deleted
|
|
50
|
+
*/
|
|
51
|
+
private deleteExpiredTokens(): number {
|
|
52
|
+
// Query deletes:
|
|
53
|
+
// 1. Non-revoked expired tokens (cleanup immediately)
|
|
54
|
+
// 2. Revoked tokens that expired AND were revoked > 90 days ago (retain for audit)
|
|
55
|
+
const stmt = this.tokenRepo['db'].prepare(`
|
|
56
|
+
DELETE FROM tokens
|
|
57
|
+
WHERE datetime(expires_at) <= datetime('now')
|
|
58
|
+
AND (
|
|
59
|
+
revoked_at IS NULL
|
|
60
|
+
OR datetime(revoked_at) <= datetime('now', '-90 days')
|
|
61
|
+
)
|
|
62
|
+
`);
|
|
63
|
+
|
|
64
|
+
const result = stmt.run();
|
|
65
|
+
const deletedCount = result.changes;
|
|
66
|
+
|
|
67
|
+
if (deletedCount > 0) {
|
|
68
|
+
logInfo('Deleted expired tokens', { count: deletedCount });
|
|
69
|
+
} else {
|
|
70
|
+
logInfo('No expired tokens to delete');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return deletedCount;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get cleanup statistics without deleting anything
|
|
78
|
+
* Useful for monitoring and alerting
|
|
79
|
+
* @returns Cleanup statistics
|
|
80
|
+
*/
|
|
81
|
+
getStatistics(): CleanupStatistics {
|
|
82
|
+
const db = this.tokenRepo['db'];
|
|
83
|
+
|
|
84
|
+
// Count expired non-revoked tokens (immediate deletion candidates)
|
|
85
|
+
const expiredNonRevoked = db
|
|
86
|
+
.prepare(`
|
|
87
|
+
SELECT COUNT(*) as count FROM tokens
|
|
88
|
+
WHERE datetime(expires_at) <= datetime('now')
|
|
89
|
+
AND revoked_at IS NULL
|
|
90
|
+
`)
|
|
91
|
+
.get() as { count: number };
|
|
92
|
+
|
|
93
|
+
// Count old revoked expired tokens (deletion candidates after 90 days)
|
|
94
|
+
const oldRevokedExpired = db
|
|
95
|
+
.prepare(`
|
|
96
|
+
SELECT COUNT(*) as count FROM tokens
|
|
97
|
+
WHERE datetime(expires_at) <= datetime('now')
|
|
98
|
+
AND revoked_at IS NOT NULL
|
|
99
|
+
AND datetime(revoked_at) <= datetime('now', '-90 days')
|
|
100
|
+
`)
|
|
101
|
+
.get() as { count: number };
|
|
102
|
+
|
|
103
|
+
// Count recent revoked expired tokens (retained for audit, < 90 days)
|
|
104
|
+
const recentRevokedExpired = db
|
|
105
|
+
.prepare(`
|
|
106
|
+
SELECT COUNT(*) as count FROM tokens
|
|
107
|
+
WHERE datetime(expires_at) <= datetime('now')
|
|
108
|
+
AND revoked_at IS NOT NULL
|
|
109
|
+
AND datetime(revoked_at) > datetime('now', '-90 days')
|
|
110
|
+
`)
|
|
111
|
+
.get() as { count: number };
|
|
112
|
+
|
|
113
|
+
const totalDeletionCandidates = expiredNonRevoked.count + oldRevokedExpired.count;
|
|
114
|
+
|
|
115
|
+
const stats: CleanupStatistics = {
|
|
116
|
+
totalTokens: this.tokenRepo.count(),
|
|
117
|
+
activeTokens: this.tokenRepo.countActive(),
|
|
118
|
+
expiredNonRevoked: expiredNonRevoked.count,
|
|
119
|
+
oldRevokedExpired: oldRevokedExpired.count,
|
|
120
|
+
recentRevokedExpired: recentRevokedExpired.count,
|
|
121
|
+
totalDeletionCandidates,
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
logInfo('Token cleanup statistics', stats as unknown as Record<string, unknown>);
|
|
125
|
+
|
|
126
|
+
return stats;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Run cleanup job and return statistics
|
|
131
|
+
* Combines cleanup and reporting in one operation
|
|
132
|
+
* @returns Cleanup result with statistics
|
|
133
|
+
*/
|
|
134
|
+
runWithStatistics(): CleanupResult {
|
|
135
|
+
const statsBefore = this.getStatistics();
|
|
136
|
+
const deletedCount = this.run();
|
|
137
|
+
const statsAfter = this.getStatistics();
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
deletedCount,
|
|
141
|
+
statsBefore,
|
|
142
|
+
statsAfter,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Token cleanup statistics
|
|
149
|
+
*/
|
|
150
|
+
export interface CleanupStatistics {
|
|
151
|
+
totalTokens: number;
|
|
152
|
+
activeTokens: number;
|
|
153
|
+
expiredNonRevoked: number; // Immediate deletion candidates
|
|
154
|
+
oldRevokedExpired: number; // Deletion candidates after 90 days
|
|
155
|
+
recentRevokedExpired: number; // Retained for audit (< 90 days)
|
|
156
|
+
totalDeletionCandidates: number; // Sum of immediate + old revoked
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Cleanup job result
|
|
161
|
+
*/
|
|
162
|
+
export interface CleanupResult {
|
|
163
|
+
deletedCount: number;
|
|
164
|
+
statsBefore: CleanupStatistics;
|
|
165
|
+
statsAfter: CleanupStatistics;
|
|
166
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
-- Initial Schema Migration
|
|
2
|
+
-- Creates tokens table for JWT token management with constraints and views
|
|
3
|
+
|
|
4
|
+
-- Tokens table
|
|
5
|
+
CREATE TABLE IF NOT EXISTS tokens (
|
|
6
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
7
|
+
jti TEXT UNIQUE NOT NULL,
|
|
8
|
+
public_key TEXT NOT NULL,
|
|
9
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
10
|
+
expires_at TEXT NOT NULL,
|
|
11
|
+
max_uses INTEGER NOT NULL DEFAULT 0,
|
|
12
|
+
used_count INTEGER NOT NULL DEFAULT 0,
|
|
13
|
+
revoked_at TEXT,
|
|
14
|
+
revoked_by TEXT,
|
|
15
|
+
revoked_reason TEXT,
|
|
16
|
+
metadata TEXT,
|
|
17
|
+
CONSTRAINT check_usage CHECK (used_count <= max_uses OR max_uses = 0),
|
|
18
|
+
CONSTRAINT check_dates CHECK (datetime(expires_at) > datetime(created_at))
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
-- Indexes for performance
|
|
22
|
+
CREATE INDEX IF NOT EXISTS idx_tokens_jti ON tokens(jti);
|
|
23
|
+
CREATE INDEX IF NOT EXISTS idx_tokens_expires_at ON tokens(expires_at);
|
|
24
|
+
CREATE INDEX IF NOT EXISTS idx_tokens_revoked_at ON tokens(revoked_at);
|
|
25
|
+
CREATE INDEX IF NOT EXISTS idx_tokens_created_at ON tokens(created_at);
|
|
26
|
+
|
|
27
|
+
-- Partial index for active tokens (optimization)
|
|
28
|
+
CREATE INDEX IF NOT EXISTS idx_tokens_expired
|
|
29
|
+
ON tokens(expires_at)
|
|
30
|
+
WHERE revoked_at IS NULL;
|
|
31
|
+
|
|
32
|
+
-- View for active tokens
|
|
33
|
+
CREATE VIEW IF NOT EXISTS v_active_tokens AS
|
|
34
|
+
SELECT
|
|
35
|
+
id,
|
|
36
|
+
jti,
|
|
37
|
+
created_at,
|
|
38
|
+
expires_at,
|
|
39
|
+
max_uses,
|
|
40
|
+
used_count,
|
|
41
|
+
CASE
|
|
42
|
+
WHEN max_uses = 0 THEN -1
|
|
43
|
+
ELSE (max_uses - used_count)
|
|
44
|
+
END AS remaining_uses
|
|
45
|
+
FROM tokens
|
|
46
|
+
WHERE revoked_at IS NULL
|
|
47
|
+
AND datetime(expires_at) > datetime('now');
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { readFileSync, readdirSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import Database from 'better-sqlite3';
|
|
4
|
+
import { logInfo, logError } from '../../utils/logger';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Migration Runner
|
|
8
|
+
* Executes SQL migration files sequentially with version tracking
|
|
9
|
+
*/
|
|
10
|
+
export class MigrationRunner {
|
|
11
|
+
constructor(private db: Database.Database) {}
|
|
12
|
+
|
|
13
|
+
async run(): Promise<void> {
|
|
14
|
+
try {
|
|
15
|
+
logInfo('Starting database migrations');
|
|
16
|
+
|
|
17
|
+
// Create migrations tracking table
|
|
18
|
+
this.createMigrationsTable();
|
|
19
|
+
|
|
20
|
+
// Get current version
|
|
21
|
+
const currentVersion = this.getCurrentVersion();
|
|
22
|
+
logInfo('Current migration version', { version: currentVersion });
|
|
23
|
+
|
|
24
|
+
// Find all migration files
|
|
25
|
+
const migrations = this.findMigrationFiles();
|
|
26
|
+
|
|
27
|
+
// Apply pending migrations
|
|
28
|
+
let appliedCount = 0;
|
|
29
|
+
for (const migration of migrations) {
|
|
30
|
+
if (migration.version > currentVersion) {
|
|
31
|
+
this.applyMigration(migration);
|
|
32
|
+
appliedCount++;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (appliedCount === 0) {
|
|
37
|
+
logInfo('No pending migrations');
|
|
38
|
+
} else {
|
|
39
|
+
logInfo('Migrations completed', { appliedCount });
|
|
40
|
+
}
|
|
41
|
+
} catch (error) {
|
|
42
|
+
logError('Migration failed', error as Error);
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private createMigrationsTable(): void {
|
|
48
|
+
this.db.exec(`
|
|
49
|
+
CREATE TABLE IF NOT EXISTS migrations (
|
|
50
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
51
|
+
version INTEGER UNIQUE NOT NULL,
|
|
52
|
+
name TEXT NOT NULL,
|
|
53
|
+
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
54
|
+
);
|
|
55
|
+
`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private getCurrentVersion(): number {
|
|
59
|
+
const result = this.db
|
|
60
|
+
.prepare('SELECT COALESCE(MAX(version), 0) as version FROM migrations')
|
|
61
|
+
.get() as { version: number };
|
|
62
|
+
return result.version;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private findMigrationFiles(): Array<{ version: number; name: string; path: string }> {
|
|
66
|
+
const migrationsDir = __dirname;
|
|
67
|
+
const files = readdirSync(migrationsDir).filter((f) => f.endsWith('.sql'));
|
|
68
|
+
|
|
69
|
+
return files
|
|
70
|
+
.map((file) => ({
|
|
71
|
+
version: this.getMigrationVersion(file),
|
|
72
|
+
name: file,
|
|
73
|
+
path: join(migrationsDir, file),
|
|
74
|
+
}))
|
|
75
|
+
.sort((a, b) => a.version - b.version);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private getMigrationVersion(filename: string): number {
|
|
79
|
+
const match = filename.match(/^(\d+)_/);
|
|
80
|
+
if (!match || !match[1]) {
|
|
81
|
+
throw new Error(`Invalid migration filename: ${filename}`);
|
|
82
|
+
}
|
|
83
|
+
return parseInt(match[1], 10);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private applyMigration(migration: { version: number; name: string; path: string }): void {
|
|
87
|
+
logInfo('Applying migration', { name: migration.name, version: migration.version });
|
|
88
|
+
|
|
89
|
+
// Wrap in transaction for atomicity
|
|
90
|
+
const apply = this.db.transaction(() => {
|
|
91
|
+
try {
|
|
92
|
+
// Read and execute SQL
|
|
93
|
+
const sql = readFileSync(migration.path, 'utf-8');
|
|
94
|
+
this.db.exec(sql);
|
|
95
|
+
|
|
96
|
+
// Record migration
|
|
97
|
+
this.db
|
|
98
|
+
.prepare('INSERT INTO migrations (version, name) VALUES (?, ?)')
|
|
99
|
+
.run(migration.version, migration.name);
|
|
100
|
+
|
|
101
|
+
logInfo('Migration applied successfully', { name: migration.name });
|
|
102
|
+
} catch (error) {
|
|
103
|
+
logError('Migration failed', error as Error, { name: migration.name });
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
apply();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Static helper for running migrations from config
|
|
113
|
+
*/
|
|
114
|
+
static async runMigrations(dbPath: string): Promise<void> {
|
|
115
|
+
const Database = (await import('better-sqlite3')).default;
|
|
116
|
+
const db = new Database(dbPath);
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
const runner = new MigrationRunner(db);
|
|
120
|
+
await runner.run();
|
|
121
|
+
} finally {
|
|
122
|
+
db.close();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token Model
|
|
3
|
+
* Represents a JWT token in the database
|
|
4
|
+
*/
|
|
5
|
+
export interface Token {
|
|
6
|
+
id: number;
|
|
7
|
+
jti: string;
|
|
8
|
+
public_key: string;
|
|
9
|
+
created_at: string;
|
|
10
|
+
expires_at: string;
|
|
11
|
+
max_uses: number;
|
|
12
|
+
used_count: number;
|
|
13
|
+
revoked_at: string | null;
|
|
14
|
+
revoked_by: string | null;
|
|
15
|
+
revoked_reason: string | null;
|
|
16
|
+
metadata: string | null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface TokenCreateInput {
|
|
20
|
+
jti: string;
|
|
21
|
+
public_key: string;
|
|
22
|
+
expires_at: string;
|
|
23
|
+
max_uses?: number;
|
|
24
|
+
metadata?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ActiveToken {
|
|
28
|
+
id: number;
|
|
29
|
+
jti: string;
|
|
30
|
+
created_at: string;
|
|
31
|
+
expires_at: string;
|
|
32
|
+
max_uses: number;
|
|
33
|
+
used_count: number;
|
|
34
|
+
remaining_uses: number;
|
|
35
|
+
}
|
|
File without changes
|