Agregar certificado
This commit is contained in:
parent
361aa131d3
commit
f525ced698
|
@ -1,7 +1,9 @@
|
|||
falcon
|
||||
falcon-multipart
|
||||
Beaker
|
||||
Mako
|
||||
peewee
|
||||
click
|
||||
logbook
|
||||
bcrypt
|
||||
python-dateutil
|
||||
|
|
|
@ -70,6 +70,17 @@ class AppValues(object):
|
|||
req.context['result'] = self._db.get_values(table, values)
|
||||
resp.status = falcon.HTTP_200
|
||||
|
||||
def on_post(self, req, resp, table):
|
||||
file_object = req.get_param('upload')
|
||||
if file_object is None:
|
||||
session = req.env['beaker.session']
|
||||
values = req.params
|
||||
req.context['result'] = self._db.validate_cert(values, session)
|
||||
else:
|
||||
req.context['result'] = self._db.add_cert(file_object)
|
||||
resp.status = falcon.HTTP_200
|
||||
|
||||
|
||||
|
||||
class AppPartners(object):
|
||||
|
||||
|
|
|
@ -2,17 +2,20 @@
|
|||
|
||||
import datetime
|
||||
import getpass
|
||||
import hashlib
|
||||
import json
|
||||
import mimetypes
|
||||
import os
|
||||
import re
|
||||
import sqlite3
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unicodedata
|
||||
import uuid
|
||||
|
||||
#~ import bcrypt
|
||||
from dateutil import parser
|
||||
|
||||
from settings import log, template_lookup, COMPANIES, DB_SAT
|
||||
from settings import DEBUG, log, template_lookup, COMPANIES, DB_SAT
|
||||
|
||||
|
||||
#~ def _get_hash(password):
|
||||
|
@ -23,6 +26,17 @@ from settings import log, template_lookup, COMPANIES, DB_SAT
|
|||
#~ return bcrypt.hashpw(password.encode(), hashed.encode()) == hashed.encode()
|
||||
|
||||
|
||||
def _call(args):
|
||||
return subprocess.check_output(args, shell=True).decode()
|
||||
|
||||
|
||||
def _save_temp(data, modo='wb'):
|
||||
path = tempfile.mkstemp()[1]
|
||||
with open(path, modo) as f:
|
||||
f.write(data)
|
||||
return path
|
||||
|
||||
|
||||
def get_pass():
|
||||
password = getpass.getpass('Introduce la contraseña: ')
|
||||
pass2 = getpass.getpass('Confirma la contraseña: ')
|
||||
|
@ -177,3 +191,148 @@ def to_slug(string):
|
|||
.encode('ascii', 'ignore')
|
||||
.decode('ascii').lower())
|
||||
return value
|
||||
|
||||
|
||||
class Certificado(object):
|
||||
|
||||
def __init__(self, key, cer):
|
||||
self._key = key
|
||||
self._cer = cer
|
||||
self._modulus = ''
|
||||
self._save_files()
|
||||
self.error = ''
|
||||
|
||||
def _save_files(self):
|
||||
try:
|
||||
self._path_key = _save_temp(self._key)
|
||||
self._path_cer = _save_temp(self._cer)
|
||||
except:
|
||||
self._path_key = ''
|
||||
self._path_cer = ''
|
||||
return
|
||||
|
||||
def _kill(self, path):
|
||||
try:
|
||||
os.remove(path)
|
||||
except:
|
||||
pass
|
||||
return
|
||||
|
||||
def _get_info_cer(self, session_rfc):
|
||||
data = {}
|
||||
args = 'openssl x509 -inform DER -in {}'
|
||||
try:
|
||||
cer_pem = _call(args.format(self._path_cer))
|
||||
except Exception as e:
|
||||
self.error = 'No se pudo convertir el CER en PEM'
|
||||
return data
|
||||
|
||||
args = 'openssl enc -base64 -in {}'
|
||||
try:
|
||||
cer_txt = _call(args.format(self._path_cer))
|
||||
except Exception as e:
|
||||
self.error = 'No se pudo convertir el CER en TXT'
|
||||
return data
|
||||
|
||||
args = 'openssl x509 -inform DER -in {} -noout -{}'
|
||||
try:
|
||||
result = _call(args.format(self._path_cer, 'purpose')).split('\n')[3]
|
||||
except Exception as e:
|
||||
self.error = 'No se puede saber si es FIEL'
|
||||
return data
|
||||
|
||||
if result == 'SSL server : No':
|
||||
self.error = 'El certificado es FIEL'
|
||||
return data
|
||||
|
||||
result = _call(args.format(self._path_cer, 'serial'))
|
||||
serie = result.split('=')[1].split('\n')[0][1::2]
|
||||
result = _call(args.format(self._path_cer, 'subject'))
|
||||
rfc = result.split('=')[5].split('/')[0].strip()
|
||||
|
||||
if not DEBUG:
|
||||
if not rfc == session_rfc:
|
||||
self.error = 'El RFC del certificado no corresponde.'
|
||||
return data
|
||||
|
||||
dates = _call(args.format(self._path_cer, 'dates')).split('\n')
|
||||
desde = parser.parse(dates[0].split('=')[1])
|
||||
hasta = parser.parse(dates[1].split('=')[1])
|
||||
self._modulus = _call(args.format(self._path_cer, 'modulus'))
|
||||
|
||||
data['cer'] = self._cer
|
||||
data['cer_tmp'] = None
|
||||
data['cer_pem'] = cer_pem
|
||||
data['cer_txt'] = cer_txt.replace('\n', '')
|
||||
data['serie'] = serie
|
||||
data['rfc'] = rfc
|
||||
data['desde'] = desde
|
||||
data['hasta'] = hasta
|
||||
return data
|
||||
|
||||
def _get_p12(self, password, rfc):
|
||||
tmp_cer = tempfile.mkstemp()[1]
|
||||
tmp_key = tempfile.mkstemp()[1]
|
||||
tmp_p12 = tempfile.mkstemp()[1]
|
||||
|
||||
args = 'openssl x509 -inform DER -in "{}" -out "{}"'
|
||||
_call(args.format(self._path_cer, tmp_cer))
|
||||
args = 'openssl pkcs8 -inform DER -in "{}" -passin pass:{} -out "{}"'
|
||||
_call(args.format(self._path_key, password, tmp_key))
|
||||
|
||||
args = 'openssl pkcs12 -export -in "{}" -inkey "{}" -name "{}" -passout ' \
|
||||
'pass:"{}" -out "{}"'
|
||||
_call(args.format(tmp_cer, tmp_key, rfc,
|
||||
hashlib.md5(rfc.encode()).hexdigest(), tmp_p12))
|
||||
data = open(tmp_p12, 'rb').read()
|
||||
|
||||
self._kill(tmp_cer)
|
||||
self._kill(tmp_key)
|
||||
self._kill(tmp_p12)
|
||||
|
||||
return data
|
||||
|
||||
def _get_info_key(self, password, rfc):
|
||||
data = {}
|
||||
|
||||
args = 'openssl pkcs8 -inform DER -in "{}" -passin pass:{}'
|
||||
try:
|
||||
result = _call(args.format(self._path_key, password))
|
||||
except Exception as e:
|
||||
self.error = 'Contraseña incorrecta'
|
||||
return data
|
||||
|
||||
args = 'openssl pkcs8 -inform DER -in "{}" -passin pass:{} | ' \
|
||||
'openssl rsa -noout -modulus'
|
||||
mod_key = _call(args.format(self._path_key, password))
|
||||
|
||||
if self._modulus != mod_key:
|
||||
self.error = 'Los archivos no son pareja'
|
||||
return data
|
||||
|
||||
args = 'openssl pkcs8 -inform DER -in "{}" -passin pass:{} | ' \
|
||||
'openssl rsa -des3 -passout pass:{}'.format(
|
||||
self._path_key, password, hashlib.md5(rfc.encode()).hexdigest())
|
||||
key_enc = _call(args)
|
||||
|
||||
data['key'] = self._key
|
||||
data['key_tmp'] = None
|
||||
data['key_enc'] = key_enc
|
||||
data['p12'] = self._get_p12(password, rfc)
|
||||
return data
|
||||
|
||||
def validate(self, password, rfc):
|
||||
if not self._path_key or not self._path_cer:
|
||||
self.error = 'Error al cargar el certificado'
|
||||
return {}
|
||||
|
||||
data = self._get_info_cer(rfc)
|
||||
llave = self._get_info_key(password, rfc)
|
||||
if not llave:
|
||||
return {}
|
||||
|
||||
data.update(llave)
|
||||
|
||||
self._kill(self._path_key)
|
||||
self._kill(self._path_cer)
|
||||
return data
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import falcon
|
||||
from falcon_multipart.middleware import MultipartMiddleware
|
||||
from beaker.middleware import SessionMiddleware
|
||||
|
||||
from middleware import (
|
||||
|
@ -20,8 +21,12 @@ from settings import DEBUG
|
|||
|
||||
db = StorageEngine()
|
||||
|
||||
api = falcon.API(
|
||||
middleware=[AuthMiddleware(), JSONTranslator(), ConnectionMiddleware()])
|
||||
api = falcon.API(middleware=[
|
||||
AuthMiddleware(),
|
||||
JSONTranslator(),
|
||||
ConnectionMiddleware(),
|
||||
MultipartMiddleware(),
|
||||
])
|
||||
api.req_options.auto_parse_form_urlencoded = True
|
||||
api.add_sink(handle_404, '')
|
||||
|
||||
|
|
|
@ -14,6 +14,15 @@ class StorageEngine(object):
|
|||
def get_values(self, table, values=None):
|
||||
return getattr(self, '_get_{}'.format(table))(values)
|
||||
|
||||
def add_cert(self, file_object):
|
||||
return main.Certificado.add(file_object)
|
||||
|
||||
def validate_cert(self, values, session):
|
||||
return main.Certificado.validate(values, session)
|
||||
|
||||
def _get_cert(self, values):
|
||||
return main.Certificado.get_data()
|
||||
|
||||
def _get_cp(self, values):
|
||||
return main.get_cp(values['cp'])
|
||||
|
||||
|
|
|
@ -238,20 +238,77 @@ class Emisor(BaseModel):
|
|||
|
||||
|
||||
class Certificado(BaseModel):
|
||||
key = BlobField()
|
||||
key = BlobField(null=True)
|
||||
key_tmp = BlobField(null=True)
|
||||
key_enc = TextField(default='')
|
||||
cer = BlobField()
|
||||
cer = BlobField(null=True)
|
||||
cer_tmp = BlobField(null=True)
|
||||
cer_pem = TextField(default='')
|
||||
cer_txt = TextField(default='')
|
||||
p12 = BlobField()
|
||||
p12 = BlobField(null=True)
|
||||
serie = TextField(default='')
|
||||
rfc = TextField(default='')
|
||||
desde = DateTimeField()
|
||||
hasta = DateTimeField()
|
||||
desde = DateTimeField(null=True)
|
||||
hasta = DateTimeField(null=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.serie
|
||||
|
||||
@classmethod
|
||||
def get_data(cls):
|
||||
obj = cls.get_(cls)
|
||||
row = {
|
||||
'cert_rfc': obj.rfc,
|
||||
'cert_serie': obj.serie,
|
||||
'cert_desde': obj.desde,
|
||||
'cert_hasta': obj.hasta,
|
||||
}
|
||||
return row
|
||||
|
||||
def get_(cls):
|
||||
if Certificado.select().count():
|
||||
obj = Certificado.select()[0]
|
||||
else:
|
||||
obj = Certificado()
|
||||
return obj
|
||||
|
||||
@classmethod
|
||||
def add(cls, file_object):
|
||||
obj = cls.get_(cls)
|
||||
if file_object.filename.endswith('key'):
|
||||
obj.key_tmp = file_object.file.read()
|
||||
elif file_object.filename.endswith('cer'):
|
||||
obj.cer_tmp = file_object.file.read()
|
||||
obj.save()
|
||||
return {'status': 'server'}
|
||||
|
||||
@classmethod
|
||||
def validate(cls, values, session):
|
||||
row = {}
|
||||
result = False
|
||||
obj = cls.get_(cls)
|
||||
cert = util.Certificado(obj.key_tmp, obj.cer_tmp)
|
||||
data = cert.validate(values['contra'], session['rfc'])
|
||||
if data:
|
||||
msg = 'Certificado guardado correctamente'
|
||||
q = Certificado.update(**data).where(Certificado.id==obj.id)
|
||||
if q.execute():
|
||||
result = True
|
||||
row = {
|
||||
'cert_rfc': data['rfc'],
|
||||
'cert_serie': data['serie'],
|
||||
'cert_desde': data['desde'],
|
||||
'cert_hasta': data['hasta'],
|
||||
}
|
||||
else:
|
||||
msg = cert.error
|
||||
obj.key_tmp = None
|
||||
obj.cer_tmp = None
|
||||
obj.save()
|
||||
|
||||
return {'ok': result, 'msg': msg, 'data': row}
|
||||
|
||||
|
||||
|
||||
class Folios(BaseModel):
|
||||
serie = TextField(unique=True)
|
||||
|
|
|
@ -13,7 +13,8 @@ var controllers = {
|
|||
$$('emisor_cp').attachEvent('onTimedKeyPress', emisor_postal_code_key_up)
|
||||
$$('chk_escuela').attachEvent('onChange', chk_escuela_change)
|
||||
$$('chk_ong').attachEvent('onChange', chk_ong_change)
|
||||
|
||||
$$('cmd_subir_certificado').attachEvent('onItemClick', cmd_subir_certificado_click)
|
||||
$$('up_cert').attachEvent('onUploadComplete', up_cert_upload_complete)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,11 +142,29 @@ function get_emisor(){
|
|||
}
|
||||
|
||||
|
||||
function get_certificado(){
|
||||
var form = $$('form_cert')
|
||||
|
||||
webix.ajax().get("/values/cert", {}, {
|
||||
error: function(text, data, xhr) {
|
||||
msg = 'Error al consultar'
|
||||
msg_error(msg)
|
||||
},
|
||||
success: function(text, data, xhr) {
|
||||
var values = data.json()
|
||||
form.setValues(values)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
function multi_admin_change(prevID, nextID){
|
||||
//~ webix.message(nextID)
|
||||
if(nextID == 'app_emisor'){
|
||||
$$('tab_emisor').setValue('Datos Fiscales')
|
||||
get_emisor()
|
||||
get_certificado()
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -221,3 +240,99 @@ function chk_ong_change(new_value, old_value){
|
|||
$$('ong_fecha_dof').disable()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function cmd_subir_certificado_click(){
|
||||
var form = $$('form_upload')
|
||||
|
||||
if (!form.validate()){
|
||||
msg = 'Valores inválidos'
|
||||
msg_error(msg)
|
||||
return
|
||||
}
|
||||
|
||||
var values = form.getValues()
|
||||
|
||||
if(!values.contra.trim()){
|
||||
msg = 'La contraseña no puede estar vacía'
|
||||
msg_error(msg)
|
||||
return
|
||||
}
|
||||
|
||||
if($$('lst_cert').count() < 2){
|
||||
msg = 'Selecciona al menos dos archivos: CER y KEY del certificado.'
|
||||
msg_error(msg)
|
||||
return
|
||||
}
|
||||
|
||||
if($$('lst_cert').count() > 2){
|
||||
msg = 'Selecciona solo dos archivos: CER y KEY del certificado.'
|
||||
msg_error(msg)
|
||||
return
|
||||
}
|
||||
|
||||
var fo1 = $$('up_cert').files.getItem($$('up_cert').files.getFirstId())
|
||||
var fo2 = $$('up_cert').files.getItem($$('up_cert').files.getLastId())
|
||||
|
||||
var ext = ['key', 'cer']
|
||||
if(ext.indexOf(fo1.type.toLowerCase()) == -1 || ext.indexOf(fo2.type.toLowerCase()) == -1){
|
||||
msg = 'Archivos inválidos, se requiere un archivo CER y un KEY.'
|
||||
msg_error(msg)
|
||||
return
|
||||
}
|
||||
|
||||
if(fo1.type == fo2.type && fo1.size == fo2.size){
|
||||
msg = 'Selecciona archivos diferentes: un archivo CER y un KEY.'
|
||||
msg_error(msg)
|
||||
return
|
||||
}
|
||||
|
||||
var rfc = $$('form_cert').getValues()['cert_rfc']
|
||||
if(rfc){
|
||||
msg = 'Ya existe un certificado guardado<BR><BR>¿Deseas reemplazarlo?'
|
||||
webix.confirm({
|
||||
title: 'Certificado Existente',
|
||||
ok: 'Si',
|
||||
cancel: 'No',
|
||||
type: 'confirm-error',
|
||||
text: msg,
|
||||
callback:function(result){
|
||||
if(result){
|
||||
$$('up_cert').send()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function up_cert_upload_complete(response){
|
||||
if(response.status != 'server'){
|
||||
msg = 'Ocurrio un error al subir los archivos'
|
||||
msg_error(msg)
|
||||
return
|
||||
}
|
||||
|
||||
msg = 'Archivos subidos correctamente. Esperando validación'
|
||||
msg_sucess(msg)
|
||||
|
||||
var values = $$('form_upload').getValues()
|
||||
$$('form_upload').setValues({})
|
||||
$$('up_cert').files.data.clearAll()
|
||||
|
||||
webix.ajax().post('/values/cert', values, {
|
||||
error:function(text, data, XmlHttpRequest){
|
||||
msg = 'Ocurrio un error, consulta a soporte técnico'
|
||||
msg_error(msg)
|
||||
},
|
||||
success:function(text, data, XmlHttpRequest){
|
||||
var values = data.json()
|
||||
if(values.ok){
|
||||
$$('form_cert').setValues(values.data)
|
||||
msg_sucess(values.msg)
|
||||
}else{
|
||||
msg_error(values.msg)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -104,15 +104,15 @@ var emisor_certificado = [
|
|||
{template: 'Cargar Certificado', type: 'section'},
|
||||
{view: 'form', id: 'form_upload', rows: [
|
||||
{cols: [{},
|
||||
{view: 'uploader', id: 'up_cert', autosend:false, link: 'lst_cert',
|
||||
value: 'Seleccionar certificado', upload: '/values/cert'}, {}]},
|
||||
{view: 'uploader', id: 'up_cert', autosend: false, link: 'lst_cert',
|
||||
value: 'Seleccionar certificado', upload: '/values/files'}, {}]},
|
||||
{cols: [{},
|
||||
{view: 'list', id: 'lst_cert', type: 'uploader', autoheight:true,
|
||||
borderless: true}, {}]},
|
||||
{view: 'list', id: 'lst_cert', name: 'certificado',
|
||||
type: 'uploader', autoheight:true, borderless: true}, {}]},
|
||||
{cols: [{},
|
||||
{view: 'text', id: 'txt_contra', label: 'Contraseña KEY',
|
||||
labelPosition: 'top', labelAlign: 'center', type: 'password',
|
||||
required: true}, {}]},
|
||||
{view: 'text', id: 'txt_contra', name: 'contra',
|
||||
label: 'Contraseña KEY', labelPosition: 'top',
|
||||
labelAlign: 'center', type: 'password', required: true}, {}]},
|
||||
{cols: [{}, {view: 'button', id: 'cmd_subir_certificado',
|
||||
label: 'Subir certificado'}, {}]},
|
||||
]},
|
||||
|
|
Loading…
Reference in New Issue