367 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React from "react";
import Link from "next/link";
import { SpinnerCircular } from "spinners-react";
import moment from "moment";
import dynamic from 'next/dynamic';
import { concatSeries } from 'async';
import fileDownload from 'js-file-download';
import * as Sentry from "@sentry/nextjs";
import FormMessage from "./FormMessage";
import { downloadQuestionnaire, uploadSignedFile } from "../../../actions";
//import { generateSignature } from "../../../utils/digital_signature";
export default class DigitalCertificates extends React.Component
{
constructor(props)
{
super(props);
this.state = {
loading: true,
certificates: [],
certificates_error: null,
certificate_selected: undefined,
signing: false,
show_all: false,
};
this.cryptopro = null;
}
async componentDidMount()
{
const { company, main, head_person } = this.props;
//console.log({ company, main });
//console.log("DC", "CDM");
const { CryptoPro } = await import('ruscryptojs');
this.cryptopro = new CryptoPro();
this.cryptopro.init()
.then((info) =>
{
//console.log('Initialized', info);
// console.log('-'.repeat(20));
// for(let i in this.cryptopro)
// {
// console.log(i);
// }
// console.log('-'.repeat(20));
this.cryptopro.listCertificates()
.then((certificates_list) =>
{
console.log({ certificates_list });
console.log("all certificates", JSON.stringify(certificates_list));
Sentry.captureMessage("Client digital sign step, list of all digital certificates");
concatSeries(certificates_list, (certificate, callback) =>
{
//console.log(certificate.id, certificate.name);
this.cryptopro.certificateInfo(certificate.id)
.then((cert) =>
{
const errors = [];
let apply = true;
console.log("certificate", JSON.stringify(cert));
console.log("company", JSON.stringify(company));
//console.log({ cert });
if(cert.Subject['OGRNIP'] !== undefined && cert.Subject['OGRNIP'] !== null && cert.Subject['OGRNIP'] !== "")
{
if(cert.Subject['INN'] !== undefined && cert.Subject['INN'] !== null && cert.Subject['INN'] !== "")
{
if(main.inn !== cert.Subject['INN'])
{
errors.push('inn');
}
}
else
{
apply = false;
}
}
else
{
if(cert.Subject['INNLE'] !== undefined && cert.Subject['INNLE'] !== null && cert.Subject['INNLE'] !== "")
{
if(main.inn !== cert.Subject['INNLE'])
{
errors.push('inn');
}
}
else
{
apply = false;
}
}
let owner_valid = true;
const fields = [ "lastname", "firstname", "middlename", ];
const cert_owner = `${ cert.Subject['SN']} ${ cert.Subject['G']}`.toUpperCase();
for(let i in fields)
{
if(head_person[fields[i]] !== null && head_person[fields[i]] !== "")
{
if(cert_owner.indexOf(head_person[fields[i]].toUpperCase().trim()) < 0)
{
owner_valid = false;
}
}
}
if(!owner_valid)
{
errors.push('name');
}
if(cert.ValidToDate !== null && cert.ValidToDate !== undefined && cert.ValidToDate !== "")
{
if(moment(cert.ValidToDate) < moment())
{
errors.push('date');
}
}
else
{
errors.push('date');
}
if(!cert.IsValid)
{
errors.push('invalid');
}
if(apply)
{
callback(null, [ { id: certificate.id, fields: cert.Subject, valid_to_date: moment(cert.ValidToDate).format("DD.MM.YYYY"), errors } ]);
}
else
{
callback(null, []);
}
})
.catch((error_certificates_info) =>
{
Sentry.captureMessage("Client digital sign step error");
console.error({ error_certificates_info });
this.setState({ loading: false, certificates_error: "CERTIFICATES" });
});
}, (error, certificates) =>
{
console.log("applicable certificates", JSON.stringify(certificates));
Sentry.captureMessage("Client digital sign step, list of applicable digital certificates");
if(certificates.length === 0)
{
this.setState({ loading: false, certificates_error: "INVALID" });
}
else
{
const list = [];
let valid_certificate_exists = false;
for(let i in certificates) { if(certificates[i].errors.length === 0) { list.push(certificates[i]); valid_certificate_exists = true; } }
for(let i in certificates) { if(certificates[i].errors.length !== 0) { list.push(certificates[i]); } }
const update = { loading: false, certificates: list, certificates_error: null };
if(list[0] !== undefined)
{
if(list[0].errors.length === 0)
{
update.certificate_selected = list[0];
}
}
if(!valid_certificate_exists)
{
update.certificates_error = "INVALID";
}
this.setState(update);
}
});
})
.catch((error_certificates_list) =>
{
console.error({ error_certificates_list });
this.setState({ loading: false, certificates_error: "CERTIFICATES" });
});
})
.catch((error_init) =>
{
console.error({ error_init });
this.setState({ loading: false, certificates_error: "NOT_INSTALLED" });
});
}
_sign = () =>
{
this.setState({ signing: true }, () =>
{
const { company, main, onSignDigital } = this.props;
const filename = `${ main.inn }_questionnaire_${ moment().format("DDMMYYYY_HHmmss") }.sig`;
downloadQuestionnaire({ filename, download: false, base64: true })
.then(({ file }) =>
{
let file_to_sign = file;
const now = moment().format("DDMMYYYY_HHmmss");
this.cryptopro.signData(file_to_sign, this.state.certificate_selected.id)
.then(signature =>
{
//console.log({ signature });
let signature_file = new File([ signature ], filename);
uploadSignedFile(signature_file, company.questionnaire_id, true)
.then(() =>
{
onSignDigital();
})
.catch(() =>
{
this.setState({ signing: false });
});
})
.catch((error_sign) =>
{
console.error({ error_sign });
this.setState({ signing: false });
})
})
.catch((e) =>
{
console.error("exception on sign");
console.error(e);
});
});
}
render()
{
const { company, main, head_person } = this.props;
const { loading, certificates, certificates_error, certificate_selected, signing, show_all } = this.state;
//console.log("render()", { certificates });
console.log({ certificates_error });
if(loading)
{
return (
<div style={{ minHeight: 200, display: "flex", justifyContent: "center", alignItems: "center", }}>
<SpinnerCircular size={ 90 } thickness={ 51 } speed={ 100 } color="rgba(28, 1, 169, 1)" secondaryColor="rgba(236, 239, 244, 1)" />
</div>
);
}
else
{
return (
<>
{ certificates_error !== null && (
<>
{ (certificates_error === "NOT_INSTALLED" || certificates_error === "CERTIFICATES") && (
<FormMessage type="moderate" title="Внимание!" message={ <>
Плагин КриптоПРО не установлен или не активирован. Посмотите <a style={{ color: "#fff", textDecoration: "underline", }} href={ `${ process.env.NEXT_PUBLIC_MAIN_SITE }/upload/docs/anketa-sign.pdf` }>инструкцию</a> как установить, активировать и проверить работу плагина КриптоПро.<br/>
<br/>
После активации плагина, пожалуйста, обновите страницу.
</> }/>
) }
{ certificates_error === "INVALID" && (
<FormMessage type="moderate" title="Внимание!" message={ <>
Анкету необходимо подписать по ЭЦП сертификатом юридического лица с ИНН: <br/>
{ main.inn }, выданного: { head_person.lastname } { head_person.firstname } { head_person.middlename }. <br/>
Такой сертификат не найден!<br/><br/>
Если Вы уверены в наличии такого сертификата:
<ol style={{ listStyle: "decimal", padding: "revert" }}>
<li>Проверьте, вставлен ли ключ с этим сертификатом.</li>
<li>Проверьте возможность подписания этим сертификатом с помощью средства <a style={{ color: "#fff", textDecoration: "underline", }} href="https://cryptopro.ru/sites/default/files/products/cades/demopage/cades_bes_sample.html">проверки КриптоПро</a>.</li>
<li>Возможно, сертификат нужно установить. <a style={{ color: "#fff", textDecoration: "underline", }} href={ `${ process.env.NEXT_PUBLIC_MAIN_SITE }/upload/docs/anketa-sign.pdf` }>Воспользуйтесь инструкцией</a>.</li>
</ol>
</> }/>
) }
{ certificates_error === "MISMATCH" && (
<FormMessage type="moderate" title="Внимание!" message={ <>Подписант не соответствует указанному подписанту в анкете.</> }/>
) }
</>
)
}
{ certificates.length > 0 && (
<>
<div className="feed">
<p>Список сертификатов, доступных на Вашем компьютере:</p>
<div className="feed_list">
{ certificates.slice(0, show_all ? certificates.length : 2).map((certificate, index) => (
<div className={`form_field checkbox digital_certificates_status ${ certificate.errors.length > 0 ? "error" : "" } ${ certificates.length > 1 && !show_all && index === 1 ? "show_all_holder" : ""}`} key={ index }>
<input type="radio"
hidden=""
id={ `certificate_${ certificate.id }` }
name={ `certificate_${ certificate.id }` }
checked={ certificate_selected !== undefined && certificate_selected.id === certificate.id ? true : false }
onChange={ () => { this.setState({ certificate_selected: certificate }) } }
/>
<label htmlFor={ `certificate_${ certificate.id }` }>
<div className="feed_item user">
<img src="/assets/images/icons/avatar.svg" alt="" />
<div>
<p className="item_title">{ certificate.fields['CN'].replace(/"/g, '') }</p>
<p className="item_desc">
{ certificate.fields['SN'] ? (<span>{ certificate.fields['SN'] } { certificate.fields['G'] }</span>) : null }
</p>
<p className="item_desc">
<span>ИНН { certificate.fields['INNLE'] !== undefined && certificate.fields['INNLE'] !== null ? certificate.fields['INNLE'] : certificate.fields['INN'] }</span>
</p>
<p className="item_desc">
{ certificate.fields['OGRNIP'] && (<span>ОГРНИП { certificate.fields['OGRNIP'] }</span>) }
{ certificate.fields['OGRN'] && (<span>ОГРН { certificate.fields['OGRN'] }</span>) }
</p>
<p className="item_desc">
<span>Подпись действительна до { certificate.valid_to_date }</span>
</p>
</div>
{ certificate.errors.length > 0 && (
<div>
<p className="item_title">&nbsp;</p>
{ certificate.errors.indexOf("name") > -1 ? <p className="item_desc" style={{ color: "#A8026B" }}>Не соответствует ФИО</p> : <p className="item_desc">&nbsp;</p>}
{ certificate.errors.indexOf("inn") > -1 ? <p className="item_desc" style={{ color: "#A8026B" }}>Не соответствует ИНН</p> : <p className="item_desc">&nbsp;</p>}
<p className="item_desc">&nbsp;</p>
{ certificate.errors.indexOf("date") > -1 ? <p className="item_desc" style={{ color: "#A8026B" }}>{ certificate.errors.indexOf('date') > 1 ? "Срок действия закончился" : certificate.errors.indexOf('invalid') > 1 ? "Сертификат недействителен или нарушена цепочка сертификации" : null }</p> : <p className="item_desc">&nbsp;</p>}
</div>
) }
</div>
</label>
{ certificates.length > 1 && !show_all && index === 1 && (
<div className="show_all" onClick={ () => this.setState({ show_all: true }) }><span>Посмотреть все</span></div>
) }
</div>
)) }
</div>
</div>
<div className="action" style={{marginBottom: "20px"}}>
<div></div>
<button
type="submit"
className="button button-blue"
disabled={ certificate_selected !== null && certificate_selected !== undefined ? false : true }
onClick={ this._sign }
style={{ minWidth: "250px" }}
>
{ signing ? (
<SpinnerCircular size={24} thickness={100} speed={100} color="rgba(255, 255, 255, 1)" secondaryColor="rgba(255, 255, 255, 0.5)" style={{ marginTop: "4px" }}/>
) : "Подписать выбранной подписью" }
</button>
</div>
</>
) }
</>
)
}
}
}