diff --git a/package.json b/package.json index 7b887ef..e2deb46 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "crypto-pro", - "version": "2.2.0", + "version": "2.2.1", "description": "API для взаимодействия с КриптоПро", "main": "./lib/crypto-pro.js", "types": "./lib/crypto-pro.d.ts", diff --git a/src/api/getAllUserCertificates.test.ts b/src/api/getAllUserCertificates.test.ts new file mode 100644 index 0000000..eabac29 --- /dev/null +++ b/src/api/getAllUserCertificates.test.ts @@ -0,0 +1,68 @@ +import 'cadesplugin'; +import { rawCertificates, parsedCertificates } from '../__mocks__/certificates'; +import { getAllUserCertificates } from './getAllUserCertificates'; + +const [rawCertificateMock] = rawCertificates; +const [parsedCertificateMock] = parsedCertificates; + +const executionSteps = [ + Symbol('step 0'), + Symbol('step 1'), + Symbol('step 2'), + Symbol('step 3'), + Symbol('step 4'), + Symbol('step 5'), + Symbol('step 6'), + Symbol('step 7'), + Symbol('step 8'), + Symbol('step 9'), + Symbol('step 10'), +]; + +const executionFlow = { + [executionSteps[0]]: { + Certificates: executionSteps[1], + Close: jest.fn(), + Open: jest.fn(), + }, + [executionSteps[1]]: { + Find: jest.fn(() => executionSteps[2]), + }, + [executionSteps[2]]: { + Find: jest.fn(() => executionSteps[3]), + }, + [executionSteps[3]]: { + Count: executionSteps[4], + Item: jest.fn(() => executionSteps[5]), + }, + [executionSteps[4]]: 1, + [executionSteps[5]]: { + IssuerName: executionSteps[8], + SubjectName: executionSteps[7], + Thumbprint: executionSteps[6], + ValidFromDate: executionSteps[9], + ValidToDate: executionSteps[10], + }, + [executionSteps[8]]: rawCertificateMock.IssuerName, + [executionSteps[7]]: rawCertificateMock.SubjectName, + [executionSteps[6]]: rawCertificateMock.Thumbprint, + [executionSteps[9]]: rawCertificateMock.ValidFromDate, + [executionSteps[10]]: rawCertificateMock.ValidToDate, +}; + +window.cadesplugin.__defineExecutionFlow(executionFlow); +window.cadesplugin.CreateObjectAsync.mockImplementation(() => executionSteps[0]); + +describe('getUserCertificates', () => { + test('returns certificates list', async () => { + const certificates = await getAllUserCertificates(); + + expect(certificates.length).toBeGreaterThan(0); + }); + + test('returns certificates with correct fields', async () => { + const [certificate] = await getAllUserCertificates(); + + expect(certificate).toMatchObject(parsedCertificateMock); + }); +}); diff --git a/src/api/getAllUserCertificates.ts b/src/api/getAllUserCertificates.ts new file mode 100644 index 0000000..b113123 --- /dev/null +++ b/src/api/getAllUserCertificates.ts @@ -0,0 +1,99 @@ +import { CadesCertificate, Certificate } from './certificate'; +import { _afterPluginsLoaded } from '../helpers/_afterPluginsLoaded'; +import { _extractCommonName } from '../helpers/_extractCommonName'; +import { _extractMeaningfulErrorMessage } from '../helpers/_extractMeaningfulErrorMessage'; +import { __cadesAsyncToken__, __createCadesPluginObject__, _generateCadesFn } from '../helpers/_generateCadesFn'; + +let certificatesCache: Certificate[]; + +/** + * Возвращает все сертификаты без фильтрации по дате и наличию приватного ключа + * + * @param resetCache = false - позволяет сбросить кэш ранее полученных сертификатов + * @returns список сертификатов + */ +export const getAllUserCertificates = _afterPluginsLoaded((resetCache: boolean = false): Certificate[] => { + const { cadesplugin } = window; + + if (!resetCache && certificatesCache) { + return certificatesCache; + } + + return eval( + _generateCadesFn(function getUserCertificates(): Certificate[] { + let cadesStore; + + try { + cadesStore = __cadesAsyncToken__ + __createCadesPluginObject__('CAdESCOM.Store'); + } catch (error) { + console.error(error); + + throw new Error(_extractMeaningfulErrorMessage(error) || 'Ошибка при попытке доступа к хранилищу'); + } + + try { + void ( + __cadesAsyncToken__ + + cadesStore.Open( + cadesplugin.CAPICOM_CURRENT_USER_STORE, + cadesplugin.CAPICOM_MY_STORE, + cadesplugin.CAPICOM_STORE_OPEN_MAXIMUM_ALLOWED, + ) + ); + } catch (error) { + console.error(error); + + throw new Error(_extractMeaningfulErrorMessage(error) || 'Ошибка при открытии хранилища'); + } + + let cadesCertificates; + let cadesCertificatesCount; + + try { + cadesCertificates = __cadesAsyncToken__ + cadesStore.Certificates; + cadesCertificatesCount = __cadesAsyncToken__ + cadesCertificates.Count; + } catch (error) { + console.error(error); + + throw new Error(_extractMeaningfulErrorMessage(error) || 'Ошибка получения списка сертификатов'); + } + + if (!cadesCertificatesCount) { + throw new Error('Нет доступных сертификатов'); + } + + const certificateList: Certificate[] = []; + + try { + while (cadesCertificatesCount) { + const cadesCertificate: CadesCertificate = + __cadesAsyncToken__ + cadesCertificates.Item(cadesCertificatesCount); + + certificateList.push( + new Certificate( + cadesCertificate, + _extractCommonName(__cadesAsyncToken__ + cadesCertificate.SubjectName), + __cadesAsyncToken__ + cadesCertificate.IssuerName, + __cadesAsyncToken__ + cadesCertificate.SubjectName, + __cadesAsyncToken__ + cadesCertificate.Thumbprint, + __cadesAsyncToken__ + cadesCertificate.ValidFromDate, + __cadesAsyncToken__ + cadesCertificate.ValidToDate, + ), + ); + + cadesCertificatesCount--; + } + } catch (error) { + console.error(error); + + throw new Error(_extractMeaningfulErrorMessage(error) || 'Ошибка обработки сертификатов'); + } + + cadesStore.Close(); + + certificatesCache = certificateList; + + return certificatesCache; + }), + ); +});