aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas "Cakeisalie5" Touhey <thomas@touhey.fr>2018-10-27 13:30:55 +0200
committerThomas "Cakeisalie5" Touhey <thomas@touhey.fr>2018-10-27 13:30:55 +0200
commitdcf9be0eabe03c5828add50093a6eddba77e5334 (patch)
treec6e9f0af0dcd4069f1244912eeb5299e68d911d4
parentdcc2b5509386aac74d02563abd51818d15e7e22e (diff)
Latest corrections.
-rw-r--r--docs/intranet/auth.rst19
-rw-r--r--docs/intranet/misc.rst3
-rwxr-xr-xsgdfi/_decode.py6
-rwxr-xr-xsgdfi/_intranet.py178
-rwxr-xr-xsgdfi/_manager.py38
5 files changed, 160 insertions, 84 deletions
diff --git a/docs/intranet/auth.rst b/docs/intranet/auth.rst
index 598229f..d883888 100644
--- a/docs/intranet/auth.rst
+++ b/docs/intranet/auth.rst
@@ -17,7 +17,26 @@ services using it to authenticate and identify users.
Internal authentication
-----------------------
+Authentification goes through the ``/Default.aspx`` page as a normal form.
+The following arguments are taken:
+``ctl00$MainContent$login``
+ The login name, i.e. the adherent code.
+
+``ctl00$MainContent$password``
+ The password.
+
+If the credentials are valid, the user is redirected to ``/Accueil.aspx``,
+otherwise the same page is loaded with a warning, amongst:
+
+``Identifiant invalide``
+ The identifier is invalid.
+
+``Mot de passe invalide``
+ The password is invalid.
+
+``Le compte associé à l'identifiant 'XXXXXXXXX' ne donne pas le droit d'utiliser cette application``
+ The given identifier doesn't allow to login (e.g. ``160000000``).
External authentication
-----------------------
diff --git a/docs/intranet/misc.rst b/docs/intranet/misc.rst
index d5c53ff..3e53c93 100644
--- a/docs/intranet/misc.rst
+++ b/docs/intranet/misc.rst
@@ -4,9 +4,6 @@ Miscallaneous
Other locations
---------------
-``/Default.aspx``
- Login page.
-
``/Accueil.aspx``
A home page where you are redirected when you can find some tips and
announcements, plus the menu.
diff --git a/sgdfi/_decode.py b/sgdfi/_decode.py
index 628a65e..a097a99 100755
--- a/sgdfi/_decode.py
+++ b/sgdfi/_decode.py
@@ -14,7 +14,7 @@ from base64 import b64decode as _b64decode
from urllib.parse import urlparse as _urlparse, parse_qs as _parse_qs, \
unquote as _unquote
from html import unescape as _htmlunescape
-from json import loads as _jsonloads
+from json import load as _jsonload, loads as _jsonloads
from csv import reader as _csvreader
from bs4 import BeautifulSoup as _BeautifulSoup
@@ -341,7 +341,7 @@ class Decoder:
# is a string representing a JSON content.
# Then send the to the function.
- data = _jsonloads(content)
+ data = _jsonload(inp)
try:
data = _jsonloads(data['d'])
@@ -1055,7 +1055,7 @@ class Decoder:
funcs = {}
- for idx, value in enumerate(content):
+ for idx, value in enumerate(data):
iid = int(value['id'])
name = value['name']
diff --git a/sgdfi/_intranet.py b/sgdfi/_intranet.py
index 17fba0b..a217acd 100755
--- a/sgdfi/_intranet.py
+++ b/sgdfi/_intranet.py
@@ -18,7 +18,7 @@ from bs4 import BeautifulSoup as _BeautifulSoup
from ._repr import IID as _IID
-__all__ = ["IntranetSession", "IntranetDecoder"]
+__all__ = ["AnonymousIntranetSession", "IntranetSession"]
_monotime = lambda: _getclocktime(_MONOCLOCK)
@@ -36,81 +36,46 @@ class RedirectError(Exception):
return self.__location
# ---
-# Définition de l'objet principal.
+# Définition des objets principaux.
# ---
-class IntranetSession:
- """ Class for interacting with the intranet. """
+class AnonymousIntranetSession:
+ """ Class for interacting with the intranet while not logged in. """
- def __init__(self, manager, base = 'https://intranet.sgdf.fr',
- user = None, pw = None):
+ def __init__(self, manager, base = 'https://intranet.sgdf.fr'):
self.__mgr = manager
self.__base = base
- self.__user = user
- self.__pw = pw
self.__session = _Session()
-
- # Connexion.
-
- self.login()
- # XXX: get information about self.
+ self.__oldsess = None
def __repr__(self):
- return f"{self.__class__.__name__}(user = {repr(self.__user)})"
-
- def __log(self, *msg):
- prefix = f"[{_strftime('%Y-%m-%d %H:%M:%S', _localtime())} I]"
- _stderr.write(f"{prefix} {' '.join(msg)}{_linesep}")
-
- # ---
- # Properties.
- # ---
-
- @property
- def user(self):
- """ Username set. """
-
- return self.__user
-
- @property
- def pw(self):
- """ Password set. """
+ return f"{self.__class__.__name__}()"
- return self.__pw
+ def _log_prefix(self):
+ return f"[{_strftime('%Y-%m-%d %H:%M:%S', _localtime())} " \
+ f"I:anon]"
- @pw.setter
- def pw(self, value):
- # Use another password instead.
-
- if type(value) != str:
- raise ValueError("expected a string")
-
- new_session = _Session()
- self.login(pw = value, session = new_session)
- self.__pw = value
- self.__session = new_session
-
- @property
- def password(self):
- """ Alias for `pw`. """
-
- return self.pw
-
- @password.setter
- def password(self, value):
- self.pw = value
+ def __log(self, *msg):
+ _stderr.write(f"{self._log_prefix()} {' '.join(msg)}{_linesep}")
# ---
# Base utilities.
# ---
METHOD_BASIC = 1
- METHOD_POST = 2
+ METHOD_JSONPOST = 2
METHOD_FORM = 3
METHOD_AJAX = 4
+ def _restore_session(self):
+ """ Restore the previous session. """
+
+ if self.__oldsession is not None:
+ self.__session = self.__oldsession
+ self.__oldsession = None
+
def get_page(self, path, args = {}, method = METHOD_BASIC, hint = None,
- session = None):
+ new_session = False):
""" Gather a page:
- as a normal page using GET (`METHOD_BASIC`).
- as a POST page (`METHOD_POST`).
@@ -122,15 +87,15 @@ class IntranetSession:
object's requests Session object, or the requests Session object
to use. """
- if not method in (self.METHOD_BASIC, self.METHOD_POST,
+ if new_session:
+ self.__oldsession = self.__session
+ self.__session = _Session()
+
+ if not method in (self.METHOD_BASIC, self.METHOD_JSONPOST,
self.METHOD_FORM, self.METHOD_AJAX):
raise ValueError("invalid method")
- if session is None:
- session = self.__session
- elif not isinstance(session, _Session):
- raise ValueError("expected a Session object")
- mname = ("basic", "post", "form", "ajax")[method - 1]
+ mname = ("basic", "jsonpost", "form", "ajax")[method - 1]
self.__log(f"Get a page using {mname} method")
def ret(r):
@@ -202,12 +167,13 @@ class IntranetSession:
df = {}
- if method != self.METHOD_POST:
+ if method != self.METHOD_JSONPOST:
# Récupération à proprement parler.
tm = _monotime()
r = self.__session.get(self.__base + path, headers = headers)
- self.__log(f"Page GET request delay: {_monotime() - tm}s")
+ self.__log("Page GET request delay: " \
+ f"{round(_monotime() - tm, 4)}s")
# Si la méthode est un GET basique, on peut s'arrêter là.
@@ -268,13 +234,77 @@ class IntranetSession:
# Exécution de la requête.
+ pkw = {}
+ if method == self.METHOD_JSONPOST:
+ pkw['json'] = payload
+ else:
+ pkw['data'] = payload
+
tm = _monotime()
r = self.__session.post(self.__base + path, headers = headers,
- data = payload, allow_redirects = False)
- self.__log(f"Page POST request delay: {_monotime() - tm}s")
+ allow_redirects = False, **pkw)
+ self.__log(f"Page POST request delay: {round(_monotime() - tm, 4)}s")
return ret(r)
+class IntranetSession(AnonymousIntranetSession):
+ """ Class for interacting with the intranet while logged in. """
+
+ def __init__(self, manager, base = 'https://intranet.sgdf.fr',
+ user = None, pw = None):
+ super().__init__(manager, base)
+
+ self.__user = user
+ self.__pw = pw
+
+ # Login process.
+
+ self.login()
+ # XXX: get information about self.
+
+ def __repr__(self):
+ return f"{self.__class__.__name__}(user = {repr(self.__user)})"
+
+ # ---
+ # Properties.
+ # ---
+
+ @property
+ def user(self):
+ """ Username set. """
+
+ return self.__user
+
+ @property
+ def pw(self):
+ """ Password set. """
+
+ return self.__pw
+
+ @pw.setter
+ def pw(self, value):
+ # Use another password instead.
+
+ if type(value) != str:
+ raise ValueError("expected a string")
+
+ self.login(pw = value, new_session = True)
+ self.__pw = value
+
+ @property
+ def password(self):
+ """ Alias for `pw`. """
+
+ return self.pw
+
+ @password.setter
+ def password(self, value):
+ self.pw = value
+
+ def _log_prefix(self):
+ return f"[{_strftime('%Y-%m-%d %H:%M:%S', _localtime())} " \
+ f"I:{self.__user}]"
+
# ---
# Data generation.
# ---
@@ -283,10 +313,11 @@ class IntranetSession:
""" Get the functions. """
path = '/Specialisation/Sgdf' \
- '/WebServices/AutoComplete.asmx/GetFonctions?q='
+ '/WebServices/AutoComplete.asmx/GetFonctions'
+ args = {'id_token': 'undefined', 'q': ''}
- return self.get_page(path, method = self.METHOD_BASIC,
- hint = 'functions')
+ return self.get_page(path, method = self.METHOD_JSONPOST, args = args,
+ hint = 'intranet_functions')
# ---
# Usage.
@@ -298,6 +329,8 @@ class IntranetSession:
if pw is None:
pw = self.__pw
+ e = None
+
try:
temp = self.get_page('/Default.aspx', {
'ctl00': {
@@ -306,14 +339,17 @@ class IntranetSession:
'password': pw}}},
method = self.METHOD_FORM,
hint = 'ignore',
- session = session)
+ new_session = True)
except RedirectError:
# We have successfully been redirected!
return
- # FIXME: the credentials were invalid.
+ # The credentials were invalid.
+ # FIXME: an appropriate exception?
+ # FIXME: what if we were reconnecting?
+ self._restore_session()
raise ValueError("invalid credentials")
def _get_ops_page(self, ent_type, ent_iid, page):
diff --git a/sgdfi/_manager.py b/sgdfi/_manager.py
index 8efabe8..72cae59 100755
--- a/sgdfi/_manager.py
+++ b/sgdfi/_manager.py
@@ -22,24 +22,42 @@ from ._repr import Base as _Base, Structure as _Structure, \
Adherent as _Adherent, Place as _Place, \
RallyRegistration as _RallyRegistration, Camp as _Camp, \
Operation as _Operation, FunctionRawData as _FunctionRawData
-from ._intranet import IntranetSession as _IntranetSession
+from ._intranet import AnonymousIntranetSession as _AnonymousIntranetSession, \
+ IntranetSession as _IntranetSession
from ._decode import Decoder as _Decoder
__all__ = ["Manager"]
+def _names(value):
+ names = ()
+ try:
+ names += (str(value),)
+ except:
+ pass
+ try:
+ names += (value.__name__,)
+ except:
+ pass
+ names += (value.__class__.__name__,)
+
+ flt = lambda x: ''.join(c for c in x.casefold() if c in _ascii_letters)
+ return tuple(map(flt, names))
+
def _isany(value):
""" Check if a value can be considered as any. """
- flt = lambda x: ''.join(c for c in x.casefold() if c in _ascii_letters)
- return bool(value is any or 'any' in map(filt, (value.__name__,
- value.__class__.__name__)))
+ return bool(value is any or "any" in _names(value))
def _isall(value):
""" Check if a value can be considered as all. """
- flt = lambda x: ''.join(c for c in x.casefold() if c in _ascii_letters)
- return bool(value is all or 'all' in map(filt, (value.__name__,
- value.__class__.__name__)))
+ return bool(value is all or "all" in _names(value))
+
+def _isanon(value):
+ """ Check if a value can be considered as anonymous. """
+
+ return bool(value is None or any(n for n in _names(value) \
+ if n in ("anon", "anonymous", "none")))
# ---
# Utilities.
@@ -93,6 +111,7 @@ class _IntranetSessionsManager:
def __init__(self, mgr):
self.__mgr = mgr
+ self.__anon = None
self.__sessions = []
pass
@@ -102,6 +121,11 @@ class _IntranetSessionsManager:
return self.__sessions[0]
raise KeyError("no session available")
+ if _isanon(key):
+ if self.__anon is None:
+ self.__anon = _AnonymousIntranetSession(self.__mgr)
+ return self.__anon
+
try:
s = next(s for s in self.__sessions if s.user == key)
except StopIteration: