Compare commits

..

3 commits

45 changed files with 95 additions and 267 deletions

View file

@ -35,37 +35,32 @@ before_script:
- apt-get update -q && apt-get -o dir::cache::archives="vendor/apt" install -yqq python3-dev libldap2-dev libsasl2-dev - apt-get update -q && apt-get -o dir::cache::archives="vendor/apt" install -yqq python3-dev libldap2-dev libsasl2-dev
- pip install tox - pip install tox
python35:
image: python:3.5
stage: tests
script:
- tox -e py35-django22
python36: python36:
image: python:3.6 image: python:3.6
stage: tests stage: tests
script: script:
- tox -e py36-django22 - tox -e py36-django22
- tox -e py36-django31 - tox -e py36-django30
- tox -e py36-django32
python37: python37:
image: python:3.7 image: python:3.7
stage: tests stage: tests
script: script:
- tox -e py37-django22 - tox -e py37-django22
- tox -e py37-django31 - tox -e py37-django30
- tox -e py37-django32
python38: python38:
image: python:3.8 image: python:3.8
stage: tests stage: tests
script: script:
- tox -e py38-django22 - tox -e py38-django22
- tox -e py38-django31 - tox -e py38-django30
- tox -e py38-django32
python39:
image: python:3.9
stage: tests
script:
- tox -e py39-django22
- tox -e py39-django31
- tox -e py39-django32
# --- # ---
# Release artifacts # Release artifacts

3
MANIFEST.in Normal file
View file

@ -0,0 +1,3 @@
include LICENSE
recursive-include authens/static *
recursive-include authens/templates *

View file

