Compare commits

..

3 commits

Author SHA1 Message Date
54e4d28d0c combine_as_imports = true 2021-05-12 01:44:45 +02:00
41752ac344 Rajoute un paramètre pour désactiver le LDAP 2021-05-12 01:36:24 +02:00
423dfcd2ec fix isort 2021-05-12 01:26:24 +02:00
47 changed files with 87 additions and 292 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

@ -74,16 +74,6 @@ AUTHENS_USE_OLDCAS = False
AUTHENS_USE_PASSWORD = False AUTHENS_USE_PASSWORD = False
``` ```
- (Optionnel) Il est possible d'autoriser la connexion via CAS pour les membres
de `staffs`, lorsque cette option est activée, leur promotion est fixée à 0,
lorsque l'option est désactivée, une tentative de connexion renvoie une erreur
car le format de `$HOME` n'est pas valide.
```python
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.0/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.
@ -96,7 +86,7 @@ AUTHENS_ALLOW_STAFF = True
### 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

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"

View file

@ -1,5 +1,5 @@
LDAP_SERVER_URL = "ldaps://ldap.spi.ens.fr:636" LDAP_SERVER_URL = "ldaps://ldap.spi.ens.fr:636"
AUTHENS_USE_OLDCAS = True AUTHENS_USE_OLDCAS = True
AUTHENS_USE_PASSWORD = True AUTHENS_USE_PASSWORD = True
AUTHENS_ALLOW_STAFF = False USE_LDAP = True
# TODO: CAS_SERVER_URL # TODO: CAS_SERVER_URL

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

@ -30,9 +30,6 @@ class CASAccount(models.Model):
verbose_name=_("année de création du compte CAS"), blank=False, null=False verbose_name=_("année de création du compte CAS"), blank=False, null=False
) )
# The entrance year 0 is used for members of staff
STAFF_ENTRANCE_YEAR = 0
class Meta: class Meta:
verbose_name = _("Compte CAS") verbose_name = _("Compte CAS")
verbose_name_plural = _("Comptes CAS") verbose_name_plural = _("Comptes CAS")

View file

@ -1,5 +1,7 @@
"""Helper functions to get CAS metadata and create CAS accounts.""" """Helper functions to get CAS metadata and create CAS accounts."""
import warnings
# TODO: make the python-ldap dependency optional # TODO: make the python-ldap dependency optional
import ldap import ldap
@ -25,6 +27,10 @@ def fetch_cas_account(cas_login):
if not cas_login.isalnum(): if not cas_login.isalnum():
raise ValueError("Illegal CAS login: {}".format(cas_login)) raise ValueError("Illegal CAS login: {}".format(cas_login))
if not getattr(settings, "USE_LDAP", default_conf.USE_LDAP):
warnings.warn("Use of LDAP is disabled", RuntimeWarning)
return None
ldap_url = getattr(settings, "LDAP_SERVER_URL", default_conf.LDAP_SERVER_URL) ldap_url = getattr(settings, "LDAP_SERVER_URL", default_conf.LDAP_SERVER_URL)
ldap_obj = ldap.initialize(ldap_url) ldap_obj = ldap.initialize(ldap_url)
res = ldap_obj.search_s( res = ldap_obj.search_s(

View file

View file

@ -3,11 +3,6 @@ from urllib.parse import urlunparse
from cas import CASClient from cas import CASClient
from django.conf import settings
from authens import conf as default_conf
from authens.models import CASAccount
def get_cas_client(request): def get_cas_client(request):
"""Return a CAS client configured for SPI's CAS.""" """Return a CAS client configured for SPI's CAS."""
@ -31,14 +26,6 @@ def parse_entrance_year(home_dir):
return None return None
dirs = home_dir.split("/") dirs = home_dir.split("/")
allow_staff = getattr(
settings, "AUTHENS_ALLOW_STAFF", default_conf.AUTHENS_ALLOW_STAFF
)
if allow_staff and dirs[:3] == ["", "users", "staffs"]:
return CASAccount.STAFF_ENTRANCE_YEAR
if len(dirs) < 3 or not dirs[2].isdecimal() or dirs[1] != "users": if len(dirs) < 3 or not dirs[2].isdecimal() or dirs[1] != "users":
raise ValueError("Invalid home directory: {}".format(home_dir)) raise ValueError("Invalid home directory: {}".format(home_dir))

View file

@ -102,7 +102,7 @@ class PasswordResetView(SuccessMessageMixin, auth_views.PasswordResetView):
success_url = reverse_lazy("authens:login") success_url = reverse_lazy("authens:login")
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 +138,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

@ -13,10 +13,9 @@ Including another URLconf
1. Import the include() function: from django.urls import include, path 1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
""" """
from example import views
from django.contrib import admin from django.contrib import admin
from django.urls import include, path from django.urls import include, path
from example import views
urlpatterns = [ urlpatterns = [
path("admin/", admin.site.urls), path("admin/", admin.site.urls),

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"]

View file

@ -14,4 +14,5 @@ profile = black
combine_as_imports = true combine_as_imports = true
known_django = django known_django = django
known_first_party = authens,tests known_first_party = authens,tests
line_length = 88
sections = FUTURE,STDLIB,THIRDPARTY,DJANGO,FIRSTPARTY,LOCALFOLDER sections = FUTURE,STDLIB,THIRDPARTY,DJANGO,FIRSTPARTY,LOCALFOLDER

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.1b3",
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 =