2017-10-16 19:47:17 +02:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
2024-10-23 11:06:57 +02:00
|
|
|
""" API client for bocal-site """
|
2017-09-24 18:20:10 +02:00
|
|
|
|
2017-10-16 17:04:51 +02:00
|
|
|
import argparse
|
2024-10-23 11:06:57 +02:00
|
|
|
import hashlib
|
|
|
|
import hmac
|
|
|
|
import json
|
2017-10-16 17:04:51 +02:00
|
|
|
import os.path
|
|
|
|
import sys
|
2024-10-23 11:06:57 +02:00
|
|
|
import urllib.request
|
|
|
|
from datetime import date, datetime
|
2017-09-24 18:20:10 +02:00
|
|
|
|
|
|
|
|
|
|
|
def sendReq(url):
|
|
|
|
def send(payload, host):
|
|
|
|
try:
|
2024-10-23 11:06:57 +02:00
|
|
|
req = urllib.request.Request(
|
|
|
|
"https://{}/{}".format(host, url), json.dumps(payload).encode("ascii")
|
|
|
|
)
|
|
|
|
req.add_header("Content-Type", "application/json")
|
2017-09-24 18:20:10 +02:00
|
|
|
handle = urllib.request.urlopen(req)
|
|
|
|
code = handle.getcode()
|
|
|
|
content = handle.read()
|
|
|
|
handle.close()
|
2024-10-23 11:06:57 +02:00
|
|
|
return (code, content.decode("utf-8"))
|
2017-09-24 18:20:10 +02:00
|
|
|
except urllib.error.HTTPError as e:
|
2024-10-23 11:06:57 +02:00
|
|
|
return (e.code, e.read().decode("utf-8"))
|
2017-09-24 18:20:10 +02:00
|
|
|
|
|
|
|
def authentify(apiKey, payload):
|
2024-10-23 11:06:57 +02:00
|
|
|
keyId, key = apiKey.split("$")
|
2017-09-24 18:20:10 +02:00
|
|
|
keyId = int(keyId)
|
|
|
|
time = datetime.now().timestamp()
|
2024-10-23 11:06:57 +02:00
|
|
|
mac = hmac.new(
|
|
|
|
key.encode("utf-8"),
|
|
|
|
msg=str(int(time)).encode("utf-8"),
|
|
|
|
digestmod=hashlib.sha256,
|
|
|
|
)
|
2017-10-16 20:02:36 +02:00
|
|
|
payload_enc = json.dumps(payload)
|
2024-10-23 11:06:57 +02:00
|
|
|
mac.update(payload_enc.encode("utf-8"))
|
2017-09-24 18:20:10 +02:00
|
|
|
|
|
|
|
auth = {
|
2024-10-23 11:06:57 +02:00
|
|
|
"keyId": keyId,
|
|
|
|
"timestamp": time,
|
|
|
|
"hmac": mac.hexdigest(),
|
2017-09-24 18:20:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
2024-10-23 11:06:57 +02:00
|
|
|
"auth": auth,
|
|
|
|
"req": payload_enc,
|
2017-09-24 18:20:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
def decorator(fct):
|
2024-10-23 11:06:57 +02:00
|
|
|
"""Decorator. Adds authentication layer."""
|
|
|
|
|
2017-09-24 18:20:10 +02:00
|
|
|
def wrap(host, apiKey, *args, **kwargs):
|
|
|
|
innerReq = fct(*args, **kwargs)
|
|
|
|
payload = authentify(apiKey, innerReq)
|
|
|
|
return send(payload, host)
|
2024-10-23 11:06:57 +02:00
|
|
|
|
2017-09-24 18:20:10 +02:00
|
|
|
return wrap
|
|
|
|
|
|
|
|
return decorator
|
|
|
|
|
|
|
|
|
2024-10-23 11:06:57 +02:00
|
|
|
@sendReq(url="api/publish")
|
2017-09-24 18:20:10 +02:00
|
|
|
def publish(bocId, url, date):
|
|
|
|
return {
|
2024-10-23 11:06:57 +02:00
|
|
|
"id": bocId,
|
|
|
|
"url": url,
|
|
|
|
"date": date.strftime("%Y-%m-%d"),
|
2017-09-24 18:20:10 +02:00
|
|
|
}
|
2017-10-16 17:04:51 +02:00
|
|
|
|
|
|
|
|
|
|
|
###############################################################################
|
|
|
|
|
|
|
|
TOKEN_DFT_FILE = os.path.expanduser("~/.bocal_api_token")
|
2024-10-23 11:06:57 +02:00
|
|
|
DFT_HOST = "bocal.cof.ens.fr"
|
2017-10-16 17:04:51 +02:00
|
|
|
|
|
|
|
|
|
|
|
def read_token(path):
|
2024-10-23 11:06:57 +02:00
|
|
|
token = ""
|
2017-10-16 17:04:51 +02:00
|
|
|
try:
|
2024-10-23 11:06:57 +02:00
|
|
|
with open(path, "r") as handle:
|
2017-10-16 17:04:51 +02:00
|
|
|
token = handle.readline().strip()
|
|
|
|
except FileNotFoundError:
|
2024-10-23 11:06:57 +02:00
|
|
|
print(
|
|
|
|
"[Erreur] Fichier d'identifiants absent (`{}`).".format(path),
|
|
|
|
file=sys.stderr,
|
|
|
|
)
|
2017-10-16 17:04:51 +02:00
|
|
|
sys.exit(1)
|
|
|
|
return token
|
|
|
|
|
|
|
|
|
|
|
|
def cmd(func):
|
|
|
|
def wrap(parse_args, *args, **kwargs):
|
|
|
|
token = read_token(parse_args.creds)
|
|
|
|
return func(token, parse_args, *args, **kwargs)
|
2024-10-23 11:06:57 +02:00
|
|
|
|
2017-10-16 17:04:51 +02:00
|
|
|
return wrap
|
|
|
|
|
|
|
|
|
|
|
|
@cmd
|
|
|
|
def cmd_publish(token, args):
|
2017-10-16 19:47:17 +02:00
|
|
|
if not args.date:
|
|
|
|
publish_date = date.today()
|
|
|
|
else:
|
2024-10-23 11:06:57 +02:00
|
|
|
year, month, day = [int(x) for x in args.date.strip().split("-")]
|
2017-10-16 19:47:17 +02:00
|
|
|
publish_date = date(year=year, month=month, day=day)
|
2024-10-23 11:06:57 +02:00
|
|
|
(ret_code, ret_str) = publish(args.host, token, args.numero, args.url, publish_date)
|
2017-10-16 17:04:51 +02:00
|
|
|
if ret_code == 200:
|
|
|
|
print("Succès :)")
|
|
|
|
else:
|
|
|
|
print("[Erreur :c] {} : {}".format(ret_code, ret_str))
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
|
|
def setup_argparse():
|
|
|
|
parser = argparse.ArgumentParser()
|
2024-10-23 11:06:57 +02:00
|
|
|
parser.add_argument(
|
|
|
|
"--host",
|
|
|
|
help=("Adresse du site à contacter (par défaut, " "`{}`).".format(DFT_HOST)),
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
"--creds",
|
|
|
|
help=(
|
|
|
|
"Fichier contenant le token API à utiliser "
|
|
|
|
"(par défaut, `{}`)".format(TOKEN_DFT_FILE)
|
|
|
|
),
|
|
|
|
)
|
2017-10-16 19:54:45 +02:00
|
|
|
parser.set_defaults(creds=TOKEN_DFT_FILE, host=DFT_HOST)
|
2017-10-16 17:04:51 +02:00
|
|
|
subparsers = parser.add_subparsers()
|
|
|
|
|
2024-10-23 11:06:57 +02:00
|
|
|
parser_publish = subparsers.add_parser("publier", help="Publier un numéro du BOcal")
|
|
|
|
parser_publish.add_argument("numero", help="Numéro du BOcal")
|
|
|
|
parser_publish.add_argument("url", help="Adresse (locale) du PDF du BOcal")
|
|
|
|
parser_publish.add_argument("-d", "--date", help="Date de publication indiquée")
|
2017-10-16 17:04:51 +02:00
|
|
|
parser_publish.set_defaults(func=cmd_publish)
|
|
|
|
|
|
|
|
out_args = parser.parse_args()
|
2024-10-23 11:06:57 +02:00
|
|
|
if "func" not in out_args: # No subcommand provided
|
2017-10-16 17:04:51 +02:00
|
|
|
print("You must provide a command.", file=sys.stderr)
|
2024-10-23 11:06:57 +02:00
|
|
|
print(parser.parse_args(["-h"]), file=sys.stderr)
|
2017-10-16 17:04:51 +02:00
|
|
|
sys.exit(1)
|
|
|
|
return out_args
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
args = setup_argparse()
|
|
|
|
args.func(args)
|
|
|
|
|
|
|
|
|
2024-10-23 11:06:57 +02:00
|
|
|
if __name__ == "__main__":
|
2017-10-16 17:04:51 +02:00
|
|
|
main()
|