@ -47,9 +47,9 @@ en plus du site.
### Dans le fichier `settings.py` ### Dans le fichier `settings.py`
- Ajouter `"authens"` dans les [`INSTALLED_APPS`](https://docs.djangoproject.com/en/3.0/ref/settings/#installed-apps). - Ajouter `"authens"` dans les [`INSTALLED_APPS`](https://docs.djangoproject.com/en/3.2/ref/settings/#installed-apps).
- Ajouter `"authens.backends.ENSCASBackend"` dans les - Ajouter `"authens.backends.ENSCASBackend"` dans les
[`AUTHENTICATION_BACKENDS`](https://docs.djangoproject.com/en/3.0/ref/settings/#authentication-backends). [`AUTHENTICATION_BACKENDS`](https://docs.djangoproject.com/en/3.2/ref/settings/#authentication-backends).
Si `AUTHENTICATION_BACKENDS` n'apparaît pas dans vos settings, utiliser : Si `AUTHENTICATION_BACKENDS` n'apparaît pas dans vos settings, utiliser :
```python ```python
@ -85,18 +85,27 @@ AUTHENS_ALLOW_STAFF = True
- (Optionnel) AuthENS utilise le paramètre Django standard - (Optionnel) AuthENS utilise le paramètre Django standard
[`LOGIN_REDIRECT_URL`](https://docs.djangoproject.com/en/3.0/ref/settings/#login-redirect-url) [`LOGIN_REDIRECT_URL`](https://docs.djangoproject.com/en/3.2/ref/settings/#login-redirect-url)
par défaut pour rediriger l'utilisateurice en cas de connexion réussie. par défaut pour rediriger l'utilisateurice en cas de connexion réussie.
Il peut être utile de définir ce paramètre. Il peut être utile de définir ce paramètre.
- (Optionnel) Pour filtrer quels utilisateurs peuvent réinitialiser leur mot de
passe, il est possible de définir un formulaire personnalisé pour remplacer le
formulaire Django standard [`PasswordResetForm`](https://docs.djangoproject.com/en/3.2/topics/auth/default/#django.contrib.auth.forms.PasswordResetForm)
```python
AUTHENS_PASSWORD_RESET_FORM = CustomPwdResetForm
```
## Utilisation ## Utilisation
À lire À lire
### Création d'utilisateurices ### Création d'utilisateurices
AuthENS maintient une table des comptes clipper connus. AuthENS maintient une tables des comptes clipper connus.
Cette table est automatiquement mise à jour lors qu'une personne se connecte via Cette table est automatiquement mise à jour lors qu'une personne se connecte via
le CAS pour la première fois. le CAS pour la première fois.
En revanche lorsqu'un nouveau compte est créé manuellement et que ce compte En revanche lorsqu'un nouveau compte est créé manuellement et que ce compte
@ -144,7 +153,7 @@ celui-ci. Les templates à substituer sont :
### Migration depuis `django_cas_ng` ### Migration depuis `django_cas_ng`
Pour migrer depuis `django_cas_ng`, il est recommandé de faire une Pour migrer depuis `django_cas_ng`, il est recommandé de faire une
[data migration](https://docs.djangoproject.com/en/3.1/howto/writing-migrations/#migrating-data-between-third-party-apps) [data migration](https://docs.djangoproject.com/en/3.2/howto/writing-migrations/#migrating-data-between-third-party-apps)
et d'appliquer la fonction `register_cas_account` sur tou⋅tes les et d'appliquer la fonction `register_cas_account` sur tou⋅tes les
utilisateurices. utilisateurices.

View file

@ -3,4 +3,3 @@ from django.apps import AppConfig
class AuthEnsConfig(AppConfig): class AuthEnsConfig(AppConfig):
name = "authens" name = "authens"
default_auto_field = "django.db.models.AutoField"

Binary file not shown.

View file

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-11-02 10:22+0100\n" "POT-Creation-Date: 2021-01-28 20:40+0100\n"
"PO-Revision-Date: 2021-01-30 20:48+0100\n" "PO-Revision-Date: 2021-01-30 20:48+0100\n"
"Last-Translator: Tom Hubrecht <tom.hubrecht@ens.fr>\n" "Last-Translator: Tom Hubrecht <tom.hubrecht@ens.fr>\n"
"Language-Team: \n" "Language-Team: \n"
@ -22,7 +22,7 @@ msgstr ""
msgid "Ancien login clipper" msgid "Ancien login clipper"
msgstr "Old clipper username" msgstr "Old clipper username"
#: forms.py:18 templates/authens/login_switch.html:15 #: forms.py:18 templates/authens/login_switch.html:14
msgid "Mot de passe" msgid "Mot de passe"
msgstr "Password" msgstr "Password"
@ -39,7 +39,7 @@ msgstr ""
"No user exists with this username, this entrance year and/or this password. " "No user exists with this username, this entrance year and/or this password. "
"Please check your entry. Attention, all fields are case sensitive!" "Please check your entry. Attention, all fields are case sensitive!"
#: models.py:19 models.py:60 #: models.py:19 models.py:57
msgid "utilisateurice" msgid "utilisateurice"
msgstr "user" msgstr "user"
@ -47,36 +47,36 @@ msgstr "user"
msgid "login CAS" msgid "login CAS"
msgstr "CAS username" msgstr "CAS username"
#: models.py:30 models.py:70 #: models.py:30 models.py:67
msgid "année de création du compte CAS" msgid "année de création du compte CAS"
msgstr "year of CAS account creation" msgstr "year of CAS account creation"
#: models.py:37 #: models.py:34
msgid "Compte CAS" msgid "Compte CAS"
msgstr "CAS account" msgstr "CAS account"
#: models.py:38 #: models.py:35
msgid "Comptes CAS" msgid "Comptes CAS"
msgstr "CAS accounts" msgstr "CAS accounts"
#: models.py:42 #: models.py:39
#, python-format #, python-format
msgid "compte CAS %(cas_login)s (promo %(entrance_year)s) lié à %(user)s" msgid "compte CAS %(cas_login)s (promo %(entrance_year)s) lié à %(user)s"
msgstr "CAS account %(cas_login)s (year %(entrance_year)s) linked to %(user)s" msgstr "CAS account %(cas_login)s (year %(entrance_year)s) linked to %(user)s"
#: models.py:66 #: models.py:63
msgid "ancien login CAS" msgid "ancien login CAS"
msgstr "old CAS username" msgstr "old CAS username"
#: models.py:81 #: models.py:78
msgid "Ancien compte CAS" msgid "Ancien compte CAS"
msgstr "Old CAS account" msgstr "Old CAS account"
#: models.py:82 #: models.py:79
msgid "Anciens comptes CAS" msgid "Anciens comptes CAS"
msgstr "Old CAS accounts" msgstr "Old CAS accounts"
#: models.py:86 #: models.py:83
#, python-format #, python-format
msgid "" msgid ""
"Ancien compte CAS %(cas_login)s (promo %(entrance_year)s) lié à %(user)s" "Ancien compte CAS %(cas_login)s (promo %(entrance_year)s) lié à %(user)s"
@ -91,11 +91,11 @@ msgstr "Connection method"
msgid "Clipper" msgid "Clipper"
msgstr "Clipper" msgstr "Clipper"
#: templates/authens/login_switch.html:20 #: templates/authens/login_switch.html:18
msgid "Vieilleux" msgid "Vieilleux"
msgstr "Alumni" msgstr "Alumni"
#: templates/authens/login_switch.html:27 #: templates/authens/login_switch.html:25
#, python-format #, python-format
msgid "" msgid ""
"Si votre fin de scolarité approche, créez un mot de passe pour votre compte " "Si votre fin de scolarité approche, créez un mot de passe pour votre compte "
@ -153,25 +153,16 @@ msgstr "For your information, your username is the following: %(username)s"
msgid "L'équipe %(site_name)s" msgid "L'équipe %(site_name)s"
msgstr "The %(site_name)s team" msgstr "The %(site_name)s team"
#: views.py:89 #: views.py:86
#, fuzzy msgid "Connexion échouée !"
#| msgid "Connexion échouée !"
msgid "Connection échouée !"
msgstr "Connection failed!" msgstr "Connection failed!"
#: views.py:105 #: views.py:102
msgid "" msgid ""
"Si un compte avec cet email existe, un email de réinitialisation vient de " "Un email de réinitialisation du mot de passe vient d'être envoyé à l'adresse "
"lui être envoyé !" "indiquée !"
msgstr "" msgstr "A password reset email has just been sent to the indicated address!"
"A password reset email has just been send to the inidcated address, "
"provided an account with this email exists)"
#: views.py:115 #: views.py:112
msgid "Mot de passe modifié avec succès !" msgid "Mot de passe modifié avec succès !"
msgstr "Password changed successfully!" msgstr "Password changed successfully!"
#~ msgid ""
#~ "Un email de réinitialisation du mot de passe vient d'être envoyé à "
#~ "l'adresse indiquée !"
#~ msgstr "A password reset email has just been sent to the indicated address!"

View file

View file

@ -3,6 +3,7 @@ from urllib.parse import urlparse, urlunparse
from django.conf import settings from django.conf import settings
from django.contrib import auth from django.contrib import auth
from django.contrib.auth import views as auth_views from django.contrib.auth import views as auth_views
from django.contrib.auth.forms import PasswordResetForm
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.shortcuts import redirect from django.shortcuts import redirect
@ -101,8 +102,11 @@ class PasswordResetView(SuccessMessageMixin, auth_views.PasswordResetView):
subject_template_name = "authens/pwd_reset_subject.txt" subject_template_name = "authens/pwd_reset_subject.txt"
success_url = reverse_lazy("authens:login") success_url = reverse_lazy("authens:login")
def get_form_class(self):
return getattr(settings, "AUTHENS_PASSWORD_RESET_FORM", PasswordResetForm)
success_message = _( success_message = _(
"Si un compte avec cet email existe, un email de réinitialisation vient de lui être envoyé !" "Un email de réinitialisation vient d'être envoyé à l'adresse indiquée !"
) )
@ -138,14 +142,14 @@ class LogoutView(auth_views.LogoutView):
else: else:
self.cas_connected = False self.cas_connected = False
def get_success_url(self): def get_next_page(self):
next_page = super().get_success_url() next_page = super().get_next_page()
if self.cas_connected: if self.cas_connected:
cas_client = get_cas_client(self.request) cas_client = get_cas_client(self.request)
# If the next_url is local (no hostname), make it absolute so that the user # If the next_url is local (no hostname), make it absolute so that the user
# is correctly redirected from CAS. # is correctly redirected from CAS.
if next_page is not None and not urlparse(next_page).netloc: if not urlparse(next_page).netloc:
request = self.request request = self.request
next_page = urlunparse( next_page = urlunparse(
(request.scheme, request.get_host(), next_page, "", "", "") (request.scheme, request.get_host(), next_page, "", "", "")

View file

@ -1,53 +0,0 @@
{
sources ? import ./npins,
pkgs ? import sources.nixpkgs { },
}:
let
nix-pkgs = import sources.nix-pkgs { inherit pkgs; };
python3 = pkgs.python3.override { packageOverrides = _: _: { inherit (nix-pkgs) python-cas; }; };
deploy-pypi = pkgs.writeShellApplication {
name = "deploy-pypi";
runtimeInputs = [
(pkgs.python3.withPackages (ps: [
ps.setuptools
ps.build
ps.twine
]))
];
text = ''
# Clean the repository
rm -rf dist
python -m build
twine upload dist/*
'';
};
in
{
devShell = pkgs.mkShell {
name = "cas-eleves.dev";
packages = [
(python3.withPackages (ps: [
ps.django
ps.python-ldap
ps.python-cas
]))
pkgs.gettext
pkgs.gtranslator
];
};
publishShell = pkgs.mkShell {
name = "loadcredential.publish";
packages = [ deploy-pypi ];
};
}

View file

@ -1,80 +0,0 @@
# Generated by npins. Do not modify; will be overwritten regularly
let
data = builtins.fromJSON (builtins.readFile ./sources.json);
version = data.version;
mkSource =
spec:
assert spec ? type;
let
path =
if spec.type == "Git" then
mkGitSource spec
else if spec.type == "GitRelease" then
mkGitSource spec
else if spec.type == "PyPi" then
mkPyPiSource spec
else if spec.type == "Channel" then
mkChannelSource spec
else
builtins.throw "Unknown source type ${spec.type}";
in
spec // { outPath = path; };
mkGitSource =
{
repository,
revision,
url ? null,
hash,
branch ? null,
...
}:
assert repository ? type;
# At the moment, either it is a plain git repository (which has an url), or it is a GitHub/GitLab repository
# In the latter case, there we will always be an url to the tarball
if url != null then
(builtins.fetchTarball {
inherit url;
sha256 = hash;
})
else
assert repository.type == "Git";
let
urlToName =
url: rev:
let
matched = builtins.match "^.*/([^/]*)(\\.git)?$" repository.url;
short = builtins.substring 0 7 rev;
appendShort = if (builtins.match "[a-f0-9]*" rev) != null then "-${short}" else "";
in
"${if matched == null then "source" else builtins.head matched}${appendShort}";
name = urlToName repository.url revision;
in
builtins.fetchGit {
url = repository.url;
rev = revision;
inherit name;
narHash = hash;
};
mkPyPiSource =
{ url, hash, ... }:
builtins.fetchurl {
inherit url;
sha256 = hash;
};
mkChannelSource =
{ url, hash, ... }:
builtins.fetchTarball {
inherit url;
sha256 = hash;
};
in
if version == 4 then
builtins.mapAttrs (_: mkSource) data.pins
else
throw "Unsupported format version ${toString version} in sources.json. Try running `npins upgrade`"

View file

@ -1,22 +0,0 @@
{
"pins": {
"nix-pkgs": {
"type": "Git",
"repository": {
"type": "Git",
"url": "https://git.hubrecht.ovh/hubrecht/nix-pkgs.git"
},
"branch": "main",
"revision": "46879d052e4a694ceb3027dbcff641c44e0ae1bd",
"url": null,
"hash": "sha256-/Yn3NDYA76bv8x06jahLAJ2z54L0vFeAtQKzyW3MfGA="
},
"nixpkgs": {
"type": "Channel",
"name": "nixpkgs-unstable",
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-24.11pre647329.6842b061970b/nixexprs.tar.xz",
"hash": "04inyzaffqclmymaphc4cwhywnyk51hi44508kfbsj9h57875hb6"
}
},
"version": 4
}

View file

@ -1,49 +0,0 @@
[build-system]
requires = ["setuptools", "setuptools_scm"]
build-backend = "setuptools.build_meta"
[project]
name = "authens"
dynamic = ["version"]
authors = [
{name = "Klub Dev ENS", email = "klub-dev@ens.fr"},
{name = "Tom Hubrecht", email = "pypi@mail.hubrecht.ovh"},
]
description = "CAS Authentication Client at the ENS."
license = {file = "LICENSE"}
readme = "README.md"
classifiers = [
"Development Status :: 4 - Beta",
"Environment :: Web Environment",
"Framework :: Django",
"Framework :: Django :: 4.1",
"Framework :: Django :: 4.2",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Internet :: WWW/HTTP",
"Topic :: Internet :: WWW/HTTP :: Dynamic Content",
]
dependencies = [
"django >= 4.1",
"python-ldap >= 3, < 4",
"python-cas >= 1.5, < 2",
]
[project.urls]
Homepage = "https://git.dgnum.eu/DGNum/authens"
Repository = "https://git.dgnum.eu/DGNum/authens"
Issues = "https://git.dgnum.eu/DGNum/authens/issues"
[tool.setuptools.dynamic]
version = {attr = "authens.__VERSION__"}
[tool.setuptools.packages.find]
where = ["src"]

37
setup.py Normal file
View file

@ -0,0 +1,37 @@
import setuptools
with open("README.md", "r") as file:
long_description = file.read()
setuptools.setup(
name="authens",
version="0.1b4",
author="Klub Dev ENS",
author_email="klub-dev@ens.fr",
description="CAS Authentication at the ENS",
long_description=long_description,
long_description_content_type="text/markdown",
url="https://git.eleves.ens.fr/klub-dev-ens/authens",
packages=setuptools.find_packages(),
include_package_data=True,
classifiers=[
"Environment :: Web Environment",
"Framework :: Django",
"Framework :: Django :: 2.2",
"Framework :: Django :: 3.0",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Topic :: Internet :: WWW/HTTP",
"Topic :: Internet :: WWW/HTTP :: Dynamic Content",
],
python_requires=">=3.5",
install_requires=["Django>=2.2", "python-ldap>=3,<4", "python-cas>=1.5,<2"],
)

View file

@ -1 +0,0 @@
(import ./. { }).devShell

View file

@ -1,5 +0,0 @@
__VERSION__ = "0.2.0"
__all__ = [
"__VERSION__",
]

View file

@ -1,12 +1,12 @@
[tox] [tox]
envlist = envlist =
py{36,37,38,39}-django{22,31,32} py{35,36,37,38}-django22,
py{36,37,38}-django30
[testenv] [testenv]
deps = deps =
django22: Django>=2.2.17,<3 django22: Django==2.2.*
django31: Django==3.1.* django30: Django==3.0.*
django32: Django==3.2.*
python-cas python-cas
python-ldap python-ldap
commands = commands =