This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
passerelle-grandlyon-cartads/cartads/models.py

300 lines
14 KiB
Python

# Copyright (C) 2016 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import base64
import json
import datetime
import uuid
from email import encoders
from email.mime.audio import MIMEAudio
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from suds.client import Client
from suds.transport import Reply
from suds.transport.http import HttpAuthenticated
import suds.sudsobject
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.core.cache import cache
from passerelle.base.models import BaseResource
from passerelle.utils.api import endpoint
from .formdata import FormData, CREATION_SCHEMA, list_schema_fields
class ParameterTypeError(Exception):
http_status = 400
log_error = False
def fill_sudsobject_with_dict(sudsobject, fields, prefix=None):
for key, value in sudsobject:
if prefix:
attr = '%s_%s' % (prefix, key)
else:
attr = key
if isinstance(value, suds.sudsobject.Object):
fill_sudsobject_with_dict(value, fields, attr)
else:
if attr in fields:
# sudsobject.foo.bar <- fields['foo_bar']
setattr(sudsobject, key, fields[attr])
def sudsobject_to_dict(sudsobject):
out = {}
for key, value in suds.sudsobject.asdict(sudsobject).iteritems():
if hasattr(value, '__keylist__'):
out[key] = sudsobject_to_dict(value)
elif isinstance(value, list):
out[key] = []
for item in value:
if hasattr(item, '__keylist__'):
out[key].append(sudsobject_to_dict(item))
else:
out[key].append(item)
else:
out[key] = value
return out
class CartADS(BaseResource):
token_url = models.URLField(_('Token URL'), max_length=256)
token_authorization = models.CharField(_('Token Authorization'), max_length=128)
wsdl_url = models.CharField(_('WSDL URL'), max_length=256) # not URLField, it can be file://
verify_cert = models.BooleanField(default=True,
verbose_name=_('Check HTTPS Certificate validity'))
# TODO : a mettre en param du connecteur
dirname = 'D:\LSM\Gfi\DepotDossierFTP\client1\\'
category = _('Business Process Connectors')
class Meta:
verbose_name = _('CartADS Webservices')
def get_token(self, renew=False):
cache_key = 'cartads-%s-token' % self.id
if not renew:
token = cache.get(cache_key)
if token:
return token
headers = {'Authorization': 'Basic %s' % self.token_authorization}
resp = self.requests.post(self.token_url, headers=headers,
data={'grant_type': 'client_credentials'},
verify=self.verify_cert).json()
token = '%s %s' % (resp.get('token_type'), resp.get('access_token'))
timeout = int(resp.get('expires_in'))
cache.set(cache_key, token, timeout)
self.logger.debug('new token: %s (timeout %ss)', token, timeout)
return token
def get_client(self, attachments=[]):
class Transport(HttpAuthenticated):
def __init__(self, instance, attachments):
self.instance = instance
self.attachments = attachments
HttpAuthenticated.__init__(self)
def send(self, request):
request.message = request.message.replace("contentType", "xm:contentType")
# a priori attachement inutile pour cartads, a voir si on supprime
if self.attachments:
# SOAP Attachement format
message = MIMEMultipart('related', type="text/xml",
start="<rootpart@entrouvert.org>")
xml = MIMEText(None, _subtype='xml', _charset='utf-8')
xml.add_header('Content-ID', '<rootpart@entrouvert.org>')
# do not base64-encode the soap message
xml.replace_header('Content-Transfer-Encoding', '8bit')
xml_payload = request.message
# hack payload to include attachment filenames in
# SOAP-ENV:Header.
soap_headers = []
for num, attachment in enumerate(self.attachments):
filename = attachment.get('filename') or 'file%s.bin' % num
if isinstance(filename, unicode):
filename = filename.encode('utf-8', 'ignore')
soap_headers.append('<filename%s>%s</filename%s>' % (num, filename, num))
xml_payload = xml_payload.replace('<SOAP-ENV:Header/>',
'<SOAP-ENV:Header>%s</SOAP-ENV:Header>' % ''.join(soap_headers))
xml.set_payload(xml_payload)
message.attach(xml)
for num, attachment in enumerate(self.attachments):
filename = attachment.get('filename') or 'file%s.bin' % num
content = base64.b64decode(attachment.get('content') or '')
content_type = attachment.get('content_type') or 'application/octet-stream'
maintype, subtype = content_type.split('/', 1)
if maintype == 'text':
part = MIMEText(content, _subtype=subtype)
else:
part = MIMEBase(maintype, subtype, name=filename)
part.set_payload(content)
part.add_header('Content-Transfer-Encoding', 'binary')
encoders.encode_noop(part)
part.add_header('Content-Disposition', 'attachment', name=filename, filename=filename)
part.add_header('Content-ID', '<%s>' % filename)
message.attach(part)
message._write_headers = lambda x: None
msg_x = message.as_string(unixfrom=False)
# RFC 2045 defines MIME multipart boundaries:
# * boundary := 0*69<bchars> bcharsnospace
# * dash-boundary := "--" boundary
# * delimiter := CRLF dash-boundary
# but Python doesn't use CRLF, will only use LF (on Unix systems
# at least). This is http://bugs.python.org/issue1349106 and has
# been fixed in Python 3.2.
#
# Manually hack message to put \r\n so that the message is
# correctly read by Apache Axis strict parser.
boundary = message.get_boundary()
request.message = message.as_string(unixfrom=False
).replace(boundary + '\n', boundary + '\r\n'
).replace('\n--' + boundary, '\r\n--' + boundary)
request.headers.update(dict(message._headers))
request.headers['Authorization'] = self.instance.get_token()
resp = self.instance.requests.post(request.url, data=request.message,
headers=request.headers,
verify=self.instance.verify_cert)
if resp.status_code == 401:
# ask for a new token, and retry
request.headers['Authorization'] = self.instance.get_token(renew=True)
resp = self.instance.requests.post(request.url, data=request.message,
headers=request.headers,
verify=self.instance.verify_cert)
return Reply(resp.status_code, resp.headers, resp.content)
return Client(url=self.wsdl_url, transport=Transport(self, attachments))
@endpoint(perm='can_access', methods=['get','post'])
def create(self, request):
# get creation fields from payload
try:
formdata = FormData(json.loads(request.body), CREATION_SCHEMA)
except ValueError as e:
raise ParameterTypeError(e.message)
if formdata.attachments:
print >> open('/home/grandlyon/log/cartads.debug', 'a+'), datetime.datetime.now(), "nb attach: ", len(formdata.attachments)
for num, attachment in enumerate(formdata.attachments):
filename = attachment.get('filename') or 'file%s.bin' % num
print >> open('/home/grandlyon/log/cartads.debug', 'a+'), datetime.datetime.now(), "filename: ", filename
#content = base64.b64decode(attachment.get('content') or '')
b64_fileContent = attachment.get('content')
#print >> open('/home/grandlyon/log/cartads.debug', 'a+'), datetime.datetime.now(), "b64_fileContent: ", b64_fileContent
# Pour l'instant pour test, on envoie le dernier fichier du formulaires
# TODO : construire le zip de tous les fichiers du form
# Nom du fichier zip a envoyer
fileName = str(uuid.uuid4()) + ".zip";
# test base64
#b64_fileContent = "UEsDBBQAAAAAAMqEQUzI2HQpGgAAABoAAAAKAAAAdGVzdDAxLnR4dHRlc3QgYmFzZTY0IG1pbmkgdG90byB0YXRhUEsBAhQAFAAAAAAAyoRBTMjYdCkaAAAAGgAAAAoAAAAAAAAAAQAgAAAAAAAAAHRlc3QwMS50eHRQSwUGAAAAAAEAAQA4AAAAQgAAAAAA"
# size_max doit etre un multiple de 4 pour avoir un nb de caracteres valide en base 64 (car 3 octets y sont encodes sur 4 caracteres)
# size_max choisi pour eviter erreur OpenSSL.SSL.WantWriteError due au openssl socket send buffer size (https://pyopenssl.org/en/stable/api/ssl.html)
# TODO: demander a EO si on peut l'augmenter, car en curl on peut envoyer plus de 7 Mo d'un coup d'apres nos tests
size_max = 65536 # 64 ko, d'apres test semble etre la limite par defaut du openssl socket send buffer size
# TODO : mettre en parametre l'url du ws sendfile
for x in range(0, len(b64_fileContent)/size_max + 1):
resp = self.requests.post('https://api-rec.grandlyon.com/ads-sendfile-dev/sendfile.aspx',
data={'fileName': self.dirname+fileName,
'b64_fileContent': b64_fileContent[x*size_max:(x+1)*size_max],
'part': str(x),
'total': int(len(b64_fileContent)/size_max)}
) #.json()
return {'data': resp.content, 'length': len(b64_fileContent)}
@classmethod
def creation_fields(cls):
'''used in cartads_detail.html template'''
return list_schema_fields(CREATION_SCHEMA)
def get_token_cartads(self):
#TODO : a encoder d'apres les exemples php et c# fournispar GFI
return 'ieTluf0q6vwjitxope55YZ2ud0CEtuO9BBHr2hQaxySeDrz66mntHl83Wqj7oadMSyZqwSkzVdZJrQ92Zg2p3bwkAuv5yUzwmpBfdtAYYLE='
def get_type_compte_utilisateur(self):
#TODO : a encoder d'apres les exemples php et c# fournispar GFI
return 'ContactService'
@endpoint(perm='can_access')
def get_communes(self, request):
resp = self.get_client().service.GetCommunes(self.get_token_cartads(),
self.get_type_compte_utilisateur()
)
'''
#TODO: Ca serait mieux mais ca marche pas...
resp = self.get_client().service.GetCommunes({
'token': self.get_token_cartads(),
'typeCompteUtilisateur': self.get_type_compte_utilisateur(),
})'''
return {'data': sudsobject_to_dict(resp)}
@endpoint(perm='can_access')
def get_objets_demande(self, request, type_dossier):
resp = self.get_client().service.GetObjetsDemande(self.get_token_cartads(), type_dossier)
# TODO : parcourir la liste en json devrait etre plus simple qu'en suds, mais j'ai pas reussi pour l'instant
# On parcourt la liste pour la mettre sous la forme id, text, pour l'utiliser comme source de donnees dans le formulaire
out = {}
for key, value in suds.sudsobject.asdict(resp).iteritems():
out[key] = []
for item in value:
if hasattr(item, '__keylist__'):
out_item = {}
for key_item, value_item in suds.sudsobject.asdict(item).iteritems():
out_item['id'] = item.Key
out_item['text'] = item.Value
out[key].append(out_item)
else:
item.id = item.Key
item.text = item.Value
out[key].append(item)
return {'data': out['KeyValueOfintstring']}
@endpoint(perm='can_access')
def get_liste_pdf(self, request, type_dossier):
resp = self.get_client().service.GetListePdf(self.get_token_cartads(),
type_dossier,
self.get_type_compte_utilisateur()
)
return {'data': sudsobject_to_dict(resp)}
@endpoint(perm='can_access')
def get_pieces(self, request, type_dossier, objet_demande):
resp = self.get_client().service.GetPieces(self.get_token_cartads(),
type_dossier,
objet_demande
)
return {'data': sudsobject_to_dict(resp)}