Modifications qui seront validées :
modifié : ../app.py nouveau fichier : cloud.py nouveau fichier : common_imports.py nouveau fichier : config.py nouveau fichier : dns.py nouveau fichier : kaz_user.py nouveau fichier : ldap.py nouveau fichier : mattermost.py nouveau fichier : paheko.py nouveau fichier : password.py nouveau fichier : quota.py nouveau fichier : sympa.py nouveau fichier : test.py
This commit is contained in:
parent
94cbbc1006
commit
f796f01d40
File diff suppressed because it is too large
Load Diff
147
dockers/apikaz/source/resources/cloud.py
Normal file
147
dockers/apikaz/source/resources/cloud.py
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
|
||||||
|
from resources.common_imports import *
|
||||||
|
|
||||||
|
#les variables globales minimum
|
||||||
|
from resources.config import cloud_ident, cloud_pass, cloud_url
|
||||||
|
|
||||||
|
|
||||||
|
#TODO: pas réussi à faire une seule classe Cloud_user avec 2 méthodes get/delete
|
||||||
|
|
||||||
|
class Cloud_user(Resource):
|
||||||
|
|
||||||
|
@jwt_required()
|
||||||
|
def get(self, email):
|
||||||
|
"""
|
||||||
|
Existe dans le cloud général ?
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Cloud
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: email
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Succès
|
||||||
|
404:
|
||||||
|
description: L'utilisateur n'existe pas
|
||||||
|
500:
|
||||||
|
description: Erreur interne du serveur
|
||||||
|
"""
|
||||||
|
|
||||||
|
global cloud_ident, cloud_pass, cloud_url
|
||||||
|
|
||||||
|
try:
|
||||||
|
auth = (cloud_ident, cloud_pass)
|
||||||
|
api_url = f"{cloud_url}/ocs/v1.php/cloud/users?search={email}"
|
||||||
|
headers = {"OCS-APIRequest": "true"}
|
||||||
|
response = requests.get(api_url, auth=auth, headers=headers)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
if re.search(r'<element>.*</element>', response.text):
|
||||||
|
return 200
|
||||||
|
else:
|
||||||
|
return 404
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
class Cloud_user_delete(Resource):
|
||||||
|
|
||||||
|
@jwt_required()
|
||||||
|
def delete(self, email):
|
||||||
|
"""
|
||||||
|
Supprime le compte dans le cloud général
|
||||||
|
QUESTION: A PRIORI INUTILE CAR LIE AU LDAP
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Cloud
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: email
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Succès, l'utilisateur a été supprimé du cloud général
|
||||||
|
404:
|
||||||
|
description: Oops, l'utilisateur n'a pas été supprimé du cloud général
|
||||||
|
500:
|
||||||
|
description: Erreur interne du serveur
|
||||||
|
"""
|
||||||
|
|
||||||
|
global cloud_ident, cloud_pass, cloud_url
|
||||||
|
|
||||||
|
try:
|
||||||
|
auth = (cloud_ident, cloud_pass)
|
||||||
|
api_url = f"{cloud_url}/ocs/v1.php/cloud/users?search={email}"
|
||||||
|
headers = {"OCS-APIRequest": "true"}
|
||||||
|
response = requests.delete(api_url, auth=auth, headers=headers)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
if re.search(r'<element>.*</element>', response.text):
|
||||||
|
return 200
|
||||||
|
else:
|
||||||
|
return 404
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
# class Cloud_user_change(Resource):
|
||||||
|
# @jwt_required()
|
||||||
|
# def put(self, email, new_password):
|
||||||
|
# """
|
||||||
|
# Modifie le mot de passe d'un Utilisateur dans le cloud général: QUESTION: A PRIORI INUTILE CAR LIE AU LDAP
|
||||||
|
# ---
|
||||||
|
# tags:
|
||||||
|
# - Cloud
|
||||||
|
# security:
|
||||||
|
# - Bearer: []
|
||||||
|
# parameters:
|
||||||
|
# - in: path
|
||||||
|
# name: email
|
||||||
|
# type: string
|
||||||
|
# required: true
|
||||||
|
# - in: path
|
||||||
|
# name: new_password
|
||||||
|
# type: string
|
||||||
|
# required: true
|
||||||
|
# responses:
|
||||||
|
# 200:
|
||||||
|
# description: Succès, mot de passe changé
|
||||||
|
# 404:
|
||||||
|
# description: Oops, mot de passe NON changé
|
||||||
|
# 500:
|
||||||
|
# description: Erreur interne du serveur
|
||||||
|
# """
|
||||||
|
#
|
||||||
|
# global cloud_ident, cloud_pass, cloud_url
|
||||||
|
#
|
||||||
|
# try:
|
||||||
|
# auth = (cloud_ident, cloud_pass)
|
||||||
|
# api_url = f"{cloud_url}/ocs/v1.php/cloud/users?search={email}"
|
||||||
|
# headers = {"OCS-APIRequest": "true"}
|
||||||
|
# data = {
|
||||||
|
# "key": "password",
|
||||||
|
# "value": new_password
|
||||||
|
# }
|
||||||
|
# response = requests.put(api_url, auth=auth, headers=headers)
|
||||||
|
#
|
||||||
|
# if response.status_code == 200:
|
||||||
|
# if re.search(r'<element>.*</element>', response.text):
|
||||||
|
# return 200
|
||||||
|
# else:
|
||||||
|
# return 404
|
||||||
|
#
|
||||||
|
# except Exception as e:
|
||||||
|
# return jsonify({'error': str(e)}), 500
|
||||||
|
#
|
25
dockers/apikaz/source/resources/common_imports.py
Normal file
25
dockers/apikaz/source/resources/common_imports.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import os
|
||||||
|
import re
|
||||||
|
import requests
|
||||||
|
import subprocess
|
||||||
|
import logging
|
||||||
|
import tempfile
|
||||||
|
import ldap
|
||||||
|
import imaplib
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
import json
|
||||||
|
|
||||||
|
from flask import Flask, jsonify, send_from_directory, request, abort, json, Response, render_template
|
||||||
|
from flask_mail import Mail, Message
|
||||||
|
from flasgger import Swagger
|
||||||
|
from flask_restful import Api, Resource
|
||||||
|
from flask_jwt_extended import JWTManager, create_access_token, jwt_required
|
||||||
|
|
||||||
|
from passlib.hash import sha512_crypt
|
||||||
|
from unidecode import unidecode
|
||||||
|
from email_validator import validate_email, EmailNotValidError
|
||||||
|
from time import sleep
|
||||||
|
from glob import glob
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
from datetime import datetime
|
45
dockers/apikaz/source/resources/config.py
Normal file
45
dockers/apikaz/source/resources/config.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
#le paheko de kaz
|
||||||
|
paheko_ident=os.environ.get('paheko_API_USER')
|
||||||
|
paheko_pass=os.environ.get('paheko_API_PASSWORD')
|
||||||
|
paheko_url=os.environ.get('paheko_url')
|
||||||
|
|
||||||
|
#mattermost
|
||||||
|
mattermost_user=os.environ.get('mattermost_user')
|
||||||
|
mattermost_pass=os.environ.get('mattermost_pass')
|
||||||
|
mattermost_url=os.environ.get('mattermost_url')
|
||||||
|
|
||||||
|
#ldap
|
||||||
|
ldap_admin=os.environ.get('ldap_LDAP_ADMIN_USERNAME')
|
||||||
|
ldap_pass=os.environ.get('ldap_LDAP_ADMIN_PASSWORD')
|
||||||
|
ldap_root=os.environ.get('ldap_root')
|
||||||
|
ldap_host="ldapServ.ldapNet"
|
||||||
|
|
||||||
|
#cloud général
|
||||||
|
cloud_ident=os.environ.get('nextcloud_NEXTCLOUD_ADMIN_USER')
|
||||||
|
cloud_pass=os.environ.get('nextcloud_NEXTCLOUD_ADMIN_PASSWORD')
|
||||||
|
cloud_url=os.environ.get('cloud_url')
|
||||||
|
|
||||||
|
#sympa
|
||||||
|
sympa_ident=os.environ.get('sympa_SOAP_USER')
|
||||||
|
sympa_pass=os.environ.get('sympa_SOAP_PASSWORD')
|
||||||
|
sympa_url=os.environ.get('sympa_url')
|
||||||
|
sympa_liste_info=os.environ.get('sympa_liste_info')
|
||||||
|
MAIL_USERNAME=os.environ.get('apikaz_MAIL_USERNAME')
|
||||||
|
|
||||||
|
#pour QUOTA (à virer ensuite)
|
||||||
|
serveur_imap = os.environ.get('serveur_imap')
|
||||||
|
mot_de_passe_mail=os.environ.get('mot_de_passe_mail')
|
||||||
|
|
||||||
|
#dns
|
||||||
|
gandi_key=os.environ.get('gandi_GANDI_KEY')
|
||||||
|
gandi_url_api=os.environ.get('gandi_GANDI_API')
|
||||||
|
|
||||||
|
#kaz_user
|
||||||
|
site_url=os.environ.get('site_url')
|
||||||
|
#pour webmail_url et mdp_url, ça renvoie des tuples et non des str, bizarre, du coup, je mets en dur
|
||||||
|
#webmail_url=os.environ.get('webmail_url'),
|
||||||
|
#mdp_url=os.environ.get('mdp_url'),
|
||||||
|
webmail_url='https://webmail.kaz.bzh',
|
||||||
|
mdp_url='https://mdp.kazkouil.fr',
|
159
dockers/apikaz/source/resources/dns.py
Normal file
159
dockers/apikaz/source/resources/dns.py
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
from resources.common_imports import *
|
||||||
|
|
||||||
|
#les variables globales minimum
|
||||||
|
from resources.config import gandi_key, gandi_url_api
|
||||||
|
|
||||||
|
|
||||||
|
class Dns_serveurs(Resource):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
global gandi_key, gandi_url_api
|
||||||
|
self.gandi_key = gandi_key
|
||||||
|
self.gandi_url_api = gandi_url_api
|
||||||
|
|
||||||
|
@jwt_required()
|
||||||
|
def get(self):
|
||||||
|
"""
|
||||||
|
Renvoie tous les serveurs kaz de la zone dns
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Dns
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Succès, liste des serveurs
|
||||||
|
404:
|
||||||
|
description: Oops, soucis...
|
||||||
|
"""
|
||||||
|
url = f"{self.gandi_url_api}/records/srv/TXT"
|
||||||
|
headers = { "Authorization": f"Apikey {self.gandi_key}" }
|
||||||
|
response = requests.get(url, headers=headers)
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
return response.json(),response.status_code
|
||||||
|
|
||||||
|
# Extraire la liste des serveurs de la réponse JSON
|
||||||
|
rrset_values = response.json()["rrset_values"]
|
||||||
|
# Nettoyer la liste
|
||||||
|
serveurs = [serveur.strip('"') for serveur in rrset_values[0].split(";")]
|
||||||
|
return serveurs, 200
|
||||||
|
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
|
||||||
|
class Dns(Resource):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
global gandi_key, gandi_url_api
|
||||||
|
self.gandi_key = gandi_key
|
||||||
|
self.gandi_url_api = gandi_url_api
|
||||||
|
self.dns_serveurs_resource = Dns_serveurs()
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
@jwt_required()
|
||||||
|
def get(self,sdomaine):
|
||||||
|
"""
|
||||||
|
Le sous-domaine existe t-il dans la zone dns avec un enreg CNAME ?
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Dns
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: sdomaine
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Succès, sdomaine existe déjà avec un CNAME
|
||||||
|
404:
|
||||||
|
description: Oops, sdomaine non trouvé
|
||||||
|
"""
|
||||||
|
url = f"{self.gandi_url_api}/records/{sdomaine}/CNAME"
|
||||||
|
headers = { "Authorization": f"Apikey {self.gandi_key}" }
|
||||||
|
response = requests.get(url, headers=headers)
|
||||||
|
return response.json(),response.status_code
|
||||||
|
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
|
||||||
|
@jwt_required()
|
||||||
|
def delete(self,sdomaine):
|
||||||
|
"""
|
||||||
|
suppression du sdomaine
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Dns
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: sdomaine
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
responses:
|
||||||
|
204:
|
||||||
|
description: Succès, sdomaine supprimé
|
||||||
|
404:
|
||||||
|
description: Oops,
|
||||||
|
"""
|
||||||
|
url = f"{self.gandi_url_api}/records/{sdomaine}"
|
||||||
|
headers = { "Authorization": f"Apikey {self.gandi_key}" }
|
||||||
|
response = requests.delete(url, headers=headers)
|
||||||
|
return response.text,response.status_code
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
|
||||||
|
@jwt_required()
|
||||||
|
def post(self,sdomaine,serveur):
|
||||||
|
"""
|
||||||
|
Créé le sous-domaine de type CNAME qui pointe sur serveur
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Dns
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: sdomaine
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
- in: path
|
||||||
|
name: serveur
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
description: Le serveur doit être l'un des serveurs disponibles dans la zone DNS
|
||||||
|
responses:
|
||||||
|
201:
|
||||||
|
description: Succès, sdomaine créé comme CNAME
|
||||||
|
400:
|
||||||
|
description: Oops, serveur inconnu, sdomaine non créé
|
||||||
|
404:
|
||||||
|
description: Oops, sdomaine non créé
|
||||||
|
200,409:
|
||||||
|
description: Oops, sdomaine déjà créé
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# Récupérer la liste des serveurs disponibles
|
||||||
|
serveurs_disponibles, status_code = self.dns_serveurs_resource.get()
|
||||||
|
|
||||||
|
if status_code != 200:
|
||||||
|
return serveurs_disponibles, status_code
|
||||||
|
|
||||||
|
if serveur not in serveurs_disponibles:
|
||||||
|
return f"Erreur: Le serveur {serveur} n'est pas disponible", 400
|
||||||
|
|
||||||
|
url = f"{self.gandi_url_api}/records/{sdomaine}/CNAME"
|
||||||
|
headers = { "Authorization": f"Apikey {self.gandi_key}" }
|
||||||
|
payload = f'{{"rrset_values":["{serveur}"]}}'
|
||||||
|
|
||||||
|
response = requests.post(url, data=payload, headers=headers)
|
||||||
|
return response.json(),response.status_code
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
|
||||||
|
|
||||||
|
#*************************************************
|
223
dockers/apikaz/source/resources/kaz_user.py
Normal file
223
dockers/apikaz/source/resources/kaz_user.py
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
from resources.common_imports import *
|
||||||
|
|
||||||
|
#les variables globales minimum
|
||||||
|
from resources.config import site_url, webmail_url, mdp_url
|
||||||
|
|
||||||
|
|
||||||
|
class Kaz_user(Resource):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
global sympa_liste_info
|
||||||
|
|
||||||
|
self.paheko_users_action_resource = Paheko_users_action()
|
||||||
|
self.paheko_user_resource=Paheko_user()
|
||||||
|
self.ldap_user_resource = Ldap_user()
|
||||||
|
self.password_create_resource = Password_create()
|
||||||
|
self.mattermost_message_resource=Mattermost_message()
|
||||||
|
self.mattermost_user_resource=Mattermost_user()
|
||||||
|
self.mattermost_user_team_resource=Mattermost_user_team()
|
||||||
|
self.mattermost_user_channel_resource=Mattermost_user_channel()
|
||||||
|
self.mattermost_team_resource=Mattermost_team()
|
||||||
|
self.sympa_user_resource=Sympa_user()
|
||||||
|
|
||||||
|
|
||||||
|
#********************************************************************************************
|
||||||
|
@jwt_required()
|
||||||
|
def delete(self):
|
||||||
|
"""
|
||||||
|
Utile pour les tests de createUser. Avant le POST de /kaz/create/users. Ça permet de supprimer/maj les comptes.
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Kaz User
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters: []
|
||||||
|
responses:
|
||||||
|
201:
|
||||||
|
description: Succès
|
||||||
|
401:
|
||||||
|
description: Oops, un soucis quelconque
|
||||||
|
"""
|
||||||
|
#verrou pour empêcher de lancer en même temps la même api
|
||||||
|
try:
|
||||||
|
prefixe="del_user_lock_"
|
||||||
|
if glob(f"{tempfile.gettempdir()}/{prefixe}*"): raise ValueError("ERREUR 0 : api déjà en cours d'utilisation !")
|
||||||
|
lock_file = tempfile.NamedTemporaryFile(prefix=prefixe,delete=True)
|
||||||
|
|
||||||
|
#TODO à remplir à la main
|
||||||
|
liste_emails=["",""]
|
||||||
|
email_secours=""
|
||||||
|
liste_sympa=""
|
||||||
|
|
||||||
|
for email in liste_emails:
|
||||||
|
res, status_code = self.ldap_user_resource.delete(email)
|
||||||
|
res, status_code = self.mattermost_user_resource.delete(email)
|
||||||
|
nom_orga=''.join(random.choice(string.ascii_lowercase) for _ in range(8))
|
||||||
|
res, status_code = self.paheko_user_resource.put(email,"nom_orga",nom_orga)
|
||||||
|
res, status_code = self.paheko_user_resource.put(email,"action_auto","A créer")
|
||||||
|
res, status_code = self.paheko_user_resource.put(email,"email_secours",email_secours)
|
||||||
|
res, status_code = self.sympa_user_resource.delete(email,liste_sympa)
|
||||||
|
res, status_code = self.sympa_user_resource.delete(email_secours,liste_sympa)
|
||||||
|
msg=f"**POST AUTO** suppression de {email} ok"
|
||||||
|
self.mattermost_message_resource.post(message=msg)
|
||||||
|
return "OK", 200
|
||||||
|
|
||||||
|
except ValueError as e:
|
||||||
|
msg=f"(classe: {__class__.__name__} : {e}"
|
||||||
|
self.mattermost_message_resource.post(message=msg)
|
||||||
|
return str(msg), 401
|
||||||
|
|
||||||
|
#********************************************************************************************
|
||||||
|
|
||||||
|
@jwt_required()
|
||||||
|
def post(self):
|
||||||
|
"""
|
||||||
|
Créé un nouveau kaznaute: inscription sur MM / Cloud / email + msg sur MM + email à partir de action="a créer" sur paheko
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Kaz User
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters: []
|
||||||
|
responses:
|
||||||
|
201:
|
||||||
|
description: Succès, kaznaute créé
|
||||||
|
400:
|
||||||
|
description: Oops, rien à créer
|
||||||
|
401:
|
||||||
|
description: Oops, un soucis quelconque
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
#verrou pour empêcher de lancer en même temps la même api
|
||||||
|
prefixe="create_user_lock_"
|
||||||
|
if glob(f"{tempfile.gettempdir()}/{prefixe}*"): raise ValueError("ERREUR 0 : api déjà en cours d'utilisation !")
|
||||||
|
lock_file = tempfile.NamedTemporaryFile(prefix=prefixe,delete=True)
|
||||||
|
|
||||||
|
#qui sont les kaznautes à créer ?
|
||||||
|
liste_kaznautes, status_code = self.paheko_users_action_resource.get("A créer")
|
||||||
|
|
||||||
|
if liste_kaznautes=="pas de résultat": return "ERREUR: paheko non joignable",401
|
||||||
|
|
||||||
|
count=liste_kaznautes['count']
|
||||||
|
if count==0: return "aucun nouveau kaznaute à créer",400
|
||||||
|
|
||||||
|
#au moins un kaznaute à créer
|
||||||
|
for tab in liste_kaznautes['results']:
|
||||||
|
email = tab['email'].lower()
|
||||||
|
|
||||||
|
# est-il déjà dans le ldap ? (mail ou alias)
|
||||||
|
res, status_code = self.ldap_user_resource.get(email)
|
||||||
|
if status_code != 400: raise ValueError(f"ERREUR 1: {email} déjà existant dans ldap. {res}, on arrête tout")
|
||||||
|
|
||||||
|
#test nom orga
|
||||||
|
if tab['admin_orga'] == 1:
|
||||||
|
if tab['nom_orga'] is None:
|
||||||
|
raise ValueError(f"ERREUR 0 sur paheko: {email} : nom_orga vide, on arrête tout")
|
||||||
|
if not bool(re.match(r'^[a-z0-9-]+$', tab['nom_orga'])):
|
||||||
|
raise ValueError(f"ERREUR 0 sur paheko: {email} : nom_orga ({tab['nom_orga']}) incohérent (minuscule/chiffre/-), on arrête tout")
|
||||||
|
|
||||||
|
#test email_secours
|
||||||
|
email_secours = tab['email_secours'].lower()
|
||||||
|
if not validate_email(email_secours): raise EmailNotValidError()
|
||||||
|
|
||||||
|
#test quota
|
||||||
|
quota = tab['quota_disque']
|
||||||
|
if not quota.isdigit(): raise ValueError(f"ERREUR 2: quota non numérique : {quota}, on arrête tout")
|
||||||
|
|
||||||
|
#quel type de test ?
|
||||||
|
#"nom": "ROUSSEAU Mickael",
|
||||||
|
nom, prenom = tab['nom'].split(maxsplit=1)
|
||||||
|
|
||||||
|
#on génère un password
|
||||||
|
password,status_code = self.password_create_resource.get()
|
||||||
|
|
||||||
|
#on créé dans le ldap
|
||||||
|
#à quoi servent prenom/nom dans le ldap ?
|
||||||
|
data = {
|
||||||
|
"prenom": prenom,
|
||||||
|
"nom": nom,
|
||||||
|
"password": password,
|
||||||
|
"email_secours": email_secours,
|
||||||
|
"quota": quota
|
||||||
|
}
|
||||||
|
res, status_code = self.ldap_user_resource.put(email, **data)
|
||||||
|
if status_code != 200: raise ValueError(f"ERREUR 3 sur ldap: {email} : {res}, on arrête tout ")
|
||||||
|
|
||||||
|
#on créé dans MM
|
||||||
|
user = email.split('@')[0]
|
||||||
|
res, status_code = self.mattermost_user_resource.post(user,email,password)
|
||||||
|
if status_code != 200: raise ValueError(f"ERREUR 4 sur MM: {email} : {res}, on arrête tout ")
|
||||||
|
|
||||||
|
# et on affecte à l'équipe kaz
|
||||||
|
res, status_code = self.mattermost_user_team_resource.post(email,"kaz")
|
||||||
|
if status_code != 200: raise ValueError(f"ERREUR 5 sur MM: {email} : {res}, on arrête tout ")
|
||||||
|
|
||||||
|
#et aux 2 canaux de base
|
||||||
|
res, status_code = self.mattermost_user_channel_resource.post(email,"kaz","une-question--un-soucis")
|
||||||
|
if status_code != 200: raise ValueError(f"ERREUR 6 sur MM: {email} : {res}, on arrête tout ")
|
||||||
|
res, status_code = self.mattermost_user_channel_resource.post(email,"kaz","cafe-du-commerce--ouvert-2424h")
|
||||||
|
if status_code != 200: raise ValueError(f"ERREUR 7 sur MM: {email} : {res}, on arrête tout ")
|
||||||
|
|
||||||
|
#on créé une nouvelle équipe ds MM si besoin
|
||||||
|
if tab['admin_orga'] == 1:
|
||||||
|
res, status_code = self.mattermost_team_resource.post(tab['nom_orga'],email)
|
||||||
|
if status_code != 200: raise ValueError(f"ERREUR 8 sur MM: {email} : {res}, on arrête tout ")
|
||||||
|
#BUG: créer la nouvelle équipe n'a pas rendu l'email admin, on le rajoute comme membre simple
|
||||||
|
res, status_code = self.mattermost_user_team_resource.post(email,tab['nom_orga'])
|
||||||
|
if status_code != 200: raise ValueError(f"ERREUR 8.1 sur MM: {email} : {res}, on arrête tout ")
|
||||||
|
|
||||||
|
#on créé dans le cloud genéral
|
||||||
|
#inutile car tous les user du ldap sont user du cloud général.
|
||||||
|
|
||||||
|
#on inscrit email et email_secours à la nl sympa_liste_info
|
||||||
|
res, status_code = self.sympa_user_resource.post(email,sympa_liste_info)
|
||||||
|
if status_code != 200: raise ValueError(f"ERREUR 9 sur Sympa: {email} : {res}, on arrête tout ")
|
||||||
|
res, status_code = self.sympa_user_resource.post(email_secours,sympa_liste_info)
|
||||||
|
if status_code != 200: raise ValueError(f"ERREUR 10 sur Sympa: {email_secours} : {res}, on arrête tout ")
|
||||||
|
|
||||||
|
#on construit/envoie le mail
|
||||||
|
context = {
|
||||||
|
'ADMIN_ORGA': tab['admin_orga'],
|
||||||
|
'NOM': tab['nom'],
|
||||||
|
'EMAIL_SOUHAITE': email,
|
||||||
|
'PASSWORD': password,
|
||||||
|
'QUOTA': tab['quota_disque'],
|
||||||
|
'URL_WEBMAIL': webmail_url,
|
||||||
|
'URL_AGORA': mattermost_url,
|
||||||
|
'URL_MDP': mdp_url,
|
||||||
|
'URL_LISTE': sympa_url,
|
||||||
|
'URL_SITE': site_url,
|
||||||
|
'URL_CLOUD': cloud_url
|
||||||
|
}
|
||||||
|
subject="KAZ: confirmation d'inscription !"
|
||||||
|
sender=app.config['MAIL_USERNAME']
|
||||||
|
reply_to = app.config['MAIL_REPLY_TO']
|
||||||
|
msg = Message(subject=subject, sender=sender, reply_to=reply_to, recipients=[email,email_secours])
|
||||||
|
msg.html = render_template('email_inscription.html', **context)
|
||||||
|
mail.send(msg)
|
||||||
|
|
||||||
|
#on met le flag paheko action à Aucune
|
||||||
|
res, status_code = self.paheko_user_resource.put(email,"action_auto","Aucune")
|
||||||
|
if status_code != 200: raise ValueError(f"ERREUR 12 sur paheko: {email} : {res}, on arrête tout ")
|
||||||
|
|
||||||
|
#on post sur MM pour dire ok
|
||||||
|
msg=f"**POST AUTO** Inscription réussie pour {email} avec le secours {email_secours} Bisou!"
|
||||||
|
self.mattermost_message_resource.post(message=msg)
|
||||||
|
|
||||||
|
return "fin des inscriptions", 201
|
||||||
|
|
||||||
|
except EmailNotValidError as e:
|
||||||
|
msg=f"classe: {__class__.__name__} : ERREUR 13 : email_secours : {email_secours} " + str(e) +", on arrête tout"
|
||||||
|
self.mattermost_message_resource.post(message=msg)
|
||||||
|
return msg, 401
|
||||||
|
except ValueError as e:
|
||||||
|
msg=f"(classe: {__class__.__name__} : {e}"
|
||||||
|
self.mattermost_message_resource.post(message=msg)
|
||||||
|
return str(msg), 401
|
||||||
|
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
|
||||||
|
|
341
dockers/apikaz/source/resources/ldap.py
Normal file
341
dockers/apikaz/source/resources/ldap.py
Normal file
@ -0,0 +1,341 @@
|
|||||||
|
from resources.common_imports import *
|
||||||
|
|
||||||
|
#les variables globales minimum
|
||||||
|
from resources.config import ldap_admin, ldap_pass, ldap_root, ldap_host
|
||||||
|
|
||||||
|
class Ldap_user(Resource):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
global ldap_admin, ldap_pass, ldap_root, ldap_host
|
||||||
|
self.ldap_admin = ldap_admin
|
||||||
|
self.ldap_pass = ldap_pass
|
||||||
|
self.ldap_root = ldap_root
|
||||||
|
self.ldap_host = f"ldap://{ldap_host}"
|
||||||
|
|
||||||
|
def _connect_ldap(self):
|
||||||
|
ldap_connection = ldap.initialize(self.ldap_host)
|
||||||
|
ldap_connection.simple_bind_s("cn={},{}".format(self.ldap_admin, self.ldap_root), self.ldap_pass)
|
||||||
|
return ldap_connection
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_valid_field(cls, field):
|
||||||
|
allowed_fields = ['mailDeSecours', 'mailEnabled', 'nextcloudEnabled', 'mobilizonEnabled', 'agoraEnabled', 'userPassword', 'identifiantKaz', 'mailAlias', 'quota']
|
||||||
|
return field in allowed_fields
|
||||||
|
|
||||||
|
@jwt_required()
|
||||||
|
def get(self, email):
|
||||||
|
"""
|
||||||
|
Vérifier si un utilisateur avec cet email existe dans le LDAP soit comme mail principal soit comme alias
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Ldap
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: email
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Existe
|
||||||
|
400:
|
||||||
|
description: N'existe pas
|
||||||
|
401:
|
||||||
|
description: oops, email invalide
|
||||||
|
402:
|
||||||
|
description: oops, autre erreur
|
||||||
|
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
|
||||||
|
if not validate_email(email):
|
||||||
|
return "Adresse e-mail non valide", 400
|
||||||
|
|
||||||
|
ldap_connection = self._connect_ldap()
|
||||||
|
|
||||||
|
#result = ldap_connection.search_s("ou=users,{}".format(self.ldap_root), ldap.SCOPE_SUBTREE, "(cn={})".format(email))
|
||||||
|
|
||||||
|
# Créer une chaîne de filtre pour rechercher dans les champs "cn" et "mailAlias"
|
||||||
|
filter_str = "(|(cn={})(mailAlias={}))".format(email, email)
|
||||||
|
result = ldap_connection.search_s("ou=users,{}".format(self.ldap_root), ldap.SCOPE_SUBTREE, filter_str)
|
||||||
|
|
||||||
|
ldap_connection.unbind_s()
|
||||||
|
|
||||||
|
if result:
|
||||||
|
return True, 200
|
||||||
|
else:
|
||||||
|
return False, 400
|
||||||
|
|
||||||
|
except EmailNotValidError as e:
|
||||||
|
return str(e), 401
|
||||||
|
|
||||||
|
except ldap.LDAPError as e:
|
||||||
|
return str(e), 402
|
||||||
|
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
@jwt_required()
|
||||||
|
def delete(self, email):
|
||||||
|
"""
|
||||||
|
Supprimer un utilisateur du LDAP par son adresse e-mail
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Ldap
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: email
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Utilisateur supprimé avec succès
|
||||||
|
404:
|
||||||
|
description: Utilisateur non trouvé dans le LDAP
|
||||||
|
400:
|
||||||
|
description: Erreur lors de la suppression de l'utilisateur
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if not validate_email(email):
|
||||||
|
return "Adresse e-mail non valide", 400
|
||||||
|
|
||||||
|
ldap_connection = self._connect_ldap()
|
||||||
|
|
||||||
|
# Recherche de l'utilisateur
|
||||||
|
result = ldap_connection.search_s("ou=users,{}".format(self.ldap_root), ldap.SCOPE_SUBTREE, "(cn={})".format(email))
|
||||||
|
|
||||||
|
if not result:
|
||||||
|
return False, 404 # Utilisateur non trouvé
|
||||||
|
|
||||||
|
# Récupération du DN de l'utilisateur
|
||||||
|
dn = result[0][0]
|
||||||
|
|
||||||
|
# Suppression de l'utilisateur
|
||||||
|
ldap_connection.delete_s(dn)
|
||||||
|
ldap_connection.unbind_s()
|
||||||
|
|
||||||
|
return True, 200 # Utilisateur supprimé avec succès
|
||||||
|
|
||||||
|
except ldap.NO_SUCH_OBJECT:
|
||||||
|
return False, 404 # Utilisateur non trouvé
|
||||||
|
except ldap.LDAPError as e:
|
||||||
|
return str(e), 400 # Erreur lors de la suppression
|
||||||
|
except EmailNotValidError as e:
|
||||||
|
return str(e), 400
|
||||||
|
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
@jwt_required()
|
||||||
|
def post(self, email):
|
||||||
|
"""
|
||||||
|
Ajouter, supprimer ou modifier un champ pour l'utilisateur LDAP
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Ldap
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: email
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
- in: query
|
||||||
|
name: action
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
enum: ['add', 'delete', 'modify']
|
||||||
|
- in: body
|
||||||
|
name: data
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
field:
|
||||||
|
type: string
|
||||||
|
enum: ['mailDeSecours', 'mailEnabled', 'nextcloudEnabled', 'mobilizonEnabled', 'agoraEnabled', 'userPassword', 'identifiantKaz', 'mailAlias', 'quota']
|
||||||
|
description: Le champ à ajouter, supprimer ou modifier (par exemple, mailDeSecours, mailAlias, etc.)
|
||||||
|
value:
|
||||||
|
type: string
|
||||||
|
description: La valeur à ajouter, supprimer ou modifier pour le champ spécifié
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Opération réussie
|
||||||
|
404:
|
||||||
|
description: Utilisateur non trouvé dans le LDAP
|
||||||
|
400:
|
||||||
|
description: Erreur lors de l'opération
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
if not validate_email(email):
|
||||||
|
return "Adresse e-mail non valide", 400
|
||||||
|
|
||||||
|
action = request.args.get('action')
|
||||||
|
field = request.json.get('field')
|
||||||
|
value = request.json.get('value')
|
||||||
|
|
||||||
|
if not action or not field or not value:
|
||||||
|
return "Action, champ ou valeur manquant", 400
|
||||||
|
|
||||||
|
if not self.is_valid_field(field):
|
||||||
|
return "Champ non autorisé", 400
|
||||||
|
|
||||||
|
ldap_connection = self._connect_ldap()
|
||||||
|
result = ldap_connection.search_s("ou=users,{}".format(self.ldap_root), ldap.SCOPE_SUBTREE, "(cn={})".format(email))
|
||||||
|
|
||||||
|
if not result:
|
||||||
|
return False, 404
|
||||||
|
|
||||||
|
dn = result[0][0]
|
||||||
|
|
||||||
|
if field == 'userPassword' and (action == 'add' or action == 'modify'):
|
||||||
|
password_chiffre = sha512_crypt.hash(value)
|
||||||
|
value = "{{CRYPT}}{}".format(password_chiffre)
|
||||||
|
|
||||||
|
if action == 'add':
|
||||||
|
mod_attrs = [(ldap.MOD_ADD, field, value.encode('utf-8'))]
|
||||||
|
|
||||||
|
elif action == 'delete':
|
||||||
|
mod_attrs = [(ldap.MOD_DELETE, field, value.encode('utf-8'))]
|
||||||
|
|
||||||
|
elif action == 'modify':
|
||||||
|
|
||||||
|
if field == 'quota':
|
||||||
|
mail_quota_value = value + 'G'
|
||||||
|
nextcloud_quota_value = value + " GB"
|
||||||
|
mod_attrs = [
|
||||||
|
(ldap.MOD_REPLACE, 'quota', value.encode('utf-8')),
|
||||||
|
(ldap.MOD_REPLACE, 'mailQuota', mail_quota_value.encode('utf-8')),
|
||||||
|
(ldap.MOD_REPLACE, 'nextcloudQuota', nextcloud_quota_value.encode('utf-8'))
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
mod_attrs = [(ldap.MOD_REPLACE, field, value.encode('utf-8'))]
|
||||||
|
|
||||||
|
else:
|
||||||
|
return "Action non valide", 400
|
||||||
|
|
||||||
|
ldap_connection.modify_s(dn, mod_attrs)
|
||||||
|
ldap_connection.unbind_s()
|
||||||
|
|
||||||
|
return True, 200
|
||||||
|
|
||||||
|
except ldap.NO_SUCH_OBJECT:
|
||||||
|
return False, 404
|
||||||
|
except ldap.LDAPError as e:
|
||||||
|
return str(e), 400
|
||||||
|
except EmailNotValidError as e:
|
||||||
|
return str(e), 400
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
@jwt_required()
|
||||||
|
def put(self, email, **kwargs):
|
||||||
|
"""
|
||||||
|
Créer une nouvelle entrée dans le LDAP pour un nouvel utilisateur. QUESTION: A QUOI SERVENT PRENOM/NOM/IDENT_KAZ DANS LE LDAP ? POURQUOI 3 QUOTA ?
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Ldap
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: email
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
- in: body
|
||||||
|
name: data
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
prenom:
|
||||||
|
type: string
|
||||||
|
description: Prénom de l'utilisateur
|
||||||
|
nom:
|
||||||
|
type: string
|
||||||
|
description: Nom de l'utilisateur
|
||||||
|
password:
|
||||||
|
type: string
|
||||||
|
description: Mot de passe de l'utilisateur
|
||||||
|
email_secours:
|
||||||
|
type: string
|
||||||
|
description: Adresse e-mail de secours
|
||||||
|
quota:
|
||||||
|
type: string
|
||||||
|
description: Quota de l'utilisateur
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Utilisateur ajouté avec succès
|
||||||
|
400:
|
||||||
|
description: Erreur lors de l'ajout de l'utilisateur
|
||||||
|
406:
|
||||||
|
description: Erreur utilisateur déjà existant
|
||||||
|
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
|
||||||
|
if kwargs: # appel depuis une autre api
|
||||||
|
email_secours = kwargs.get('email_secours')
|
||||||
|
prenom = kwargs.get('prenom')
|
||||||
|
nom = kwargs.get('nom')
|
||||||
|
password = kwargs.get('password')
|
||||||
|
quota = kwargs.get('quota')
|
||||||
|
|
||||||
|
else: # appel depuis swagger
|
||||||
|
email_secours = request.json.get('email_secours')
|
||||||
|
nom = request.json.get('nom')
|
||||||
|
prenom = request.json.get('prenom')
|
||||||
|
password = request.json.get('password')
|
||||||
|
quota = request.json.get('quota')
|
||||||
|
|
||||||
|
password_chiffre = sha512_crypt.hash(password)
|
||||||
|
|
||||||
|
if not validate_email(email) or not validate_email(email_secours):
|
||||||
|
return "Adresse e-mail ou secours non valide", 400
|
||||||
|
|
||||||
|
#le user existe t-il déjà ?
|
||||||
|
ldap_connection = self._connect_ldap()
|
||||||
|
result = ldap_connection.search_s("ou=users,{}".format(self.ldap_root), ldap.SCOPE_SUBTREE, "(cn={})".format(email))
|
||||||
|
|
||||||
|
if result:
|
||||||
|
return "User déjà existant", 406
|
||||||
|
|
||||||
|
# Construire le DN
|
||||||
|
dn = f"cn={email},ou=users,{ldap_root}"
|
||||||
|
|
||||||
|
mod_attrs = [
|
||||||
|
('objectClass', [b'inetOrgPerson', b'PostfixBookMailAccount', b'nextcloudAccount', b'kaznaute']),
|
||||||
|
('sn', f'{prenom} {nom}'.encode('utf-8')),
|
||||||
|
('mail', email.encode('utf-8')),
|
||||||
|
('mailEnabled', b'TRUE'),
|
||||||
|
('mailGidNumber', b'5000'),
|
||||||
|
('mailHomeDirectory', f"/var/mail/{email.split('@')[1]}/{email.split('@')[0]}/".encode('utf-8')),
|
||||||
|
('mailQuota', f'{quota}G'.encode('utf-8')),
|
||||||
|
('mailStorageDirectory', f"maildir:/var/mail/{email.split('@')[1]}/{email.split('@')[0]}/".encode('utf-8')),
|
||||||
|
('mailUidNumber', b'5000'),
|
||||||
|
('mailDeSecours', email_secours.encode('utf-8')),
|
||||||
|
('identifiantKaz', f'{prenom.lower()}.{nom.lower()}'.encode('utf-8')),
|
||||||
|
('quota', str(quota).encode('utf-8')),
|
||||||
|
('nextcloudEnabled', b'TRUE'),
|
||||||
|
('nextcloudQuota', f'{quota} GB'.encode('utf-8')),
|
||||||
|
('mobilizonEnabled', b'TRUE'),
|
||||||
|
('agoraEnabled', b'TRUE'),
|
||||||
|
('userPassword', f'{{CRYPT}}{password_chiffre}'.encode('utf-8')),
|
||||||
|
('cn', email.encode('utf-8'))
|
||||||
|
]
|
||||||
|
|
||||||
|
ldap_connection.add_s(dn, mod_attrs)
|
||||||
|
ldap_connection.unbind_s()
|
||||||
|
|
||||||
|
return "Utilisateur créé dand le ldap", 200
|
||||||
|
|
||||||
|
except ldap.LDAPError as e:
|
||||||
|
return str(e), 400
|
||||||
|
except EmailNotValidError as e:
|
||||||
|
return str(e), 400
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
|
||||||
|
|
||||||
|
|
394
dockers/apikaz/source/resources/mattermost.py
Normal file
394
dockers/apikaz/source/resources/mattermost.py
Normal file
@ -0,0 +1,394 @@
|
|||||||
|
from resources.common_imports import *
|
||||||
|
|
||||||
|
#les variables globales minimum
|
||||||
|
from resources.config import mattermost_user, mattermost_pass, mattermost_url
|
||||||
|
|
||||||
|
# on utilise mmctl et pas l'apiv4 de MM
|
||||||
|
# pourquoi ? passe que mmctl déjà utilisé dans les scripts kaz.
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
|
||||||
|
def Mattermost_authenticate():
|
||||||
|
# Authentification sur MM
|
||||||
|
global mattermost_url, mattermost_user, mattermost_pass
|
||||||
|
cmd = f"/mm/mmctl auth login {mattermost_url} --name local-server --username {mattermost_user} --password {mattermost_pass}"
|
||||||
|
subprocess.run(cmd, shell=True, stderr=subprocess.STDOUT, check=True)
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
|
||||||
|
class Mattermost_message(Resource):
|
||||||
|
|
||||||
|
@jwt_required()
|
||||||
|
def post(self,message,equipe="kaz",canal="creation-comptes"):
|
||||||
|
"""
|
||||||
|
Envoyer un message dans une Equipe/Canal de MM
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Mattermost
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: equipe
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
- in: path
|
||||||
|
name: canal
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
- in: path
|
||||||
|
name: message
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Affiche un message dans un canal d'une équipe
|
||||||
|
500:
|
||||||
|
description: oops
|
||||||
|
"""
|
||||||
|
Mattermost_authenticate()
|
||||||
|
|
||||||
|
try:
|
||||||
|
cmd="/mm/mmctl post create "+equipe+":"+canal+" --message "+ "\"" + message + "\""
|
||||||
|
output = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
|
||||||
|
return "Message envoyé", 200
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
return "Message non envoyé", 500
|
||||||
|
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
|
||||||
|
class Mattermost_user(Resource):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
Mattermost_authenticate()
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
@jwt_required()
|
||||||
|
def get(self,user):
|
||||||
|
"""
|
||||||
|
Le user existe t-il sur MM ?
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Mattermost User
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: user
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
description: possible d'entrer un username, un email
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Existe
|
||||||
|
404:
|
||||||
|
description: N'existe pas
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
cmd = f"/mm/mmctl user search {user} --json"
|
||||||
|
user_list_output = subprocess.check_output(cmd, shell=True)
|
||||||
|
return 200 # Le nom d'utilisateur existe
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
return 404 # Le nom d'utilisateur n'existe pas
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
@jwt_required()
|
||||||
|
def post(self,user,email,password):
|
||||||
|
"""
|
||||||
|
Créer un utilisateur sur MM
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Mattermost User
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: user
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
- in: path
|
||||||
|
name: email
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
- in: path
|
||||||
|
name: password
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Utilisateur créé
|
||||||
|
400:
|
||||||
|
description: oops, Utilisateur non créé
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Création de l'utilisateur
|
||||||
|
try:
|
||||||
|
cmd = f"/mm/mmctl user create --email {email} --username {user} --password {password}"
|
||||||
|
output = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
|
||||||
|
return output.decode("utf-8"), 200
|
||||||
|
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
return e.output.decode("utf-8"), 400
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
|
||||||
|
@jwt_required()
|
||||||
|
def delete(self,email):
|
||||||
|
"""
|
||||||
|
Supprimer un utilisateur sur MM
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Mattermost User
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: email
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Utilisateur supprimé
|
||||||
|
400:
|
||||||
|
description: oops, Utilisateur non supprimé
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
cmd = f"/mm/mmctl user delete {email} --confirm"
|
||||||
|
output = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
|
||||||
|
return output.decode("utf-8"), 200
|
||||||
|
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
return e.output.decode("utf-8"), 400
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
|
||||||
|
@jwt_required()
|
||||||
|
def put(self,email,new_password):
|
||||||
|
"""
|
||||||
|
Changer un password pour un utilisateur de MM
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Mattermost User
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: email
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
- in: path
|
||||||
|
name: new_password
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Mot de passe de l'Utilisateur changé
|
||||||
|
400:
|
||||||
|
description: oops, Mot de passe de l'Utilisateur inchangé
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
cmd = f"/mm/mmctl user change-password {email} --password {new_password}"
|
||||||
|
output = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
|
||||||
|
return output.decode("utf-8"), 200
|
||||||
|
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
return e.output.decode("utf-8"), 400
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
|
||||||
|
class Mattermost_user_team(Resource):
|
||||||
|
|
||||||
|
@jwt_required()
|
||||||
|
def post(self,email,equipe):
|
||||||
|
"""
|
||||||
|
Affecte un utilisateur à une équipe MM
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Mattermost Team
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: email
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
- in: path
|
||||||
|
name: equipe
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: l'utilisateur a bien été affecté à l'équipe
|
||||||
|
400:
|
||||||
|
description: oops, Utilisateur non affecté
|
||||||
|
"""
|
||||||
|
Mattermost_authenticate()
|
||||||
|
|
||||||
|
try:
|
||||||
|
cmd = f"/mm/mmctl team users add {equipe} {email}"
|
||||||
|
output = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
|
||||||
|
return output.decode("utf-8"), 200
|
||||||
|
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
return e.output.decode("utf-8"), 400
|
||||||
|
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
|
||||||
|
class Mattermost_user_channel(Resource):
|
||||||
|
@jwt_required()
|
||||||
|
def post(self,email,equipe,canal):
|
||||||
|
"""
|
||||||
|
Affecte un utilisateur à un canal MM
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Mattermost
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: email
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
- in: path
|
||||||
|
name: equipe
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
- in: path
|
||||||
|
name: canal
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: l'utilisateur a bien été affecté au canal
|
||||||
|
400:
|
||||||
|
description: oops, Utilisateur non affecté
|
||||||
|
"""
|
||||||
|
Mattermost_authenticate()
|
||||||
|
|
||||||
|
try:
|
||||||
|
cmd = f'/mm/mmctl channel users add {equipe}:{canal} {email}'
|
||||||
|
output = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
|
||||||
|
return output.decode("utf-8"), 200
|
||||||
|
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
return e.output.decode("utf-8"), 400
|
||||||
|
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
|
||||||
|
class Mattermost_team(Resource):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
Mattermost_authenticate()
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
|
||||||
|
@jwt_required()
|
||||||
|
def get(self):
|
||||||
|
"""
|
||||||
|
Lister les équipes sur MM
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Mattermost Team
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters: []
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: liste des équipes
|
||||||
|
400:
|
||||||
|
description: oops, Equipe non supprimée
|
||||||
|
"""
|
||||||
|
Mattermost_authenticate()
|
||||||
|
|
||||||
|
try:
|
||||||
|
cmd = f"/mm/mmctl team list --disable-pager"
|
||||||
|
output = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
|
||||||
|
data_list = output.decode("utf-8").strip().split('\n')
|
||||||
|
data_list.pop()
|
||||||
|
return data_list, 200
|
||||||
|
#return jsonify(data_list),200
|
||||||
|
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
return e.output.decode("utf-8"), 400
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
@jwt_required()
|
||||||
|
def post(self,equipe,email):
|
||||||
|
"""
|
||||||
|
Créer une équipe sur MM et affecter un admin si email est renseigné (set admin marche pô)
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Mattermost Team
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: equipe
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
- in: path
|
||||||
|
name: email
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
description: admin de l'équipe
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Equipe créée
|
||||||
|
400:
|
||||||
|
description: oops, Equipe non créée
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
#DANGER: l'option --email ne rend pas le user admin de l'équipe comme c'est indiqué dans la doc :(
|
||||||
|
cmd = f"/mm/mmctl team create --name {equipe} --display-name {equipe} --private --email {email}"
|
||||||
|
output = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
|
#Workaround: on récup l'id du user et de l'équipe pour affecter le rôle "scheme_admin": true, "scheme_user": true avec l'api MM classique.
|
||||||
|
#TODO:
|
||||||
|
|
||||||
|
return output.decode("utf-8"), 200
|
||||||
|
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
return e.output.decode("utf-8"), 400
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
@jwt_required()
|
||||||
|
def delete(self,equipe):
|
||||||
|
"""
|
||||||
|
Supprimer une équipe sur MM
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Mattermost Team
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: equipe
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Equipe supprimée
|
||||||
|
400:
|
||||||
|
description: oops, Equipe non supprimée
|
||||||
|
"""
|
||||||
|
Mattermost_authenticate()
|
||||||
|
|
||||||
|
try:
|
||||||
|
cmd = f"/mm/mmctl team delete {equipe} --confirm"
|
||||||
|
output = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
|
||||||
|
return output.decode("utf-8"), 200
|
||||||
|
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
return e.output.decode("utf-8"), 400
|
||||||
|
|
||||||
|
#*************************************************
|
256
dockers/apikaz/source/resources/paheko.py
Normal file
256
dockers/apikaz/source/resources/paheko.py
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
from resources.common_imports import *
|
||||||
|
|
||||||
|
#les variables globales minimum
|
||||||
|
from resources.config import paheko_ident, paheko_pass, paheko_url
|
||||||
|
|
||||||
|
|
||||||
|
class Paheko_categories(Resource):
|
||||||
|
|
||||||
|
@jwt_required()
|
||||||
|
def get(self):
|
||||||
|
"""
|
||||||
|
Récupérer les catégories Paheko avec le compteur associé
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Paheko
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters: []
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Liste des catégories Paheko
|
||||||
|
404:
|
||||||
|
description: oops
|
||||||
|
"""
|
||||||
|
global paheko_ident, paheko_pass, paheko_url
|
||||||
|
|
||||||
|
auth = (paheko_ident, paheko_pass)
|
||||||
|
api_url = paheko_url + '/api/user/categories'
|
||||||
|
|
||||||
|
response = requests.get(api_url, auth=auth)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
data = response.json()
|
||||||
|
return jsonify(data)
|
||||||
|
else:
|
||||||
|
return jsonify({'error': 'La requête a échoué'}), response.status_code
|
||||||
|
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
|
||||||
|
class Paheko_users(Resource):
|
||||||
|
|
||||||
|
@jwt_required()
|
||||||
|
def get(self,categorie):
|
||||||
|
"""
|
||||||
|
Afficher les membres d'une catégorie Paheko
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Paheko
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: categorie
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Liste des membres une catégorie Paheko
|
||||||
|
404:
|
||||||
|
description: oops
|
||||||
|
"""
|
||||||
|
|
||||||
|
global paheko_ident, paheko_pass, paheko_url
|
||||||
|
|
||||||
|
auth = (paheko_ident, paheko_pass)
|
||||||
|
if not categorie.isdigit():
|
||||||
|
return 'Id de category non valide', 400
|
||||||
|
|
||||||
|
api_url = paheko_url + '/api/user/category/'+categorie+'.json'
|
||||||
|
|
||||||
|
response = requests.get(api_url, auth=auth)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
data = response.json()
|
||||||
|
return jsonify(data)
|
||||||
|
else:
|
||||||
|
return jsonify({'error': 'La requête a échoué'}), response.status_code
|
||||||
|
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
|
||||||
|
class Paheko_user(Resource):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
global paheko_ident, paheko_pass, paheko_url
|
||||||
|
self.paheko_ident = paheko_ident
|
||||||
|
self.paheko_pass = paheko_pass
|
||||||
|
self.paheko_url = paheko_url
|
||||||
|
self.auth = (self.paheko_ident, self.paheko_pass)
|
||||||
|
|
||||||
|
@jwt_required()
|
||||||
|
def get(self,ident):
|
||||||
|
"""
|
||||||
|
Afficher un membre de Paheko par son email kaz ou son numéro ou le non court de l'orga
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Paheko
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: ident
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
description: possible d'entrer un numéro, un email, le nom court de l'orga
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Existe et affiche
|
||||||
|
404:
|
||||||
|
description: N'existe pas
|
||||||
|
"""
|
||||||
|
|
||||||
|
emailmatchregexp = re.compile(r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$")
|
||||||
|
|
||||||
|
if emailmatchregexp.match(ident):
|
||||||
|
data = { "sql": f"select * from users where email='{ident}' or alias = '{ident}'" }
|
||||||
|
api_url = self.paheko_url + '/api/sql/'
|
||||||
|
response = requests.post(api_url, auth=self.auth, data=data)
|
||||||
|
#TODO: if faut Rechercher count et vérifier que = 1 et supprimer le count=1 dans la réponse
|
||||||
|
elif ident.isdigit():
|
||||||
|
api_url = self.paheko_url + '/api/user/'+ident
|
||||||
|
response = requests.get(api_url, auth=self.auth)
|
||||||
|
else:
|
||||||
|
nomorga = re.sub(r'\W+', '', ident) # on vire les caractères non alphanumérique
|
||||||
|
data = { "sql": f"select * from users where admin_orga=1 and nom_orga='{nomorga}'" }
|
||||||
|
api_url = self.paheko_url + '/api/sql/'
|
||||||
|
response = requests.post(api_url, auth=self.auth, data=data)
|
||||||
|
#TODO:if faut Rechercher count et vérifier que = 1 et supprimer le count=1 dans la réponse
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
data = response.json()
|
||||||
|
if data["count"] == 1:
|
||||||
|
return jsonify(data["results"][0])
|
||||||
|
elif data["count"] == 0:
|
||||||
|
return "pas de résultat", 400
|
||||||
|
else:
|
||||||
|
return "Plusieurs utilisateurs correspondent ?!", 400
|
||||||
|
else:
|
||||||
|
#return jsonify({'error': 'La requête a échoué'}), response.status_code
|
||||||
|
return "pas de résultat", response.status_code
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
|
||||||
|
@jwt_required()
|
||||||
|
def put(self,ident,field,new_value):
|
||||||
|
"""
|
||||||
|
Modifie la valeur d'un champ d'un membre paheko (ident= numéro paheko ou email kaz)
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Paheko
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: ident
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
description: possible d'entrer le numéro paheko, un email kaz
|
||||||
|
- in: path
|
||||||
|
name: field
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
description: un champ de la table users de la base paheko
|
||||||
|
- in: path
|
||||||
|
name: new_value
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
description: la nouvelle valeur à remplacer
|
||||||
|
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Modification effectuée avec succès
|
||||||
|
400:
|
||||||
|
description: Oops, ident non trouvé ou incohérent
|
||||||
|
404:
|
||||||
|
description: Oops, modification du champ KO
|
||||||
|
"""
|
||||||
|
|
||||||
|
#récupérer le numero paheko si on fournit un email kaz
|
||||||
|
emailmatchregexp = re.compile(r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$")
|
||||||
|
if emailmatchregexp.match(ident):
|
||||||
|
data = { "sql": f"select id from users where email='{ident}'" }
|
||||||
|
api_url = self.paheko_url + '/api/sql/'
|
||||||
|
response = requests.post(api_url, auth=self.auth, data=data)
|
||||||
|
if response.status_code == 200:
|
||||||
|
#on extrait l'id de la réponse
|
||||||
|
data = response.json()
|
||||||
|
if data['count'] == 0:
|
||||||
|
return "email non trouvé", 400
|
||||||
|
elif data['count'] > 1:
|
||||||
|
return "trop de résultat", 400
|
||||||
|
else:
|
||||||
|
#OK
|
||||||
|
ident = data['results'][0]['id']
|
||||||
|
else:
|
||||||
|
return "pas de résultat", response.status_code
|
||||||
|
elif not ident.isdigit():
|
||||||
|
return "Identifiant utilisateur invalide", response.status_code
|
||||||
|
|
||||||
|
regexp = re.compile("[^a-zA-Z0-9 \\r\\n\\t" + re.escape(string.punctuation) + "]")
|
||||||
|
valeur = regexp.sub('',new_value) # mouais, il faudrait être beaucoup plus précis ici en fonction des champs qu'on accepte...
|
||||||
|
|
||||||
|
champ = re.sub(r'\W+','',field) # pas de caractères non alphanumériques ici, dans l'idéal, c'est à choisir dans une liste plutot
|
||||||
|
|
||||||
|
api_url = self.paheko_url + '/api/user/'+str(ident)
|
||||||
|
payload = {champ: valeur}
|
||||||
|
response = requests.post(api_url, auth=self.auth, data=payload)
|
||||||
|
return response.json(),response.status_code
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
|
||||||
|
|
||||||
|
#*************************************************
|
||||||
|
|
||||||
|
class Paheko_users_action(Resource):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
global paheko_ident, paheko_pass, paheko_url
|
||||||
|
self.paheko_ident = paheko_ident
|
||||||
|
self.paheko_pass = paheko_pass
|
||||||
|
self.paheko_url = paheko_url
|
||||||
|
|
||||||
|
@jwt_required()
|
||||||
|
def get(self, action):
|
||||||
|
"""
|
||||||
|
retourne tous les membres de paheko avec une action à mener (création du compte kaz / modification...)
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Paheko
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: action
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
enum: ['A créer','A modifier','En attente','Aucune']
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: liste des nouveaux kaznautes à créer
|
||||||
|
404:
|
||||||
|
description: aucun nouveau kaznaute à créer
|
||||||
|
"""
|
||||||
|
auth = (self.paheko_ident, self.paheko_pass)
|
||||||
|
|
||||||
|
api_url = self.paheko_url + '/api/sql/'
|
||||||
|
payload = { "sql": f"select * from users where action_auto='{action}'" }
|
||||||
|
response = requests.post(api_url, auth=auth, data=payload)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
return response.json(),200
|
||||||
|
else:
|
||||||
|
return "pas de résultat", response.status_code
|
||||||
|
|
||||||
|
|
34
dockers/apikaz/source/resources/password.py
Normal file
34
dockers/apikaz/source/resources/password.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
from resources.common_imports import *
|
||||||
|
|
||||||
|
#les variables globales minimum
|
||||||
|
#from resources.config import
|
||||||
|
|
||||||
|
|
||||||
|
class Password_create(Resource):
|
||||||
|
@jwt_required()
|
||||||
|
def get(self):
|
||||||
|
"""
|
||||||
|
créer un password qui colle avec les appli kaz
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Password
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters: []
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: le password
|
||||||
|
404:
|
||||||
|
description: oops
|
||||||
|
"""
|
||||||
|
global new_password
|
||||||
|
|
||||||
|
cmd="apg -n 1 -m 10 -M NCL -d"
|
||||||
|
try:
|
||||||
|
output = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
|
||||||
|
new_password="_"+output.decode("utf-8")+"_"
|
||||||
|
return new_password,200
|
||||||
|
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
return e.output.decode("utf-8"), 400
|
||||||
|
|
64
dockers/apikaz/source/resources/quota.py
Normal file
64
dockers/apikaz/source/resources/quota.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
from resources.common_imports import *
|
||||||
|
|
||||||
|
#les variables globales minimum
|
||||||
|
from resources.config import serveur_imap, mot_de_passe_mail
|
||||||
|
|
||||||
|
|
||||||
|
class Quota(Resource):
|
||||||
|
|
||||||
|
#https://doc.dovecot.org/configuration_manual/authentication/master_users/
|
||||||
|
|
||||||
|
#https://blog.debugo.fr/serveur-messagerie-dovecot/
|
||||||
|
# sur kazkouil.fr, j'ai modifié /etc/dovecot/conf.d/20-lmtp.conf
|
||||||
|
#mail_plugins = $mail_plugins sieve quota
|
||||||
|
|
||||||
|
@jwt_required()
|
||||||
|
def get(self, email):
|
||||||
|
"""
|
||||||
|
Récupérer la place prise par une BAL (EN COURS)
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Quota
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: email
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Succès, taille d'une BAL'
|
||||||
|
400:
|
||||||
|
description: Oops, échec dans l'affichage de la taille d'une BAL
|
||||||
|
"""
|
||||||
|
global serveur_imap, mot_de_passe_mail
|
||||||
|
|
||||||
|
try:
|
||||||
|
if validate_email(email):
|
||||||
|
# Connexion au serveur IMAP
|
||||||
|
mail = imaplib.IMAP4_SSL(serveur_imap)
|
||||||
|
mail.login(email, mot_de_passe_mail)
|
||||||
|
|
||||||
|
#res, data = mail.select("INBOX")
|
||||||
|
#return data[0].decode("utf-8"), 200
|
||||||
|
|
||||||
|
# Requête pour obtenir le quota de la boîte aux lettres
|
||||||
|
# res, data = mail.getquota("INBOX")
|
||||||
|
# return str(data[0]).split()
|
||||||
|
|
||||||
|
res, data = mail.getquotaroot("INBOX")
|
||||||
|
return str(data[1]).split()[3]+" KB"
|
||||||
|
|
||||||
|
# Fermeture de la connexion
|
||||||
|
mail.close()
|
||||||
|
mail.logout()
|
||||||
|
|
||||||
|
else:
|
||||||
|
return "Email non valide", 400
|
||||||
|
|
||||||
|
except imaplib.IMAP4.error as e:
|
||||||
|
return str(e), 400 # Retourne le message d'erreur et un code d'erreur 400
|
||||||
|
except EmailNotValidError as e:
|
||||||
|
return str(e), 400 # Retourne le message d'erreur et un code d'erreur 400
|
||||||
|
|
81
dockers/apikaz/source/resources/sympa.py
Normal file
81
dockers/apikaz/source/resources/sympa.py
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
|
||||||
|
from resources.common_imports import *
|
||||||
|
|
||||||
|
#les variables globales minimum
|
||||||
|
from resources.config import sympa_ident, sympa_pass, sympa_url, sympa_liste_info, MAIL_USERNAME
|
||||||
|
|
||||||
|
|
||||||
|
class Sympa_user(Resource):
|
||||||
|
def __init__(self):
|
||||||
|
global sympa_ident, sympa_pass, sympa_url,MAIL_USERNAME
|
||||||
|
self.sympa_ident = sympa_ident
|
||||||
|
self.sympa_pass = sympa_pass
|
||||||
|
self.sympa_url = sympa_url
|
||||||
|
|
||||||
|
def _execute_sympa_command(self, email, liste, service):
|
||||||
|
try:
|
||||||
|
if validate_email(email) and validate_email(liste):
|
||||||
|
cmd = f'export PERL5LIB=/usr/src/app/:$PERL5LIB && /usr/src/app/Sympa/sympa_soap_client.pl --soap_url={self.sympa_url}/sympasoap --trusted_application={self.sympa_ident} --trusted_application_password={self.sympa_pass} --proxy_vars=USER_EMAIL={MAIL_USERNAME} --service={service} --service_parameters="{liste},{email}" && echo $?'
|
||||||
|
output = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
|
||||||
|
return output.decode("utf-8"), 200 # Retourne la sortie et un code de succès
|
||||||
|
except EmailNotValidError as e:
|
||||||
|
return str(e), 400 # Retourne le message d'erreur et un code d'erreur 400
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
return e.output.decode("utf-8"), 400 # Retourne la sortie de la commande et un code d'erreur 400
|
||||||
|
|
||||||
|
@jwt_required()
|
||||||
|
def post(self, email, liste):
|
||||||
|
"""
|
||||||
|
Ajouter un email dans une liste sympa
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Sympa
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: email
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
- in: path
|
||||||
|
name: liste
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Succès, email ajouté dans la liste
|
||||||
|
400:
|
||||||
|
description: Oops, email non ajouté dans la liste
|
||||||
|
"""
|
||||||
|
output, status_code = self._execute_sympa_command(email, liste, 'add')
|
||||||
|
return output, status_code
|
||||||
|
|
||||||
|
@jwt_required()
|
||||||
|
def delete(self, email, liste):
|
||||||
|
"""
|
||||||
|
Supprimer un email dans une liste sympa
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Sympa
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: email
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
- in: path
|
||||||
|
name: liste
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Succès, email supprimé dans la liste
|
||||||
|
400:
|
||||||
|
description: Oops, email non supprimé dans la liste
|
||||||
|
"""
|
||||||
|
output, status_code = self._execute_sympa_command(email, liste, 'del')
|
||||||
|
return output, status_code
|
||||||
|
|
||||||
|
|
||||||
|
|
111
dockers/apikaz/source/resources/test.py
Normal file
111
dockers/apikaz/source/resources/test.py
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
from resources.common_imports import *
|
||||||
|
|
||||||
|
#les variables globales minimum
|
||||||
|
from resources.config import *
|
||||||
|
|
||||||
|
|
||||||
|
class Test(Resource):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
toto="toto"
|
||||||
|
#self.mattermost_team_resource=Mattermost_team()
|
||||||
|
#global mattermost_url, sympa_url, webmail_url, mdp_url, site_url, nc_url
|
||||||
|
|
||||||
|
#********************************************************************************************
|
||||||
|
@jwt_required()
|
||||||
|
def get(self):
|
||||||
|
"""
|
||||||
|
Pour tester des conneries: # test lançement de cmde ssh sur des serveurs distants:
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Test
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
parameters: []
|
||||||
|
responses:
|
||||||
|
201:
|
||||||
|
description: OK
|
||||||
|
401:
|
||||||
|
description: KO
|
||||||
|
"""
|
||||||
|
#********************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
# test lançcement de cmde ssh sur des serveurs distants:
|
||||||
|
# il faut au préalable que la clé publique de root du conteneur apikaz soit dans authorized key du user fabricer de la machine 163.172.94.54
|
||||||
|
# clé à créer dans le Dockerfile
|
||||||
|
# risque sécu ?
|
||||||
|
|
||||||
|
|
||||||
|
cmd="ssh -p 2201 fabricer@163.172.94.54 mkdir -p /tmp/toto"
|
||||||
|
try:
|
||||||
|
output = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
|
||||||
|
return "ok",200
|
||||||
|
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
return e.output.decode("utf-8"), 400
|
||||||
|
|
||||||
|
#********************************************************************************************
|
||||||
|
# #***** test suppression de toutes les équipes de MM sauf KAZ
|
||||||
|
# res,status=self.mattermost_team_resource=Mattermost_team().get()
|
||||||
|
# for equipe in res:
|
||||||
|
# if equipe!="kaz": res,status=self.mattermost_team_resource=Mattermost_team().delete(equipe)
|
||||||
|
# return "fin"
|
||||||
|
|
||||||
|
#********************************************************************************************
|
||||||
|
|
||||||
|
#**** test messagerie
|
||||||
|
|
||||||
|
# NOM="toto"
|
||||||
|
# EMAIL_SOUHAITE='f@kaz.bzh'
|
||||||
|
# PASSWORD="toto"
|
||||||
|
# QUOTA="1"
|
||||||
|
# ADMIN_ORGA="0"
|
||||||
|
#
|
||||||
|
# context = {
|
||||||
|
# 'ADMIN_ORGA': ADMIN_ORGA,
|
||||||
|
# 'NOM': NOM,
|
||||||
|
# 'EMAIL_SOUHAITE': EMAIL_SOUHAITE,
|
||||||
|
# 'PASSWORD': PASSWORD,
|
||||||
|
# 'QUOTA': QUOTA,
|
||||||
|
# 'URL_WEBMAIL': webmail_url,
|
||||||
|
# 'URL_AGORA': mattermost_url,
|
||||||
|
# 'URL_MDP': mdp_url,
|
||||||
|
# 'URL_LISTE': sympa_url,
|
||||||
|
# 'URL_SITE': site_url,
|
||||||
|
# 'URL_CLOUD': cloud_url
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# subject = "KAZ: confirmation d'inscription !"
|
||||||
|
# sender=app.config['MAIL_USERNAME']
|
||||||
|
# reply_to = app.config['MAIL_REPLY_TO']
|
||||||
|
#
|
||||||
|
# msg = Message(subject=subject, sender=sender, reply_to=reply_to, recipients=[EMAIL_SOUHAITE])
|
||||||
|
# msg.html = render_template('email_inscription.html', **context)
|
||||||
|
#
|
||||||
|
# # Parsez le contenu HTML avec BeautifulSoup
|
||||||
|
# soup = BeautifulSoup(msg.html, 'html.parser')
|
||||||
|
# msg.body = soup.get_text()
|
||||||
|
#
|
||||||
|
# mail.send(msg)
|
||||||
|
# return "Message envoyé!"
|
||||||
|
|
||||||
|
#********************************************************************************************
|
||||||
|
# #**** test ms erreur
|
||||||
|
# email_secours="toto"
|
||||||
|
#
|
||||||
|
# msg=f"classe: {__class__.__name__} : ERREUR 8 : email_secours : {email_secours} " +", on arrête tout"
|
||||||
|
# #return __class__.__name__
|
||||||
|
# return msg
|
||||||
|
|
||||||
|
#********************************************************************************************
|
||||||
|
#**** test vérou
|
||||||
|
# prefixe="toto_"
|
||||||
|
# if glob(f"{tempfile.gettempdir()}/{prefixe}*"):
|
||||||
|
# return "ERREUR : api déjà en cours d'utilisation !", 400
|
||||||
|
# else:
|
||||||
|
# lock_file = tempfile.NamedTemporaryFile(prefix=prefixe,delete=True)
|
||||||
|
#
|
||||||
|
# sleep(20)
|
||||||
|
# return str(lock_file), 201
|
||||||
|
|
Loading…
Reference in New Issue
Block a user