From 97d1375888c590c1ef20d60006f98c3a1e3a227b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marinho=20Brand=C3=A3o?= Date: Sat, 16 Jan 2010 08:48:37 -0200 Subject: [PATCH] Enviando arquivos esquecidos e iniciando trabalho na assinatura do XML. Tambem iniciado o arquivo de autores, devido ao inicio da colaboracao da TaugaRS --- AUTHORS | 25 ++++++ pynfe/entidades/certificado.py | 27 +++++++ pynfe/entidades/fonte_dados.py | 128 +++++++++++++++++++++++++++++++ pynfe/processamento/assinatura.py | 35 ++++++++- tests/01-basico.txt | 35 +++++---- tests/03-processamento-03-assinatura.txt | 25 ++++++ 6 files changed, 259 insertions(+), 16 deletions(-) create mode 100644 AUTHORS create mode 100644 pynfe/entidades/certificado.py create mode 100644 pynfe/entidades/fonte_dados.py create mode 100644 tests/03-processamento-03-assinatura.txt diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..33c8bd0 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,25 @@ +Sobre os autores deste e outros créditos +======================================== + +O PyNFe foi criado por Marinho Brandão e seu +desenvolvimento só foi possível graças à massiva colaboração de pessoas, +empresas e outros projetos livres para o mesmo fim, disponívels para outras +linguagens. + +A TaugaRS Sistemas gentil e voluntariamente forneceu o +código-fonte de seu pacote já testado e maduro para servir de base de apoio +neste processo, assim como seu know-how de mais de 2 anos em NF-eletronicas. +Por esse motivo, encontra-se em igual ou maior importância que a do criador, o +desenvolvedor Ari Caldeira e o restante da diretoria desta empresa. + +Outra fonte importante de informação foi do projeto de componentes para Delphi +voltados para automação comercial ACBr , que +inclui um componente que tem a mesma finalidade que o PyNFe. + +Outras pessoas que devem ser referenciadas por sua colaboração, seja através de +know-how e debates, seja através de código enviado para colaborar: + + - Diogo Daniel (Prosig Sistemas) + - Antonio Prado (Antonio Prado Sistemas) + - Italo Maia + diff --git a/pynfe/entidades/certificado.py b/pynfe/entidades/certificado.py new file mode 100644 index 0000000..9c34a5c --- /dev/null +++ b/pynfe/entidades/certificado.py @@ -0,0 +1,27 @@ +# -*- coding; utf-8 -*- +from base import Entidade + +class Certificado(Entidade): + u"""Classe abstrata responsavel por definir o modelo padrao para as demais + classes de certificados digitais. + + Caso va implementar um novo formato de certificado, crie uma classe que + herde desta.""" + + def __new__(cls, *args, **kwargs): + if cls == Certificado: + raise Exception('Esta classe nao pode ser instanciada diretamente!') + else: + return super(Certificado, cls).__new__(cls, *args, **kwargs) + +class CertificadoA1(Certificado): + """Implementa a entidade do certificado eCNPJ A1, suportado pelo OpenSSL, + e amplamente utilizado.""" + + caminho_arquivo = None + conteudo_x509 = None + + def __init__(self, caminho_arquivo=None, conteudo_x509=None): + self.caminho_arquivo = caminho_arquivo or self.caminho_arquivo + self.conteudo_x509 = conteudo_x509 or self.conteudo_x509 + diff --git a/pynfe/entidades/fonte_dados.py b/pynfe/entidades/fonte_dados.py new file mode 100644 index 0000000..8db5ddf --- /dev/null +++ b/pynfe/entidades/fonte_dados.py @@ -0,0 +1,128 @@ +# -*- coding: utf-8 -*- +from pynfe.excecoes import NenhumObjetoEncontrado, MuitosObjetosEncontrados + +class FonteDados(object): + u"""Classe responsável por ser o repositório dos objetos em memória e que + pode ser extendida para persistir esses objetos. Também tem a função de + memorizar os objetos redundantes como um só e assim otimizar o desempenho.""" + + _objetos = None + + def __init__(self, objetos=None): + # Inicializa variável que armazena os objetos contidos na Fonte de Dados + if objetos: + self._objetos = objetos + else: + self._objetos = [] + + def carregar_objetos(self, **kwargs): + u"""Método responsavel por retornar os objetos que casem com os atributos + informados no argumento **kwargs (argumentos nomeados). + + Um argumento especial é o '_classe', que representa a classe da entidade + desejada. + + FIXME: Este algoritimo pode ser melhorado pra fazer pesquisas melhores, + mas por enquanto vamos nos focar no processo em geral para só depois nos + preocupar com otimizações e desempenho.""" + + # Função de filtro + def filtrar(obj): + ret = True + + for k,v in kwargs.items(): + # Filtra pela classe e pelos atributos + ret = (k == '_classe' and isinstance(obj, v)) or\ + (k != '_classe' and getattr(obj, k, None) == v) + + if not ret: + break + + return ret + + # Filtra a lista de objetos + lista = filter(filtrar, self._objetos) + + return lista + + def adicionar_objeto(self, _objeto): + u"""Método responsável por adicionar o(s) objeto(s) informado(s) ao + repositorio de objetos da fonte de dados.""" + + from base import Entidade + + # Adiciona _objeto como objeto + if isinstance(_objeto, Entidade): + self._objetos.append(_objeto) + + # Adiciona _objeto como lista + elif isinstance(_objeto, (list, tuple)): + self._objetos += _objeto + + else: + raise Exception('Objeto informado e invalido!') + + def remover_objeto(self, _objeto=None, **kwargs): + u"""Método responsavel por remover os objetos que casem com os atributos + informados no argumento **kwargs (argumentos nomeados). + + Um argumento especial é o '_classe', que representa a classe da entidade + desejada. + + Outro argumetno especial é o '_objeto', que representa o objeto a ser + removido. Caso o argumento _objeto seja uma lista de objetos, eles serão + removidos também.""" + + from base import Entidade + + lista = None + + # Remove objetos + if not _objeto: + lista = self.carregar_objetos(**kwargs) + + # Remove _objeto como objeto + elif isinstance(_objeto, Entidade): + lista = [_objeto] + + # Remove _objeto como objeto + elif isinstance(_objeto, (list, tuple)): + lista = _objeto + + else: + raise Exception('Objeto informado e invalido!') + + # Efetiva a remoção + for obj in lista: + self._objetos.remove(obj) + + def obter_objeto(self, **kwargs): + u"""Faz a ponte para o método 'carregar_objetos' mas obriga o retorno de + apenas um objeto, levantando exceção se nenhum for encontrado ou se forem + encontrados mais de um.""" + + lista = self.carregar_objetos(**kwargs) + + if len(lista) == 0: + raise NenhumObjetoEncontrado('Nenhum objeto foi encontrado!') + elif len(lista) > 1: + raise MuitosObjetosEncontrados('Muitos objetos foram encontrados!') + + return lista[0] + + def obter_lista(self, **kwargs): + u"""Método de proxy, que somente repassa a chamada ao metodo 'carregar_objetos'""" + return self.carregar_objetos(**kwargs) + + def contar_objetos(self, **kwargs): + u"""Método que repassa a chamada ao metodo 'carregar_objetos' mas retorna + somente a quantidade de objetos encontrados.""" + + if kwargs: + return len(self.carregar_objetos(**kwargs)) + else: + return len(self._objetos) + +# Instancia da fonte de dados default +_fonte_dados = FonteDados() + diff --git a/pynfe/processamento/assinatura.py b/pynfe/processamento/assinatura.py index e50ed6c..2bd8509 100644 --- a/pynfe/processamento/assinatura.py +++ b/pynfe/processamento/assinatura.py @@ -1,3 +1,36 @@ +# -*- coding: utf-8 -*- + class Assinatura(object): - pass + """Classe abstrata responsavel por definir os metodos e logica das classes + de assinatura digital.""" + + def __init__(self, certificado): + self.certificado = certificado + + def assinar_arquivos(self, caminho_raiz): + """Efetua a assinatura dos arquivos XML informados""" + pass + def assinar_xml(self, xml): + """Efetua a assinatura numa string contendo XML valido.""" + pass + + def assinar_etree(self, raiz): + u"""Efetua a assinatura numa instancia da biblioteca lxml.etree. + + Este metodo de assinatura será utilizado internamente pelos demais, + sendo que eles convertem para uma instancia lxml.etree para somente + depois efetivar a assinatura. + + TODO: Verificar o funcionamento da PyXMLSec antes de efetivar isso.""" + pass + + def assinar_objetos(self, objetos): + """Efetua a assinatura em instancias do PyNFe""" + pass + +class AssinaturaA1(Assinatura): + """Classe abstrata responsavel por efetuar a assinatura do certificado + digital no XML informado.""" + + pass diff --git a/tests/01-basico.txt b/tests/01-basico.txt index 8b6aaca..23458ac 100644 --- a/tests/01-basico.txt +++ b/tests/01-basico.txt @@ -45,21 +45,26 @@ modelo: | PROCESSAMENTO | ---------------------------------------------------------------------------- | | - | ------------------- -------------- -------------------------------- | - | | SerializacaoXML | | Assinatura | | Comunicacao | | - | ------------------- -------------- -------------------------------- | - | | exportar() | | assinar() | | transmitir() | | - | | importar() | -------------- | cancelar() | | - | ------------------- | situacao_nfe() | | - | ---------------------- | status_servico() | | - | -------------- | Validacao | | consultar_cadastro() | | - | | DANFE | ---------------------- | inutilizar_faixa_numeracao() | | - | -------------- | validar_arquivos() | -------------------------------- | - | | imprimir() | | validar_xml() | | - | -------------- | validar_etree() | | - | | validar_objetos() | | - | ---------------------- | - | | + | ------------------ -------------- -------------------------------- | + | | Serializacao | | DANFE | | Comunicacao | | + | ------------------ -------------- -------------------------------- | + | | exportar() | | imprimir() | | transmitir() | | + | | importar() | -------------- | cancelar() | | + | ------------------ | situacao_nfe() | | + | | status_servico() | | + | ---------------------- | consultar_cadastro() | | + | | Validacao | | inutilizar_faixa_numeracao() | | + | ---------------------- -------------------------------- | + | | validar_arquivos() | | + | | validar_xml() | | + | | validar_etree() | ---------------------- | + | | validar_objetos() | | Assinatura | | + | ---------------------- ---------------------- | + | | assinar_arquivos() | | + | | assinar_xml() | | + | | assinar_etree() | | + | | assinar_objetos() | | + | ---------------------- | ---------------------------------------------------------------------------- Os pacotes da biblioteca sao: diff --git a/tests/03-processamento-03-assinatura.txt b/tests/03-processamento-03-assinatura.txt new file mode 100644 index 0000000..2e556be --- /dev/null +++ b/tests/03-processamento-03-assinatura.txt @@ -0,0 +1,25 @@ +PROCESSAMENTO - ASSINATURA DE XML +================================= + +Carregando Certificado Digital tipo A1 +-------------------------------------- + + >>> from pynfe.entidades import CertificadoA1 + +Assinando NF-e +-------------- + + >>> from pynfe.processamento import AssinaturaC1 + +Na hora de assinar, selecionar um Certificado Digital + + >>> + +A assinatura deve ser feita em quatro tipos diferentes de origem do XML: + +- Arquivos +- + +- Utilizar pyXMLSec para isso + - verificar qual eh a integracao do PyXMLSec com o lxml.etree +