Compare commits
43 Commits
d84102657f
...
45d0495a90
Author | SHA1 | Date | |
---|---|---|---|
45d0495a90 | |||
e1cda28c47 | |||
70f97c8501 | |||
7c1263060e | |||
f177e777a9 | |||
70eab84330 | |||
64615036ec | |||
ede5aa680d | |||
597b83e6a4 | |||
6a4d6e5109 | |||
8f91d5292c | |||
14d329fb0f | |||
3e225c9fda | |||
c2798be033 | |||
be1d5d7a65 | |||
f2e86622ae | |||
512c00b2e7 | |||
e16b974761 | |||
f4611af10e | |||
3af2c460b7 | |||
ea09f18377 | |||
8cf1cc7c34 | |||
23477faa3b | |||
09a65b48ef | |||
d586359e3d | |||
7b61b25ff1 | |||
dcb53e9c35 | |||
77f68d1c4c | |||
5e8c837fb2 | |||
3da9bb6914 | |||
369b45a8c4 | |||
4864fa5040 | |||
078e0e85e0 | |||
c3e5a5d188 | |||
2ee1a386b9 | |||
1e53aa21cb | |||
66859c53ba | |||
e943e7f283 | |||
be30cf66fa | |||
2c5e59120c | |||
e52ffb8357 | |||
13ea7aaa9e | |||
295ff983a1 |
1
.gitignore
vendored
@ -131,6 +131,7 @@ venv/
|
|||||||
ENV/
|
ENV/
|
||||||
env.bak/
|
env.bak/
|
||||||
venv.bak/
|
venv.bak/
|
||||||
|
envsco8/
|
||||||
|
|
||||||
# Spyder project settings
|
# Spyder project settings
|
||||||
.spyderproject
|
.spyderproject
|
||||||
|
6
.pylintrc
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[[MESSAGES CONTROL]
|
||||||
|
# pylint and black disagree...
|
||||||
|
disable=bad-continuation
|
||||||
|
|
||||||
|
[TYPECHECK]
|
||||||
|
ignored-classes=Permission
|
78
README.md
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
# SCODOC - gestion de la scolarité
|
# ScoDoc - Gestion de la scolarité - Version ScoDoc 8
|
||||||
|
|
||||||
(c) Emmanuel Viennet 1999 - 2021 (voir LICENCE.txt)
|
(c) Emmanuel Viennet 1999 - 2021 (voir LICENCE.txt)
|
||||||
|
|
||||||
@ -8,12 +8,76 @@ Installation: voir instructions à jour sur <https://scodoc.org>
|
|||||||
|
|
||||||
Documentation utilisateur: <https://scodoc.org>
|
Documentation utilisateur: <https://scodoc.org>
|
||||||
|
|
||||||
Ce logiciel est un produit pour Zope 2.13 écrit en Python (2.4, passé à 2.7 pour ScoDoc7).
|
## Branche ScoDoc 8 expérimentale
|
||||||
|
|
||||||
|
N'utiliser que pour les développements et tests, dans le cadre de la migration de Zope vers Flask.
|
||||||
|
|
||||||
|
La version ScoDoc8 est basée sur Flask (au lieu de Zope) et sur **python 2.7**.
|
||||||
|
Elle ne sera pas mise en production, c'est une version de développement pour le passage à Python 3.
|
||||||
|
|
||||||
|
De très nombreux changements sont susceptibles d'affecter l'API, mais on s'efforce à ce que rien
|
||||||
|
ne soit visible des utilisateurs.
|
||||||
|
|
||||||
|
## Work in Progress (WIP)
|
||||||
|
|
||||||
|
En général, les commits indiqués "WIP" ne tournent pas.
|
||||||
|
|
||||||
|
### État actuel (21 juin 21)
|
||||||
|
|
||||||
|
- l'ensemble des pages est raccordé, sauf "entreprises" et la gestion des utilisateurs (de ce fait, de nombreuses pages plantent).
|
||||||
|
|
||||||
|
- pas d'excel (voir si on réinstalle `pyExcelerator` ou si on migre immédiatement ?), pdf non testé (mais devrait marcher ?).
|
||||||
|
|
||||||
|
**En cours:**
|
||||||
|
|
||||||
|
- recodage pages gestion utilisateurs avec SQLAlchemy.
|
||||||
|
|
||||||
|
- migration bases `SCUUSERS`vers `SCO8USERS`
|
||||||
|
|
||||||
|
- raccorder page "entreprises".
|
||||||
|
|
||||||
|
|
||||||
|
## Setup (sur Debian 10 / python2.7)
|
||||||
|
|
||||||
|
Pour les dev, le plus simple est de partir d'une install fonctionnelle de ScoDoc7 sous git, et de changer de branche
|
||||||
|
|
||||||
|
git checkout ScoDoc8
|
||||||
|
|
||||||
|
Puis d'installer Flask comme suit:
|
||||||
|
|
||||||
|
virtualenv envsco8
|
||||||
|
|
||||||
|
source envsco8/bin/activate
|
||||||
|
|
||||||
|
Installation:
|
||||||
|
|
||||||
|
pip install flask
|
||||||
|
# et pas mal d'autres paquets
|
||||||
|
|
||||||
|
donc utiliser:
|
||||||
|
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
pour régénerer ce fichier:
|
||||||
|
|
||||||
|
pip freeze > requirements.txt
|
||||||
|
|
||||||
|
### Bidouilles temporaires
|
||||||
|
|
||||||
|
Installer le bon vieux `pyExcelerator` dans l'environnement:
|
||||||
|
|
||||||
|
(cd /tmp; tar xfz /opt/scodoc/Products/ScoDoc/config/softs/pyExcelerator-0.6.3a.patched.tgz )
|
||||||
|
(cd /tmp/pyExcelerator-0.6.3a.patched/; python setup.py install)
|
||||||
|
|
||||||
|
## Lancement serveur (développement, sur VM Linux)
|
||||||
|
|
||||||
|
export FLASK_APP=scodoc.py
|
||||||
|
export FLASK_ENV=development
|
||||||
|
flask run --host=0.0.0.0
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
python -m unittest tests.test_users
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
238
TODO
@ -1,238 +0,0 @@
|
|||||||
|
|
||||||
NOTES EN VRAC / Brouillon / Trucs obsoletes
|
|
||||||
|
|
||||||
|
|
||||||
#do_moduleimpl_list\(\{"([a-z_]*)"\s*:\s*(.*)\}\)
|
|
||||||
#do_moduleimpl_list( $1 = $2 )
|
|
||||||
|
|
||||||
#do_moduleimpl_list\([\s\n]*args[\s\n]*=[\s\n]*\{"([a-z_]*)"[\s\n]*:[\s\n]*(.*)[\s\n]*\}[\s\n]*\)
|
|
||||||
|
|
||||||
Upgrade JavaScript
|
|
||||||
- jquery-ui-1.12.1 introduit un problème d'affichage de la barre de menu.
|
|
||||||
Il faudrait la revoir entièrement pour upgrader.
|
|
||||||
On reste donc à jquery-ui-1.10.4.custom
|
|
||||||
Or cette version est incompatible avec jQuery 3 (messages d'erreur dans la console)
|
|
||||||
On reste donc avec jQuery 1.12.14
|
|
||||||
|
|
||||||
|
|
||||||
Suivi des requêtes utilisateurs:
|
|
||||||
table sql: id, ip, authuser, request
|
|
||||||
|
|
||||||
|
|
||||||
* Optim:
|
|
||||||
porcodeb4, avant memorisation des moy_ue:
|
|
||||||
S1 SEM14133 cold start: min 9s, max 12s, avg > 11s
|
|
||||||
inval (add note): 1.33s (pas de recalcul des autres)
|
|
||||||
inval (add abs) : min8s, max 12s (recalcule tout :-()
|
|
||||||
LP SEM14946 cold start: 0.7s - 0.86s
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----------------- LISTE OBSOLETE (très ancienne, à trier) -----------------------
|
|
||||||
BUGS
|
|
||||||
----
|
|
||||||
|
|
||||||
- formsemestre_inscription_with_modules
|
|
||||||
si inscription 'un etud deja inscrit, IntegrityError
|
|
||||||
|
|
||||||
FEATURES REQUESTS
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
* Bulletins:
|
|
||||||
. logos IUT et Univ sur bull PDF
|
|
||||||
. nom departement: nom abbrégé (CJ) ou complet (Carrière Juridiques)
|
|
||||||
. bulletin: deplacer la barre indicateur (cf OLDGEA S2: gêne)
|
|
||||||
. bulletin: click nom titre -> ficheEtud
|
|
||||||
|
|
||||||
. formsemestre_pagebulletin_dialog: marges en mm: accepter "2,5" et "2.5"
|
|
||||||
et valider correctement le form !
|
|
||||||
|
|
||||||
* Jury
|
|
||||||
. recapcomplet: revenir avec qq lignes au dessus de l'étudiant en cours
|
|
||||||
|
|
||||||
|
|
||||||
* Divers
|
|
||||||
. formsemestre_editwithmodules: confirmer suppression modules
|
|
||||||
(et pour l'instant impossible si evaluations dans le module)
|
|
||||||
|
|
||||||
* Modules et UE optionnelles:
|
|
||||||
. UE capitalisées: donc dispense possible dans semestre redoublé.
|
|
||||||
traitable en n'inscrivant pas l'etudiant au modules
|
|
||||||
de cette UE: faire interface utilisateur
|
|
||||||
|
|
||||||
. page pour inscription d'un etudiant a un module
|
|
||||||
. page pour visualiser les modules auquel un etudiant est inscrit,
|
|
||||||
et le desinscrire si besoin.
|
|
||||||
|
|
||||||
. ficheEtud indiquer si inscrit au module sport
|
|
||||||
|
|
||||||
* Absences
|
|
||||||
. EtatAbsences : verifier dates (en JS)
|
|
||||||
. Listes absences pdf et listes groupes pdf + emargements (cf mail Nathalie)
|
|
||||||
. absences par demi-journées sur EtatAbsencesDate (? à vérifier)
|
|
||||||
. formChoixSemestreGroupe: utilisé par Absences/index_html
|
|
||||||
a améliorer
|
|
||||||
|
|
||||||
|
|
||||||
* Notes et évaluations:
|
|
||||||
. Exception "Not an OLE file": generer page erreur plus explicite
|
|
||||||
. Dates evaluation: utiliser JS pour calendrier
|
|
||||||
. Saisie des notes: si une note invalide, l'indiquer dans le listing (JS ?)
|
|
||||||
. et/ou: notes invalides: afficher les noms des etudiants concernes
|
|
||||||
dans le message d'erreur.
|
|
||||||
. upload excel: message erreur peu explicite:
|
|
||||||
* Feuille "Saisie notes", 17 lignes
|
|
||||||
* Erreur: la feuille contient 1 notes invalides
|
|
||||||
* Notes invalides pour les id: ['10500494']
|
|
||||||
(pas de notes modifiées)
|
|
||||||
Notes chargées. <<< CONTRADICTOIRE !!
|
|
||||||
|
|
||||||
. recap complet semestre:
|
|
||||||
Options:
|
|
||||||
- choix groupes
|
|
||||||
- critère de tri (moy ou alphab)
|
|
||||||
- nb de chiffres a afficher
|
|
||||||
|
|
||||||
+ definir des "catégories" d'évaluations (eg "théorie","pratique")
|
|
||||||
afin de n'afficher que des moyennes "de catégorie" dans
|
|
||||||
le bulletin.
|
|
||||||
|
|
||||||
. liste des absents à une eval et croisement avec BD absences
|
|
||||||
|
|
||||||
. notes_evaluation_listenotes
|
|
||||||
- afficher groupes, moyenne, #inscrits, #absents, #manquantes dans l'en-tete.
|
|
||||||
- lien vers modif notes (selon role)
|
|
||||||
|
|
||||||
. Export excel des notes d'evaluation: indiquer date, et autres infos en haut.
|
|
||||||
. Génération PDF listes notes
|
|
||||||
. Page recap notes moyennes par groupes (choisir type de groupe?)
|
|
||||||
|
|
||||||
. (GEA) edition tableau notes avec tous les evals d'un module
|
|
||||||
(comme notes_evaluation_listenotes mais avec tt les evals)
|
|
||||||
|
|
||||||
|
|
||||||
* Non prioritaire:
|
|
||||||
. optimiser scolar_news_summary
|
|
||||||
. recapitulatif des "nouvelles"
|
|
||||||
- dernieres notes
|
|
||||||
- changement de statuts (demissions,inscriptions)
|
|
||||||
- annotations
|
|
||||||
- entreprises
|
|
||||||
|
|
||||||
. notes_table: pouvoir changer decision sans invalider tout le cache
|
|
||||||
. navigation: utiliser Session pour montrer historique pages vues ?
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
A faire:
|
|
||||||
- fiche etud: code dec jury sur ligne 1
|
|
||||||
si ancien, indiquer autorisation inscription sous le parcours
|
|
||||||
|
|
||||||
- saisie notes: undo
|
|
||||||
- saisie notes: validation
|
|
||||||
- ticket #18:
|
|
||||||
UE capitalisées: donc dispense possible dans semestre redoublé. Traitable en n'inscrivant pas l'etudiant aux modules de cette UE: faire interface utilisateur.
|
|
||||||
|
|
||||||
Prévoir d'entrer une UE capitalisée avec sa note, date d'obtention et un commentaire. Coupler avec la désincription aux modules (si l'étudiant a été inscrit avec ses condisciples).
|
|
||||||
|
|
||||||
|
|
||||||
- Ticket #4: Afin d'éviter les doublons, vérifier qu'il n'existe pas d'homonyme proche lors de la création manuelle d'un étudiant. (confirmé en ScoDoc 6, vérifier aussi les imports Excel)
|
|
||||||
|
|
||||||
- Ticket #74: Il est possible d'inscrire un étudiant sans prénom par un import excel !!!
|
|
||||||
|
|
||||||
- Ticket #64: saisir les absences pour la promo entiere (et pas par groupe). Des fois, je fais signer une feuille de presence en amphi a partir de la liste de tous les etudiants. Ensuite pour reporter les absents par groupe, c'est galere.
|
|
||||||
|
|
||||||
- Ticket #62: Lors des exports Excel, le format des cellules n'est pas reconnu comme numérique sous Windows (pas de problèmes avec Macintosh et Linux).
|
|
||||||
|
|
||||||
A confirmer et corriger.
|
|
||||||
|
|
||||||
- Ticket #75: On peut modifier une décision de jury (et les autorisations de passage associées), mais pas la supprimer purement et simplement.
|
|
||||||
Ajoute ce choix dans les "décisions manuelles".
|
|
||||||
|
|
||||||
- Ticket #37: Page recap notes moyennes par groupes
|
|
||||||
Construire une page avec les moyennes dans chaque UE ou module par groupe d'étudiants.
|
|
||||||
Et aussi pourquoi pas ventiler par type de bac, sexe, parcours (nombre de semestre de parcours) ?
|
|
||||||
redemandé par CJ: à faire avant mai 2008 !
|
|
||||||
|
|
||||||
- Ticket #75: Synchro Apogée: choisir les etudiants
|
|
||||||
Sur la page de syncho Apogée (formsemestre_synchro_etuds), on peut choisir (cocher) les étudiants Apogée à importer. mais on ne peut pas le faire s'ils sont déjà dans ScoDoc: il faudrait ajouter des checkboxes dans toutes les listes.
|
|
||||||
|
|
||||||
- Ticket #9: Format des valeurs de marges des bulletins.
|
|
||||||
formsemestre_pagebulletin_dialog: marges en mm: accepter "2,5" et "2.5" et valider correctement le form !
|
|
||||||
|
|
||||||
- Ticket #17: Suppression modules dans semestres
|
|
||||||
formsemestre_editwithmodules: confirmer suppression modules
|
|
||||||
|
|
||||||
- Ticket #29: changer le stoquage des photos, garder une version HD.
|
|
||||||
|
|
||||||
- bencher NotesTable sans calcul de moyennes. Etudier un cache des moyennes de modules.
|
|
||||||
- listes d'utilisateurs (modules): remplacer menus par champs texte + completions javascript
|
|
||||||
- documenter archives sur Wiki
|
|
||||||
- verifier paquet Debian pour font pdf (reportab: helvetica ... plante si font indisponible)
|
|
||||||
- chercher comment obtenir une page d'erreur correcte pour les pages POST
|
|
||||||
(eg: si le font n'existe pas, archive semestre echoue sans page d'erreur)
|
|
||||||
? je ne crois pas que le POST soit en cause. HTTP status=500
|
|
||||||
ne se produit pas avec Safari
|
|
||||||
- essayer avec IE / Win98
|
|
||||||
- faire apparaitre les diplômés sur le graphe des parcours
|
|
||||||
- démission: formulaire: vérifier que la date est bien dans le semestre
|
|
||||||
|
|
||||||
+ graphe parcours: aligner en colonnes selon les dates (de fin), placer les diplomes
|
|
||||||
dans la même colone que le semestre terminal.
|
|
||||||
|
|
||||||
- modif gestion utilisateurs (donner droits en fct du dept. d'appartenance, bug #57)
|
|
||||||
- modif form def. utilisateur (dept appartenance)
|
|
||||||
- utilisateurs: source externe
|
|
||||||
- archivage des semestres
|
|
||||||
|
|
||||||
|
|
||||||
o-------------------------------------o
|
|
||||||
|
|
||||||
* Nouvelle gestion utilisateurs:
|
|
||||||
objectif: dissocier l'authentification de la notion "d'enseignant"
|
|
||||||
On a une source externe "d'utilisateurs" (annuaire LDAP ou base SQL)
|
|
||||||
qui permet seulement de:
|
|
||||||
- authentifier un utilisateur (login, passwd)
|
|
||||||
- lister un utilisateur: login => firstname, lastname, email
|
|
||||||
- lister les utilisateurs
|
|
||||||
|
|
||||||
et une base interne ScoDoc "d'acteurs" (enseignants, administratifs).
|
|
||||||
Chaque acteur est défini par:
|
|
||||||
- actor_id, firstname, lastname
|
|
||||||
date_creation, date_expiration,
|
|
||||||
roles, departement,
|
|
||||||
email (+flag indiquant s'il faut utiliser ce mail ou celui de
|
|
||||||
l'utilisateur ?)
|
|
||||||
state (on, off) (pour desactiver avant expiration ?)
|
|
||||||
user_id (login) => lien avec base utilisateur
|
|
||||||
|
|
||||||
On offrira une source d'utilisateurs SQL (base partagée par tous les dept.
|
|
||||||
d'une instance ScoDoc), mais dans la plupart des cas les gens utiliseront
|
|
||||||
un annuaire LDAP.
|
|
||||||
|
|
||||||
La base d'acteurs remplace ScoUsers. Les objets ScoDoc (semestres,
|
|
||||||
modules etc) font référence à des acteurs (eg responsable_id est un actor_id).
|
|
||||||
|
|
||||||
Le lien entre les deux ?
|
|
||||||
Loger un utilisateur => authentification utilisateur + association d'un acteur
|
|
||||||
Cela doit se faire au niveau d'un UserFolder Zope, pour avoir les
|
|
||||||
bons rôles et le contrôle d'accès adéquat.
|
|
||||||
(Il faut donc coder notre propre UserFolder).
|
|
||||||
On ne peut associer qu'un acteur à l'état 'on' et non expiré.
|
|
||||||
|
|
||||||
Opérations ScoDoc:
|
|
||||||
- paramétrage: choisir et paramétrer source utilisateurs
|
|
||||||
- ajouter utilisateur: choisir un utilisateur dans la liste
|
|
||||||
et lui associer un nouvel acteur (choix des rôles, des dates)
|
|
||||||
+ éventuellement: synchro d'un ensemble d'utilisateurs, basé sur
|
|
||||||
une requête (eg LDAP) précise (quelle interface utilisateur proposer ?)
|
|
||||||
|
|
||||||
- régulièrement (cron) aviser quelqu'un (le chef) de l'expiration des acteurs.
|
|
||||||
- changer etat d'un acteur (on/off)
|
|
||||||
|
|
||||||
|
|
||||||
o-------------------------------------o
|
|
||||||
|
|
1980
ZAbsences.py
1301
ZEntreprises.py
999
ZScoDoc.py
@ -1,999 +0,0 @@
|
|||||||
# -*- mode: python -*-
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
##############################################################################
|
|
||||||
#
|
|
||||||
# Gestion scolarite IUT
|
|
||||||
#
|
|
||||||
# Copyright (c) 1999 - 2021 Emmanuel Viennet. All rights reserved.
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
#
|
|
||||||
# Emmanuel Viennet emmanuel.viennet@viennet.net
|
|
||||||
#
|
|
||||||
##############################################################################
|
|
||||||
|
|
||||||
"""Site ScoDoc pour plusieurs departements:
|
|
||||||
gestion de l'installation et des creation de départements.
|
|
||||||
|
|
||||||
Chaque departement est géré par un ZScolar sous ZScoDoc.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import time
|
|
||||||
import datetime
|
|
||||||
import string
|
|
||||||
import glob
|
|
||||||
import re
|
|
||||||
import inspect
|
|
||||||
import urllib
|
|
||||||
import urllib2
|
|
||||||
import cgi
|
|
||||||
import xml
|
|
||||||
|
|
||||||
from cStringIO import StringIO
|
|
||||||
from zipfile import ZipFile
|
|
||||||
import os.path
|
|
||||||
import traceback
|
|
||||||
|
|
||||||
from email.MIMEMultipart import ( # pylint: disable=no-name-in-module,import-error
|
|
||||||
MIMEMultipart,
|
|
||||||
)
|
|
||||||
from email.MIMEText import MIMEText # pylint: disable=no-name-in-module,import-error
|
|
||||||
from email.MIMEBase import MIMEBase # pylint: disable=no-name-in-module,import-error
|
|
||||||
from email.Header import Header # pylint: disable=no-name-in-module,import-error
|
|
||||||
from email import Encoders # pylint: disable=no-name-in-module,import-error
|
|
||||||
|
|
||||||
from sco_zope import (
|
|
||||||
ObjectManager,
|
|
||||||
PropertyManager,
|
|
||||||
RoleManager,
|
|
||||||
Item,
|
|
||||||
Persistent,
|
|
||||||
Implicit,
|
|
||||||
ClassSecurityInfo,
|
|
||||||
DTMLFile,
|
|
||||||
Globals,
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
import Products.ZPsycopgDA.DA as ZopeDA
|
|
||||||
except:
|
|
||||||
import ZPsycopgDA.DA as ZopeDA # interp.py
|
|
||||||
|
|
||||||
import sco_utils as scu
|
|
||||||
import VERSION
|
|
||||||
from notes_log import log
|
|
||||||
import sco_find_etud
|
|
||||||
import sco_users
|
|
||||||
from sco_permissions import (
|
|
||||||
ScoView,
|
|
||||||
ScoEnsView,
|
|
||||||
ScoImplement,
|
|
||||||
ScoChangeFormation,
|
|
||||||
ScoObservateur,
|
|
||||||
ScoEtudInscrit,
|
|
||||||
ScoEtudChangeGroups,
|
|
||||||
ScoEtudChangeAdr,
|
|
||||||
ScoEtudSupprAnnotations,
|
|
||||||
ScoEditAllEvals,
|
|
||||||
ScoEditAllNotes,
|
|
||||||
ScoEditFormationTags,
|
|
||||||
ScoEditApo,
|
|
||||||
ScoSuperAdmin,
|
|
||||||
)
|
|
||||||
from sco_exceptions import ScoValueError, ScoLockedFormError, ScoGenError, AccessDenied
|
|
||||||
|
|
||||||
|
|
||||||
class ZScoDoc(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Implicit):
|
|
||||||
|
|
||||||
"ZScoDoc object"
|
|
||||||
|
|
||||||
meta_type = "ZScoDoc"
|
|
||||||
security = ClassSecurityInfo()
|
|
||||||
file_path = Globals.package_home(globals())
|
|
||||||
|
|
||||||
# This is the list of the methods associated to 'tabs' in the ZMI
|
|
||||||
# Be aware that The first in the list is the one shown by default, so if
|
|
||||||
# the 'View' tab is the first, you will never see your tabs by cliquing
|
|
||||||
# on the object.
|
|
||||||
manage_options = (
|
|
||||||
({"label": "Contents", "action": "manage_main"},)
|
|
||||||
+ PropertyManager.manage_options # add the 'Properties' tab
|
|
||||||
+ ({"label": "View", "action": "index_html"},)
|
|
||||||
+ Item.manage_options # add the 'Undo' & 'Owner' tab
|
|
||||||
+ RoleManager.manage_options # add the 'Security' tab
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, id, title):
|
|
||||||
"Initialise a new instance of ZScoDoc"
|
|
||||||
self.id = id
|
|
||||||
self.title = title
|
|
||||||
self.manage_addProperty("admin_password_initialized", "0", "string")
|
|
||||||
|
|
||||||
security.declareProtected(ScoView, "ScoDocURL")
|
|
||||||
|
|
||||||
def ScoDocURL(self):
|
|
||||||
"base URL for this instance (top level for ScoDoc site)"
|
|
||||||
return self.absolute_url()
|
|
||||||
|
|
||||||
def _check_admin_perm(self, REQUEST):
|
|
||||||
"""Check if user has permission to add/delete departements"""
|
|
||||||
authuser = REQUEST.AUTHENTICATED_USER
|
|
||||||
if authuser.has_role("manager") or authuser.has_permission(ScoSuperAdmin, self):
|
|
||||||
return ""
|
|
||||||
else:
|
|
||||||
return """<h2>Vous n'avez pas le droit d'accéder à cette page</h2>"""
|
|
||||||
|
|
||||||
def _check_users_folder(self, REQUEST=None):
|
|
||||||
"""Vérifie UserFolder et le crée s'il le faut"""
|
|
||||||
try:
|
|
||||||
_ = self.UsersDB
|
|
||||||
return "<!-- uf ok -->"
|
|
||||||
except:
|
|
||||||
e = self._check_admin_perm(REQUEST)
|
|
||||||
if not e: # admin permissions:
|
|
||||||
self.create_users_cnx(REQUEST)
|
|
||||||
self.create_users_folder(REQUEST)
|
|
||||||
return '<div class="head_message">Création du connecteur utilisateurs réussie</div>'
|
|
||||||
else:
|
|
||||||
return """<div class="head_message">Installation non terminée: connectez vous avec les droits d'administrateur</div>"""
|
|
||||||
|
|
||||||
security.declareProtected("View", "create_users_folder")
|
|
||||||
|
|
||||||
def create_users_folder(self, REQUEST=None):
|
|
||||||
"""Create Zope user folder"""
|
|
||||||
e = self._check_admin_perm(REQUEST)
|
|
||||||
if e:
|
|
||||||
return e
|
|
||||||
|
|
||||||
if REQUEST is None:
|
|
||||||
REQUEST = {}
|
|
||||||
|
|
||||||
REQUEST.form["pgauth_connection"] = "UsersDB"
|
|
||||||
REQUEST.form["pgauth_table"] = "sco_users"
|
|
||||||
REQUEST.form["pgauth_usernameColumn"] = "user_name"
|
|
||||||
REQUEST.form["pgauth_passwordColumn"] = "passwd"
|
|
||||||
REQUEST.form["pgauth_rolesColumn"] = "roles"
|
|
||||||
|
|
||||||
add_method = self.manage_addProduct["OFSP"].manage_addexUserFolder
|
|
||||||
log("create_users_folder: in %s" % self.id)
|
|
||||||
return add_method(
|
|
||||||
authId="pgAuthSource",
|
|
||||||
propId="nullPropSource",
|
|
||||||
memberId="nullMemberSource",
|
|
||||||
groupId="nullGroupSource",
|
|
||||||
cryptoId="MD51",
|
|
||||||
# doAuth='1', doProp='1', doMember='1', doGroup='1', allDone='1',
|
|
||||||
cookie_mode=2,
|
|
||||||
session_length=500,
|
|
||||||
not_session_length=0,
|
|
||||||
REQUEST=REQUEST,
|
|
||||||
)
|
|
||||||
|
|
||||||
def _fix_users_folder(self):
|
|
||||||
"""removes docLogin and docLogout dtml methods from exUserFolder, so that we use ours.
|
|
||||||
(called each time be index_html, to fix old ScoDoc installations.)
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
self.acl_users.manage_delObjects(ids=["docLogin", "docLogout"])
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
# add missing getAuthFailedMessage (bug in exUserFolder ?)
|
|
||||||
try:
|
|
||||||
_ = self.getAuthFailedMessage
|
|
||||||
except:
|
|
||||||
log("adding getAuthFailedMessage to Zope install")
|
|
||||||
parent = self.aq_parent
|
|
||||||
from OFS.DTMLMethod import addDTMLMethod # pylint: disable=import-error
|
|
||||||
|
|
||||||
addDTMLMethod(parent, "getAuthFailedMessage", file="Identification")
|
|
||||||
|
|
||||||
security.declareProtected("View", "create_users_cnx")
|
|
||||||
|
|
||||||
def create_users_cnx(self, REQUEST=None):
|
|
||||||
"""Create Zope connector to UsersDB
|
|
||||||
|
|
||||||
Note: la connexion est fixée (SCOUSERS) (base crée par l'installeur) !
|
|
||||||
Les utilisateurs avancés pourront la changer ensuite.
|
|
||||||
"""
|
|
||||||
# ce connecteur zope - db est encore pour l'instant utilisé par exUserFolder.pgAuthSource
|
|
||||||
# (en lecture seule en principe)
|
|
||||||
oid = "UsersDB"
|
|
||||||
log("create_users_cnx: in %s" % self.id)
|
|
||||||
da = ZopeDA.Connection(
|
|
||||||
oid,
|
|
||||||
"Cnx bd utilisateurs",
|
|
||||||
scu.SCO_DEFAULT_SQL_USERS_CNX,
|
|
||||||
False,
|
|
||||||
check=1,
|
|
||||||
tilevel=2,
|
|
||||||
encoding="LATIN1",
|
|
||||||
)
|
|
||||||
self._setObject(oid, da)
|
|
||||||
|
|
||||||
security.declareProtected("View", "change_admin_user")
|
|
||||||
|
|
||||||
def change_admin_user(self, password, REQUEST=None):
|
|
||||||
"""Change password of admin user"""
|
|
||||||
# note: controle sur le role et non pas sur une permission
|
|
||||||
# (non definies au top level)
|
|
||||||
if not REQUEST.AUTHENTICATED_USER.has_role("Manager"):
|
|
||||||
log("user %s is not Manager" % REQUEST.AUTHENTICATED_USER)
|
|
||||||
log("roles=%s" % REQUEST.AUTHENTICATED_USER.getRolesInContext(self))
|
|
||||||
raise AccessDenied("vous n'avez pas le droit d'effectuer cette opération")
|
|
||||||
log("trying to change admin password")
|
|
||||||
# 1-- check strong password
|
|
||||||
if not sco_users.is_valid_password(password):
|
|
||||||
log("refusing weak password")
|
|
||||||
return REQUEST.RESPONSE.redirect(
|
|
||||||
"change_admin_user_form?message=Mot%20de%20passe%20trop%20simple,%20recommencez"
|
|
||||||
)
|
|
||||||
# 2-- change password for admin user
|
|
||||||
username = "admin"
|
|
||||||
acl_users = self.aq_parent.acl_users
|
|
||||||
user = acl_users.getUser(username)
|
|
||||||
r = acl_users._changeUser(
|
|
||||||
username, password, password, user.roles, user.domains
|
|
||||||
)
|
|
||||||
if not r:
|
|
||||||
# OK, set property to indicate we changed the password
|
|
||||||
log("admin password changed successfully")
|
|
||||||
self.manage_changeProperties(admin_password_initialized="1")
|
|
||||||
return r or REQUEST.RESPONSE.redirect("index_html")
|
|
||||||
|
|
||||||
security.declareProtected("View", "change_admin_user_form")
|
|
||||||
|
|
||||||
def change_admin_user_form(self, message="", REQUEST=None):
|
|
||||||
"""Form allowing to change the ScoDoc admin password"""
|
|
||||||
# note: controle sur le role et non pas sur une permission
|
|
||||||
# (non definies au top level)
|
|
||||||
if not REQUEST.AUTHENTICATED_USER.has_role("Manager"):
|
|
||||||
raise AccessDenied("vous n'avez pas le droit d'effectuer cette opération")
|
|
||||||
H = [
|
|
||||||
self.scodoc_top_html_header(
|
|
||||||
REQUEST, page_title="ScoDoc: changement mot de passe"
|
|
||||||
)
|
|
||||||
]
|
|
||||||
if message:
|
|
||||||
H.append('<div id="message">%s</div>' % message)
|
|
||||||
H.append(
|
|
||||||
"""<h2>Changement du mot de passe administrateur (utilisateur admin)</h2>
|
|
||||||
<p>
|
|
||||||
<form action="change_admin_user" method="post"><table>
|
|
||||||
<tr><td>Nouveau mot de passe:</td><td><input type="password" size="14" name="password"/></td></tr>
|
|
||||||
<tr><td>Confirmation: </td><td><input type="password" size="14" name="password2" /></td></tr>
|
|
||||||
</table>
|
|
||||||
<input type="submit" value="Changer">
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
H.append("""</body></html>""")
|
|
||||||
return "\n".join(H)
|
|
||||||
|
|
||||||
security.declareProtected("View", "list_depts")
|
|
||||||
|
|
||||||
def list_depts(self, viewable=True, format=None, REQUEST=None):
|
|
||||||
"""List departments
|
|
||||||
If viewable, list only depts viewable the current user.
|
|
||||||
"""
|
|
||||||
authuser = REQUEST.AUTHENTICATED_USER
|
|
||||||
viewable = int(viewable)
|
|
||||||
return scu.sendResult(
|
|
||||||
REQUEST,
|
|
||||||
[
|
|
||||||
d.id
|
|
||||||
for d in self._list_depts()
|
|
||||||
if (not viewable) or authuser.has_permission(ScoView, d.Scolarite)
|
|
||||||
],
|
|
||||||
name="depts",
|
|
||||||
format=format,
|
|
||||||
)
|
|
||||||
|
|
||||||
def _list_depts(self, REQUEST=None): # not published
|
|
||||||
# List departments folders
|
|
||||||
# (returns a list of Zope folders containing a ZScolar instance)
|
|
||||||
folders = self.objectValues("Folder")
|
|
||||||
# select folders with Scolarite object:
|
|
||||||
r = []
|
|
||||||
for folder in folders:
|
|
||||||
try:
|
|
||||||
_ = folder.Scolarite
|
|
||||||
r.append(folder)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
return r
|
|
||||||
|
|
||||||
security.declareProtected("View", "create_dept")
|
|
||||||
|
|
||||||
def create_dept(self, REQUEST=None, DeptId="", pass2=False):
|
|
||||||
"""Creation (ajout) d'un site departement
|
|
||||||
(instance ZScolar + dossier la contenant)
|
|
||||||
"""
|
|
||||||
e = self._check_admin_perm(REQUEST)
|
|
||||||
if e:
|
|
||||||
return e
|
|
||||||
|
|
||||||
if not DeptId:
|
|
||||||
raise ValueError("nom de departement invalide")
|
|
||||||
if not pass2:
|
|
||||||
# 1- Creation de repertoire Dept
|
|
||||||
log("creating Zope folder " + DeptId)
|
|
||||||
add_method = self.manage_addProduct["OFSP"].manage_addFolder
|
|
||||||
add_method(DeptId, title="Site dept. " + DeptId)
|
|
||||||
|
|
||||||
DeptFolder = self[DeptId]
|
|
||||||
|
|
||||||
if not pass2:
|
|
||||||
# 2- Creation du repertoire Fotos
|
|
||||||
log("creating Zope folder %s/Fotos" % DeptId)
|
|
||||||
add_method = DeptFolder.manage_addProduct["OFSP"].manage_addFolder
|
|
||||||
add_method("Fotos", title="Photos identites " + DeptId)
|
|
||||||
|
|
||||||
# 3- Creation instance ScoDoc
|
|
||||||
log("creating Zope ZScolar instance")
|
|
||||||
add_method = DeptFolder.manage_addProduct["ScoDoc"].manage_addZScolarForm
|
|
||||||
return add_method(DeptId, REQUEST=REQUEST)
|
|
||||||
|
|
||||||
security.declareProtected("View", "delete_dept")
|
|
||||||
|
|
||||||
def delete_dept(self, REQUEST=None, DeptId="", force=False):
|
|
||||||
"""Supprime un departement (de Zope seulement, ne touche pas la BD)"""
|
|
||||||
e = self._check_admin_perm(REQUEST)
|
|
||||||
if e:
|
|
||||||
return e
|
|
||||||
|
|
||||||
if not force and DeptId not in [x.id for x in self._list_depts()]:
|
|
||||||
raise ValueError("nom de departement invalide")
|
|
||||||
|
|
||||||
self.manage_delObjects(ids=[DeptId])
|
|
||||||
|
|
||||||
return (
|
|
||||||
"<p>Département "
|
|
||||||
+ DeptId
|
|
||||||
+ """ supprimé du serveur web (la base de données n'est pas affectée)!</p><p><a href="/ScoDoc">Continuer</a></p>"""
|
|
||||||
)
|
|
||||||
|
|
||||||
_top_level_css = """
|
|
||||||
<style type="text/css">
|
|
||||||
</style>"""
|
|
||||||
|
|
||||||
_html_begin = """<?xml version="1.0" encoding="%(encoding)s"?>
|
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
|
||||||
<head>
|
|
||||||
<title>%(page_title)s</title>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=%(encoding)s" />
|
|
||||||
<meta http-equiv="Content-Style-Type" content="text/css" />
|
|
||||||
<meta name="LANG" content="fr" />
|
|
||||||
<meta name="DESCRIPTION" content="ScoDoc" />
|
|
||||||
|
|
||||||
<link type="text/css" rel="stylesheet" href="/ScoDoc/static/libjs/jquery-ui-1.10.4.custom/css/smoothness/jquery-ui-1.10.4.custom.min.css" />
|
|
||||||
|
|
||||||
<link href="/ScoDoc/static/css/scodoc.css" rel="stylesheet" type="text/css" />
|
|
||||||
<link href="/ScoDoc/static/css/menu.css" rel="stylesheet" type="text/css" />
|
|
||||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/libjs/menu.js"></script>
|
|
||||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/libjs/sorttable.js"></script>
|
|
||||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/libjs/bubble.js"></script>
|
|
||||||
<script type="text/javascript">
|
|
||||||
window.onload=function(){enableTooltips("gtrcontent")};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/jQuery/jquery.js"></script>
|
|
||||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/jQuery/jquery-migrate-1.2.0.min.js"></script>
|
|
||||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/libjs/jquery.field.min.js"></script>
|
|
||||||
|
|
||||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/libjs/jquery-ui-1.10.4.custom/js/jquery-ui-1.10.4.custom.min.js"></script>
|
|
||||||
|
|
||||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/libjs/qtip/jquery.qtip-3.0.3.min.js"></script>
|
|
||||||
<link type="text/css" rel="stylesheet" href="/ScoDoc/static/libjs/qtip/jquery.qtip-3.0.3.min.css" />
|
|
||||||
|
|
||||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/js/scodoc.js"></script>
|
|
||||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/js/etud_info.js"></script>
|
|
||||||
"""
|
|
||||||
|
|
||||||
def scodoc_top_html_header(self, REQUEST, page_title="ScoDoc"):
|
|
||||||
H = [
|
|
||||||
self._html_begin
|
|
||||||
% {"page_title": "ScoDoc: bienvenue", "encoding": scu.SCO_ENCODING},
|
|
||||||
self._top_level_css,
|
|
||||||
"""</head><body class="gtrcontent" id="gtrcontent">""",
|
|
||||||
scu.CUSTOM_HTML_HEADER_CNX,
|
|
||||||
]
|
|
||||||
return "\n".join(H)
|
|
||||||
|
|
||||||
security.declareProtected("View", "index_html")
|
|
||||||
|
|
||||||
def index_html(self, REQUEST=None, message=None):
|
|
||||||
"""Top level page for ScoDoc"""
|
|
||||||
authuser = REQUEST.AUTHENTICATED_USER
|
|
||||||
deptList = self._list_depts()
|
|
||||||
self._fix_users_folder() # fix our exUserFolder
|
|
||||||
isAdmin = not self._check_admin_perm(REQUEST)
|
|
||||||
try:
|
|
||||||
admin_password_initialized = self.admin_password_initialized
|
|
||||||
except:
|
|
||||||
admin_password_initialized = "0"
|
|
||||||
if isAdmin and admin_password_initialized != "1":
|
|
||||||
return REQUEST.RESPONSE.redirect(
|
|
||||||
"ScoDoc/change_admin_user_form?message=Le%20mot%20de%20passe%20administrateur%20doit%20etre%20change%20!"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Si l'URL indique que l'on est dans un folder, affiche page login du departement
|
|
||||||
try:
|
|
||||||
deptfoldername = REQUEST.URL0.split("ScoDoc")[1].split("/")[1]
|
|
||||||
if deptfoldername in [x.id for x in self._list_depts()]:
|
|
||||||
return self.index_dept(deptfoldername=deptfoldername, REQUEST=REQUEST)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
H = [
|
|
||||||
self.scodoc_top_html_header(REQUEST, page_title="ScoDoc: bienvenue"),
|
|
||||||
self._check_users_folder(REQUEST=REQUEST), # ensure setup is done
|
|
||||||
]
|
|
||||||
if message:
|
|
||||||
H.append('<div id="message">%s</div>' % message)
|
|
||||||
|
|
||||||
if isAdmin and not message:
|
|
||||||
H.append('<div id="message">Attention: connecté comme administrateur</div>')
|
|
||||||
|
|
||||||
H.append(
|
|
||||||
"""
|
|
||||||
<div class="maindiv">
|
|
||||||
<h2>ScoDoc: gestion scolarité</h2>
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
if authuser.has_role("Authenticated"):
|
|
||||||
H.append(
|
|
||||||
"""<p>Bonjour <font color="red"><b>%s</b></font>.</p>""" % str(authuser)
|
|
||||||
)
|
|
||||||
H.append(
|
|
||||||
"""<p>N'oubliez pas de vous <a href="acl_users/logout">déconnecter</a> après usage.</p>"""
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
H.append(
|
|
||||||
"""<p>Ce site est <font color="red"><b>réservé au personnel autorisé</b></font></p>"""
|
|
||||||
)
|
|
||||||
H.append(self.authentication_form(destination="."))
|
|
||||||
|
|
||||||
if not deptList:
|
|
||||||
H.append("<em>aucun département existant !</em>")
|
|
||||||
# si pas de dept et pas admin, propose lien pour loger admin
|
|
||||||
if not isAdmin:
|
|
||||||
H.append(
|
|
||||||
"""<p><a href="/force_admin_authentication">Identifiez vous comme administrateur</a> (au début: nom 'admin', mot de passe 'scodoc')</p>"""
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
H.append('<ul class="main">')
|
|
||||||
if isAdmin:
|
|
||||||
dest_folder = "/Scolarite"
|
|
||||||
else:
|
|
||||||
dest_folder = ""
|
|
||||||
for deptFolder in self._list_depts():
|
|
||||||
if authuser.has_permission(ScoView, deptFolder.Scolarite):
|
|
||||||
link_cls = "link_accessible"
|
|
||||||
else:
|
|
||||||
link_cls = "link_unauthorized"
|
|
||||||
# Essai de recuperer le nom du departement dans ses preferences
|
|
||||||
try:
|
|
||||||
DeptName = (
|
|
||||||
deptFolder.Scolarite.get_preference("DeptName") or deptFolder.id
|
|
||||||
)
|
|
||||||
except:
|
|
||||||
DeptName = deptFolder.id
|
|
||||||
H.append(
|
|
||||||
'<li><a class="stdlink %s" href="%s%s">Département %s</a>'
|
|
||||||
% (link_cls, deptFolder.absolute_url(), dest_folder, DeptName)
|
|
||||||
)
|
|
||||||
# check if roles are initialized in this depts, and do it if necessary
|
|
||||||
if deptFolder.Scolarite.roles_initialized == "0":
|
|
||||||
if isAdmin:
|
|
||||||
deptFolder.Scolarite._setup_initial_roles_and_permissions()
|
|
||||||
else:
|
|
||||||
H.append(" (non initialisé, connectez vous comme admin)")
|
|
||||||
H.append("</li>")
|
|
||||||
H.append("</ul>")
|
|
||||||
# Recherche etudiant
|
|
||||||
H.append(sco_find_etud.form_search_etud_in_accessible_depts(self, REQUEST))
|
|
||||||
|
|
||||||
if isAdmin:
|
|
||||||
H.append('<p><a href="scodoc_admin">Administration de ScoDoc</a></p>')
|
|
||||||
else:
|
|
||||||
H.append(
|
|
||||||
'<p><a href="%s/force_admin_authentication">Se connecter comme administrateur</a></p>'
|
|
||||||
% REQUEST.BASE0
|
|
||||||
)
|
|
||||||
|
|
||||||
H.append(
|
|
||||||
"""
|
|
||||||
<div id="scodoc_attribution">
|
|
||||||
<p><a href="%s">ScoDoc</a> est un logiciel libre de suivi de la scolarité des étudiants conçu par
|
|
||||||
E. Viennet (Université Paris 13).</p>
|
|
||||||
</div>
|
|
||||||
</div>"""
|
|
||||||
% (scu.SCO_WEBSITE,)
|
|
||||||
)
|
|
||||||
|
|
||||||
H.append("""</body></html>""")
|
|
||||||
return "\n".join(H)
|
|
||||||
|
|
||||||
def authentication_form(self, destination=""):
|
|
||||||
"""html snippet for authentication"""
|
|
||||||
return (
|
|
||||||
"""<!-- authentication_form -->
|
|
||||||
<form action="doLogin" method="post">
|
|
||||||
<input type="hidden" name="destination" value="%s"/>
|
|
||||||
<p>
|
|
||||||
<table border="0" cellpadding="3">
|
|
||||||
<tr>
|
|
||||||
<td><b>Nom:</b></td>
|
|
||||||
<td><input id="name" type="text" name="__ac_name" size="20"/></td>
|
|
||||||
</tr><tr>
|
|
||||||
<td><b>Mot de passe:</b></td>
|
|
||||||
<td><input id="password" type="password" name="__ac_password" size="20"/></td>
|
|
||||||
<td><input id="submit" name="submit" type="submit" value="OK"/></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</p>
|
|
||||||
</form>"""
|
|
||||||
% destination
|
|
||||||
)
|
|
||||||
|
|
||||||
security.declareProtected("View", "index_dept")
|
|
||||||
|
|
||||||
def index_dept(self, deptfoldername="", REQUEST=None):
|
|
||||||
"""Page d'accueil departement"""
|
|
||||||
authuser = REQUEST.AUTHENTICATED_USER
|
|
||||||
try:
|
|
||||||
dept = getattr(self, deptfoldername)
|
|
||||||
if authuser.has_permission(ScoView, dept):
|
|
||||||
return REQUEST.RESPONSE.redirect("ScoDoc/%s/Scolarite" % deptfoldername)
|
|
||||||
except:
|
|
||||||
log(
|
|
||||||
"*** problem in index_dept (%s) user=%s"
|
|
||||||
% (deptfoldername, str(authuser))
|
|
||||||
)
|
|
||||||
|
|
||||||
H = [
|
|
||||||
self.standard_html_header(REQUEST),
|
|
||||||
"""<div style="margin: 1em;">
|
|
||||||
<h2>Scolarité du département %s</h2>"""
|
|
||||||
% deptfoldername,
|
|
||||||
"""<p>Ce site est
|
|
||||||
<font color="#FF0000"><b>réservé au personnel du département</b></font>.
|
|
||||||
</p>""",
|
|
||||||
self.authentication_form(destination="Scolarite"),
|
|
||||||
"""
|
|
||||||
<p>Pour quitter, <a href="acl_users/logout">logout</a></p>
|
|
||||||
<p><a href="%s">Retour à l'accueil</a></p>
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
% self.ScoDocURL(),
|
|
||||||
self.standard_html_footer(REQUEST),
|
|
||||||
]
|
|
||||||
return "\n".join(H)
|
|
||||||
|
|
||||||
security.declareProtected("View", "doLogin")
|
|
||||||
|
|
||||||
def doLogin(self, REQUEST=None, destination=None):
|
|
||||||
"redirect to destination after login"
|
|
||||||
if destination:
|
|
||||||
return REQUEST.RESPONSE.redirect(destination)
|
|
||||||
|
|
||||||
security.declareProtected("View", "docLogin")
|
|
||||||
docLogin = DTMLFile("dtml/docLogin", globals())
|
|
||||||
security.declareProtected("View", "docLogout")
|
|
||||||
docLogout = DTMLFile("dtml/docLogout", globals())
|
|
||||||
|
|
||||||
security.declareProtected("View", "query_string_to_form_inputs")
|
|
||||||
|
|
||||||
def query_string_to_form_inputs(self, query_string=""):
|
|
||||||
"""Return html snippet representing the query string as POST form hidden inputs.
|
|
||||||
This is useful in conjonction with exUserfolder to correctly redirect the response
|
|
||||||
after authentication.
|
|
||||||
"""
|
|
||||||
H = []
|
|
||||||
for a in query_string.split("&"):
|
|
||||||
if a:
|
|
||||||
nv = a.split("=")
|
|
||||||
if len(nv) == 2:
|
|
||||||
name, value = nv
|
|
||||||
H.append(
|
|
||||||
'<input type="hidden" name="'
|
|
||||||
+ name
|
|
||||||
+ '" value="'
|
|
||||||
+ value
|
|
||||||
+ '"/>'
|
|
||||||
)
|
|
||||||
|
|
||||||
return "<!-- query string -->\n" + "\n".join(H)
|
|
||||||
|
|
||||||
security.declareProtected("View", "standard_html_header")
|
|
||||||
|
|
||||||
def standard_html_header(self, REQUEST=None):
|
|
||||||
"""Standard HTML header for pages outside depts"""
|
|
||||||
# not used in ZScolar, see sco_header
|
|
||||||
return """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
|
||||||
<html><head>
|
|
||||||
<title>ScoDoc: accueil</title>
|
|
||||||
<META http-equiv="Content-Type" content="text/html; charset=%s">
|
|
||||||
<META http-equiv="Content-Style-Type" content="text/css">
|
|
||||||
<META name="LANG" content="fr">
|
|
||||||
<META name="DESCRIPTION" content="ScoDoc: gestion scolarite">
|
|
||||||
|
|
||||||
<link HREF="/ScoDoc/static/css/scodoc.css" rel="stylesheet" type="text/css"/>
|
|
||||||
|
|
||||||
</head><body>%s""" % (
|
|
||||||
scu.SCO_ENCODING,
|
|
||||||
scu.CUSTOM_HTML_HEADER_CNX,
|
|
||||||
)
|
|
||||||
|
|
||||||
security.declareProtected("View", "standard_html_footer")
|
|
||||||
|
|
||||||
def standard_html_footer(self, REQUEST=None):
|
|
||||||
"""Le pied de page HTML de la page d'accueil."""
|
|
||||||
return """<p class="footer">
|
|
||||||
Problème de connexion (identifiant, mot de passe): <em>contacter votre responsable ou chef de département</em>.</p>
|
|
||||||
<p>Problèmes et suggestions sur le logiciel: <a href="mailto:%s">%s</a></p>
|
|
||||||
<p><em>ScoDoc est un logiciel libre développé par Emmanuel Viennet.</em></p>
|
|
||||||
</body></html>""" % (
|
|
||||||
scu.SCO_USERS_LIST,
|
|
||||||
scu.SCO_USERS_LIST,
|
|
||||||
)
|
|
||||||
|
|
||||||
# sendEmail is not used through the web
|
|
||||||
def sendEmail(self, msg):
|
|
||||||
# sends an email to the address using the mailhost, if there is one
|
|
||||||
try:
|
|
||||||
mail_host = self.MailHost
|
|
||||||
except:
|
|
||||||
log("warning: sendEmail: no MailHost found !")
|
|
||||||
return
|
|
||||||
# a failed notification shouldn't cause a Zope error on a site.
|
|
||||||
try:
|
|
||||||
mail_host.send(msg.as_string())
|
|
||||||
log("sendEmail: ok")
|
|
||||||
except Exception as e:
|
|
||||||
log("sendEmail: exception while sending message")
|
|
||||||
log(e)
|
|
||||||
pass
|
|
||||||
|
|
||||||
def sendEmailFromException(self, msg):
|
|
||||||
# Send email by hand, as it seems to be not possible to use Zope Mail Host
|
|
||||||
# from an exception handler (see https://bugs.launchpad.net/zope2/+bug/246748)
|
|
||||||
log("sendEmailFromException")
|
|
||||||
try:
|
|
||||||
p = os.popen("sendmail -t", "w") # old brute force method
|
|
||||||
p.write(msg.as_string())
|
|
||||||
exitcode = p.close()
|
|
||||||
if exitcode:
|
|
||||||
log("sendmail exit code: %s" % exitcode)
|
|
||||||
except:
|
|
||||||
log("an exception occurred sending mail")
|
|
||||||
|
|
||||||
security.declareProtected("View", "standard_error_message")
|
|
||||||
|
|
||||||
def standard_error_message(
|
|
||||||
self,
|
|
||||||
error_value=None,
|
|
||||||
error_message=None, # unused ?
|
|
||||||
error_type=None,
|
|
||||||
error_traceback=None,
|
|
||||||
error_tb=None,
|
|
||||||
**kv
|
|
||||||
):
|
|
||||||
"Recuperation des exceptions Zope"
|
|
||||||
# neat (or should I say dirty ?) hack to get REQUEST
|
|
||||||
# in fact, our caller (probably SimpleItem.py) has the REQUEST variable
|
|
||||||
# that we'd like to use for our logs, but does not pass it as an argument.
|
|
||||||
try:
|
|
||||||
frame = inspect.currentframe()
|
|
||||||
REQUEST = frame.f_back.f_locals["REQUEST"]
|
|
||||||
except:
|
|
||||||
REQUEST = {}
|
|
||||||
|
|
||||||
# Authentication uses exceptions, pass them up
|
|
||||||
HTTP_X_FORWARDED_FOR = REQUEST.get("HTTP_X_FORWARDED_FOR", "")
|
|
||||||
if error_type == "LoginRequired":
|
|
||||||
log("LoginRequired from %s" % HTTP_X_FORWARDED_FOR)
|
|
||||||
self.login_page = error_value
|
|
||||||
return error_value
|
|
||||||
elif error_type == "Unauthorized":
|
|
||||||
log("Unauthorized from %s" % HTTP_X_FORWARDED_FOR)
|
|
||||||
return self.acl_users.docLogin(self, REQUEST=REQUEST)
|
|
||||||
|
|
||||||
log("exception caught: %s" % error_type)
|
|
||||||
log(traceback.format_exc())
|
|
||||||
|
|
||||||
params = {
|
|
||||||
"error_type": error_type,
|
|
||||||
"error_value": error_value,
|
|
||||||
"error_tb": error_tb,
|
|
||||||
"sco_exc_mail": scu.SCO_EXC_MAIL,
|
|
||||||
"sco_dev_mail": scu.SCO_DEV_MAIL,
|
|
||||||
}
|
|
||||||
|
|
||||||
if error_type == "ScoGenError":
|
|
||||||
return "<p>" + str(error_value) + "</p>"
|
|
||||||
elif error_type in ("ScoValueError", "FormatError"):
|
|
||||||
# Not a bug, presents a gentle message to the user:
|
|
||||||
H = [
|
|
||||||
self.standard_html_header(REQUEST),
|
|
||||||
"""<h2>Erreur !</h2><p>%s</p>""" % error_value,
|
|
||||||
]
|
|
||||||
if error_value.dest_url:
|
|
||||||
H.append('<p><a href="%s">Continuer</a></p>' % error_value.dest_url)
|
|
||||||
H.append(self.standard_html_footer(REQUEST))
|
|
||||||
return "\n".join(H)
|
|
||||||
else: # Other exceptions, try carefully to build an error page...
|
|
||||||
# log('exc A')
|
|
||||||
H = []
|
|
||||||
try:
|
|
||||||
H.append(self.standard_html_header(REQUEST))
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
H.append(
|
|
||||||
"""<table border="0" width="100%%"><tr valign="top">
|
|
||||||
<td width="10%%" align="center"></td>
|
|
||||||
<td width="90%%"><h2>Erreur !</h2>
|
|
||||||
<p>Une erreur est survenue</p>
|
|
||||||
<p>
|
|
||||||
<strong>Error Type: %(error_type)s</strong><br>
|
|
||||||
<strong>Error Value: %(error_value)s</strong><br>
|
|
||||||
</p>
|
|
||||||
<hr noshade>
|
|
||||||
<p>L'URL est peut-etre incorrecte ?</p>
|
|
||||||
|
|
||||||
<p>Si l'erreur persiste, contactez Emmanuel Viennet:
|
|
||||||
<a href="mailto:%(sco_dev_mail)s">%(sco_dev_mail)s</a>
|
|
||||||
en copiant ce message d'erreur et le contenu du cadre bleu ci-dessous si possible.
|
|
||||||
</p>
|
|
||||||
</td></tr>
|
|
||||||
</table> """
|
|
||||||
% params
|
|
||||||
)
|
|
||||||
# display error traceback (? may open a security risk via xss attack ?)
|
|
||||||
params["txt_html"] = self._report_request(REQUEST, fmt="html")
|
|
||||||
H.append(
|
|
||||||
"""<h4 class="scodoc">Zope Traceback (à envoyer par mail à <a href="mailto:%(sco_dev_mail)s">%(sco_dev_mail)s</a>)</h4><div style="background-color: rgb(153,153,204); border: 1px;">
|
|
||||||
%(error_tb)s
|
|
||||||
<p><b>Informations:</b><br/>
|
|
||||||
%(txt_html)s
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p>Merci de votre patience !</p>
|
|
||||||
"""
|
|
||||||
% params
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
H.append(self.standard_html_footer(REQUEST))
|
|
||||||
except:
|
|
||||||
log("no footer found for error page")
|
|
||||||
pass
|
|
||||||
|
|
||||||
# --- Mail:
|
|
||||||
params["error_traceback_txt"] = scu.scodoc_html2txt(error_tb)
|
|
||||||
txt = (
|
|
||||||
"""
|
|
||||||
ErrorType: %(error_type)s
|
|
||||||
|
|
||||||
%(error_traceback_txt)s
|
|
||||||
"""
|
|
||||||
% params
|
|
||||||
)
|
|
||||||
|
|
||||||
self.send_debug_alert(txt, REQUEST=REQUEST)
|
|
||||||
# ---
|
|
||||||
log("done processing exception")
|
|
||||||
# log( '\n page=\n' + '\n'.join(H) )
|
|
||||||
return "\n".join(H)
|
|
||||||
|
|
||||||
def _report_request(self, REQUEST, fmt="txt"):
|
|
||||||
"""string describing current request for bug reports"""
|
|
||||||
QUERY_STRING = REQUEST.get("QUERY_STRING", "")
|
|
||||||
if QUERY_STRING:
|
|
||||||
QUERY_STRING = "?" + QUERY_STRING
|
|
||||||
if fmt == "txt":
|
|
||||||
REFERER = REQUEST.get("HTTP_REFERER", "")
|
|
||||||
HTTP_USER_AGENT = REQUEST.get("HTTP_USER_AGENT", "")
|
|
||||||
else:
|
|
||||||
REFERER = "na"
|
|
||||||
HTTP_USER_AGENT = "na"
|
|
||||||
|
|
||||||
params = dict(
|
|
||||||
AUTHENTICATED_USER=REQUEST.get("AUTHENTICATED_USER", ""),
|
|
||||||
dt=time.asctime(),
|
|
||||||
URL=REQUEST.get("URL", ""),
|
|
||||||
QUERY_STRING=QUERY_STRING,
|
|
||||||
METHOD=REQUEST.get("REQUEST_METHOD", ""),
|
|
||||||
REFERER=REFERER,
|
|
||||||
HTTP_USER_AGENT=HTTP_USER_AGENT,
|
|
||||||
form=REQUEST.get("form", ""),
|
|
||||||
HTTP_X_FORWARDED_FOR=REQUEST.get("HTTP_X_FORWARDED_FOR", ""),
|
|
||||||
svn_version=scu.get_svn_version(self.file_path),
|
|
||||||
SCOVERSION=VERSION.SCOVERSION,
|
|
||||||
)
|
|
||||||
txt = (
|
|
||||||
"""
|
|
||||||
Version: %(SCOVERSION)s
|
|
||||||
User: %(AUTHENTICATED_USER)s
|
|
||||||
Date: %(dt)s
|
|
||||||
URL: %(URL)s%(QUERY_STRING)s
|
|
||||||
Method: %(METHOD)s
|
|
||||||
|
|
||||||
REFERER: %(REFERER)s
|
|
||||||
Form: %(form)s
|
|
||||||
Origin: %(HTTP_X_FORWARDED_FOR)s
|
|
||||||
Agent: %(HTTP_USER_AGENT)s
|
|
||||||
"""
|
|
||||||
% params
|
|
||||||
)
|
|
||||||
if fmt == "html":
|
|
||||||
txt = txt.replace("\n", "<br/>")
|
|
||||||
return txt
|
|
||||||
|
|
||||||
security.declareProtected(
|
|
||||||
ScoSuperAdmin, "send_debug_alert"
|
|
||||||
) # not called through the web
|
|
||||||
|
|
||||||
def send_debug_alert(self, txt, REQUEST=None):
|
|
||||||
"""Send an alert email (bug report) to ScoDoc developpers"""
|
|
||||||
if not scu.SCO_EXC_MAIL:
|
|
||||||
log("send_debug_alert: email disabled")
|
|
||||||
return
|
|
||||||
if REQUEST:
|
|
||||||
txt = self._report_request(REQUEST) + txt
|
|
||||||
URL = REQUEST.get("URL", "")
|
|
||||||
else:
|
|
||||||
URL = "send_debug_alert"
|
|
||||||
msg = MIMEMultipart()
|
|
||||||
subj = Header("[scodoc] exc %s" % URL, scu.SCO_ENCODING)
|
|
||||||
msg["Subject"] = subj
|
|
||||||
recipients = [scu.SCO_EXC_MAIL]
|
|
||||||
msg["To"] = " ,".join(recipients)
|
|
||||||
msg["From"] = "scodoc-alert"
|
|
||||||
msg.epilogue = ""
|
|
||||||
msg.attach(MIMEText(txt, "plain", scu.SCO_ENCODING))
|
|
||||||
self.sendEmailFromException(msg)
|
|
||||||
log("Sent mail alert:\n" + txt)
|
|
||||||
|
|
||||||
security.declareProtected("View", "scodoc_admin")
|
|
||||||
|
|
||||||
def scodoc_admin(self, REQUEST=None):
|
|
||||||
"""Page Operations d'administration"""
|
|
||||||
e = self._check_admin_perm(REQUEST)
|
|
||||||
if e:
|
|
||||||
return e
|
|
||||||
|
|
||||||
H = [
|
|
||||||
self.scodoc_top_html_header(REQUEST, page_title="ScoDoc: bienvenue"),
|
|
||||||
"""
|
|
||||||
<h3>Administration ScoDoc</h3>
|
|
||||||
|
|
||||||
<p><a href="change_admin_user_form">changer le mot de passe super-administrateur</a></p>
|
|
||||||
<p><a href="%s">retour à la page d'accueil</a></p>
|
|
||||||
|
|
||||||
<h4 class="scodoc">Création d'un département</h4>
|
|
||||||
<p class="help_important">Le département doit avoir été créé au préalable sur le serveur en utilisant le script
|
|
||||||
<tt>create_dept.sh</tt> (à lancer comme <tt>root</tt> dans le répertoire <tt>config</tt> de ScoDoc).
|
|
||||||
</p>"""
|
|
||||||
% self.absolute_url(),
|
|
||||||
]
|
|
||||||
|
|
||||||
deptList = [x.id for x in self._list_depts()] # definis dans Zope
|
|
||||||
deptIds = set(self._list_depts_ids()) # definis sur le filesystem
|
|
||||||
existingDepts = set(deptList)
|
|
||||||
addableDepts = deptIds - existingDepts
|
|
||||||
|
|
||||||
if not addableDepts:
|
|
||||||
# aucun departement defini: aide utilisateur
|
|
||||||
H.append("<p>Aucun département à ajouter !</p>")
|
|
||||||
else:
|
|
||||||
H.append("""<form action="create_dept"><select name="DeptId"/>""")
|
|
||||||
for deptId in addableDepts:
|
|
||||||
H.append("""<option value="%s">%s</option>""" % (deptId, deptId))
|
|
||||||
H.append(
|
|
||||||
"""</select>
|
|
||||||
<input type="submit" value="Créer département">
|
|
||||||
</form>"""
|
|
||||||
)
|
|
||||||
|
|
||||||
if deptList:
|
|
||||||
H.append(
|
|
||||||
"""
|
|
||||||
<h4 class="scodoc">Suppression d'un département</h4>
|
|
||||||
<p>Ceci permet de supprimer le site web associé à un département, mais n'affecte pas la base de données
|
|
||||||
(le site peut donc être recréé sans perte de données).
|
|
||||||
</p>
|
|
||||||
<form action="delete_dept">
|
|
||||||
<select name="DeptId">
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
for deptFolder in self._list_depts():
|
|
||||||
H.append(
|
|
||||||
'<option value="%s">%s</option>' % (deptFolder.id, deptFolder.id)
|
|
||||||
)
|
|
||||||
H.append(
|
|
||||||
"""</select>
|
|
||||||
<input type="submit" value="Supprimer département">
|
|
||||||
|
|
||||||
</form>"""
|
|
||||||
)
|
|
||||||
|
|
||||||
H.append("""</body></html>""")
|
|
||||||
return "\n".join(H)
|
|
||||||
|
|
||||||
def _list_depts_ids(self):
|
|
||||||
"""Liste de id de departements definis par create_dept.sh
|
|
||||||
(fichiers depts/*.cfg)
|
|
||||||
"""
|
|
||||||
filenames = glob.glob(scu.SCODOC_VAR_DIR + "/config/depts/*.cfg")
|
|
||||||
ids = [os.path.split(os.path.splitext(f)[0])[1] for f in filenames]
|
|
||||||
return ids
|
|
||||||
|
|
||||||
security.declareProtected("View", "http_expiration_date")
|
|
||||||
|
|
||||||
def http_expiration_date(self):
|
|
||||||
"http expiration date for cachable elements (css, ...)"
|
|
||||||
d = datetime.timedelta(minutes=10)
|
|
||||||
return (datetime.datetime.utcnow() + d).strftime("%a, %d %b %Y %H:%M:%S GMT")
|
|
||||||
|
|
||||||
security.declareProtected("View", "get_etud_dept")
|
|
||||||
|
|
||||||
def get_etud_dept(self, REQUEST=None):
|
|
||||||
"""Returns the dept id (eg "GEII") of an etud (identified by etudid, INE or NIP in REQUEST).
|
|
||||||
Warning: This function is inefficient and its result should be cached.
|
|
||||||
"""
|
|
||||||
depts = self._list_depts()
|
|
||||||
depts_etud = [] # liste des depts où l'etud est defini
|
|
||||||
for dept in depts:
|
|
||||||
etuds = dept.Scolarite.getEtudInfo(REQUEST=REQUEST)
|
|
||||||
if etuds:
|
|
||||||
depts_etud.append((dept, etuds))
|
|
||||||
if not depts_etud:
|
|
||||||
return "" # not found
|
|
||||||
elif len(depts_etud) == 1:
|
|
||||||
return depts_etud[0][0].id
|
|
||||||
# inscriptions dans plusieurs departements: cherche la plus recente
|
|
||||||
last_dept = None
|
|
||||||
last_date = None
|
|
||||||
for (dept, etuds) in depts_etud:
|
|
||||||
dept.Scolarite.fillEtudsInfo(etuds)
|
|
||||||
etud = etuds[0]
|
|
||||||
if etud["sems"]:
|
|
||||||
if (not last_date) or (etud["sems"][0]["date_fin_iso"] > last_date):
|
|
||||||
last_date = etud["sems"][0]["date_fin_iso"]
|
|
||||||
last_dept = dept
|
|
||||||
if not last_dept:
|
|
||||||
# est present dans plusieurs semestres mais inscrit dans aucun
|
|
||||||
return depts_etud[0][0]
|
|
||||||
return last_dept.id
|
|
||||||
|
|
||||||
security.declareProtected("View", "table_etud_in_accessible_depts")
|
|
||||||
table_etud_in_accessible_depts = sco_find_etud.table_etud_in_accessible_depts
|
|
||||||
|
|
||||||
security.declareProtected("View", "search_inscr_etud_by_nip")
|
|
||||||
search_inscr_etud_by_nip = sco_find_etud.search_inscr_etud_by_nip
|
|
||||||
|
|
||||||
|
|
||||||
def manage_addZScoDoc(self, id="ScoDoc", title="Site ScoDoc", REQUEST=None):
|
|
||||||
"Add a ZScoDoc instance to a folder."
|
|
||||||
log("============== creating a new ScoDoc instance =============")
|
|
||||||
zscodoc = ZScoDoc(
|
|
||||||
id, title
|
|
||||||
) # ne cree (presque rien), tout se passe lors du 1er accès
|
|
||||||
self._setObject(id, zscodoc)
|
|
||||||
if REQUEST is not None:
|
|
||||||
REQUEST.RESPONSE.redirect("/ScoDoc/manage_workspace")
|
|
||||||
return id
|
|
2779
ZScolar.py
@ -1,5 +0,0 @@
|
|||||||
|
|
||||||
Produits Zope2 anciens et adaptes pour ScoDoc
|
|
||||||
|
|
||||||
E. Viennet 2013
|
|
||||||
|
|
@ -1,372 +0,0 @@
|
|||||||
# ZPsycopgDA/DA.py - ZPsycopgDA Zope product: Database Connection
|
|
||||||
#
|
|
||||||
# Copyright (C) 2004-2010 Federico Di Gregorio <fog@debian.org>
|
|
||||||
#
|
|
||||||
# psycopg2 is free software: you can redistribute it and/or modify it
|
|
||||||
# under the terms of the GNU Lesser General Public License as published
|
|
||||||
# by the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# psycopg2 is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
||||||
# License for more details.
|
|
||||||
|
|
||||||
# Import modules needed by _psycopg to allow tools like py2exe to do
|
|
||||||
# their work without bothering about the module dependencies.
|
|
||||||
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
import db
|
|
||||||
import re
|
|
||||||
|
|
||||||
import Acquisition
|
|
||||||
import Shared.DC.ZRDB.Connection
|
|
||||||
|
|
||||||
from db import DB
|
|
||||||
from Globals import HTMLFile
|
|
||||||
from ExtensionClass import Base
|
|
||||||
from App.Dialogs import MessageDialog
|
|
||||||
from DateTime import DateTime
|
|
||||||
|
|
||||||
# ImageFile is deprecated in Zope >= 2.9
|
|
||||||
try:
|
|
||||||
from App.ImageFile import ImageFile
|
|
||||||
except ImportError:
|
|
||||||
# Zope < 2.9. If PIL's installed with a .pth file, we're probably
|
|
||||||
# hosed.
|
|
||||||
from ImageFile import ImageFile
|
|
||||||
|
|
||||||
# import psycopg and functions/singletons needed for date/time conversions
|
|
||||||
|
|
||||||
import psycopg2
|
|
||||||
from psycopg2 import NUMBER, STRING, ROWID, DATETIME
|
|
||||||
from psycopg2.extensions import INTEGER, LONGINTEGER, FLOAT, BOOLEAN, DATE
|
|
||||||
from psycopg2.extensions import TIME, INTERVAL
|
|
||||||
from psycopg2.extensions import new_type, register_type
|
|
||||||
|
|
||||||
|
|
||||||
# add a new connection to a folder
|
|
||||||
|
|
||||||
manage_addZPsycopgConnectionForm = HTMLFile('dtml/add',globals())
|
|
||||||
|
|
||||||
def manage_addZPsycopgConnection(self, id, title, connection_string,
|
|
||||||
zdatetime=None, tilevel=2,
|
|
||||||
encoding='', check=None, REQUEST=None):
|
|
||||||
"""Add a DB connection to a folder."""
|
|
||||||
self._setObject(id, Connection(id, title, connection_string,
|
|
||||||
zdatetime, check, tilevel, encoding))
|
|
||||||
if REQUEST is not None: return self.manage_main(self, REQUEST)
|
|
||||||
|
|
||||||
|
|
||||||
# the connection object
|
|
||||||
|
|
||||||
class Connection(Shared.DC.ZRDB.Connection.Connection):
|
|
||||||
"""ZPsycopg Connection."""
|
|
||||||
_isAnSQLConnection = 1
|
|
||||||
|
|
||||||
id = 'Psycopg2_database_connection'
|
|
||||||
database_type = 'Psycopg2'
|
|
||||||
meta_type = title = 'Z Psycopg 2 Database Connection'
|
|
||||||
icon = 'misc_/conn'
|
|
||||||
|
|
||||||
def __init__(self, id, title, connection_string,
|
|
||||||
zdatetime, check=None, tilevel=2, encoding='UTF-8'):
|
|
||||||
self.zdatetime = zdatetime
|
|
||||||
self.id = str(id)
|
|
||||||
self.edit(title, connection_string, zdatetime,
|
|
||||||
check=check, tilevel=tilevel, encoding=encoding)
|
|
||||||
|
|
||||||
def factory(self):
|
|
||||||
return DB
|
|
||||||
|
|
||||||
## connection parameters editing ##
|
|
||||||
|
|
||||||
def edit(self, title, connection_string,
|
|
||||||
zdatetime, check=None, tilevel=2, encoding='UTF-8'):
|
|
||||||
self.title = title
|
|
||||||
self.connection_string = connection_string
|
|
||||||
self.zdatetime = zdatetime
|
|
||||||
self.tilevel = tilevel
|
|
||||||
self.encoding = encoding
|
|
||||||
|
|
||||||
if check: self.connect(self.connection_string)
|
|
||||||
|
|
||||||
manage_properties = HTMLFile('dtml/edit', globals())
|
|
||||||
|
|
||||||
def manage_edit(self, title, connection_string,
|
|
||||||
zdatetime=None, check=None, tilevel=2, encoding='UTF-8',
|
|
||||||
REQUEST=None):
|
|
||||||
"""Edit the DB connection."""
|
|
||||||
self.edit(title, connection_string, zdatetime,
|
|
||||||
check=check, tilevel=tilevel, encoding=encoding)
|
|
||||||
if REQUEST is not None:
|
|
||||||
msg = "Connection edited."
|
|
||||||
return self.manage_main(self,REQUEST,manage_tabs_message=msg)
|
|
||||||
|
|
||||||
def connect(self, s):
|
|
||||||
try:
|
|
||||||
self._v_database_connection.close()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# check psycopg version and raise exception if does not match
|
|
||||||
check_psycopg_version(psycopg2.__version__)
|
|
||||||
|
|
||||||
self._v_connected = ''
|
|
||||||
dbf = self.factory()
|
|
||||||
|
|
||||||
# TODO: let the psycopg exception propagate, or not?
|
|
||||||
self._v_database_connection = dbf(
|
|
||||||
self.connection_string, self.tilevel, self.get_type_casts(), self.encoding)
|
|
||||||
self._v_database_connection.open()
|
|
||||||
self._v_connected = DateTime()
|
|
||||||
|
|
||||||
return self
|
|
||||||
|
|
||||||
def get_type_casts(self):
|
|
||||||
# note that in both cases order *is* important
|
|
||||||
if self.zdatetime:
|
|
||||||
return ZDATETIME, ZDATE, ZTIME
|
|
||||||
else:
|
|
||||||
return DATETIME, DATE, TIME
|
|
||||||
|
|
||||||
## browsing and table/column management ##
|
|
||||||
|
|
||||||
manage_options = Shared.DC.ZRDB.Connection.Connection.manage_options
|
|
||||||
# + (
|
|
||||||
# {'label': 'Browse', 'action':'manage_browse'},)
|
|
||||||
|
|
||||||
#manage_tables = HTMLFile('dtml/tables', globals())
|
|
||||||
#manage_browse = HTMLFile('dtml/browse', globals())
|
|
||||||
|
|
||||||
info = None
|
|
||||||
|
|
||||||
def table_info(self):
|
|
||||||
return self._v_database_connection.table_info()
|
|
||||||
|
|
||||||
|
|
||||||
def __getitem__(self, name):
|
|
||||||
if name == 'tableNamed':
|
|
||||||
if not hasattr(self, '_v_tables'): self.tpValues()
|
|
||||||
return self._v_tables.__of__(self)
|
|
||||||
raise KeyError, name
|
|
||||||
|
|
||||||
def tpValues(self):
|
|
||||||
res = []
|
|
||||||
conn = self._v_database_connection
|
|
||||||
for d in conn.tables(rdb=0):
|
|
||||||
try:
|
|
||||||
name = d['TABLE_NAME']
|
|
||||||
b = TableBrowser()
|
|
||||||
b.__name__ = name
|
|
||||||
b._d = d
|
|
||||||
b._c = c
|
|
||||||
try:
|
|
||||||
b.icon = table_icons[d['TABLE_TYPE']]
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
r.append(b)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
return res
|
|
||||||
|
|
||||||
def check_psycopg_version(version):
|
|
||||||
"""
|
|
||||||
Check that the psycopg version used is compatible with the zope adpter.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
m = re.match(r'\d+\.\d+(\.\d+)?', version.split(' ')[0])
|
|
||||||
tver = tuple(map(int, m.group().split('.')))
|
|
||||||
except:
|
|
||||||
raise ImportError("failed to parse psycopg version %s" % version)
|
|
||||||
|
|
||||||
if tver < (2, 4):
|
|
||||||
raise ImportError("psycopg version %s is too old" % version)
|
|
||||||
|
|
||||||
if tver in ((2,4,2), (2,4,3)):
|
|
||||||
raise ImportError("psycopg version %s is known to be buggy" % version)
|
|
||||||
|
|
||||||
|
|
||||||
## database connection registration data ##
|
|
||||||
|
|
||||||
classes = (Connection,)
|
|
||||||
|
|
||||||
meta_types = ({'name':'Z Psycopg 2 Database Connection',
|
|
||||||
'action':'manage_addZPsycopgConnectionForm'},)
|
|
||||||
|
|
||||||
folder_methods = {
|
|
||||||
'manage_addZPsycopgConnection': manage_addZPsycopgConnection,
|
|
||||||
'manage_addZPsycopgConnectionForm': manage_addZPsycopgConnectionForm}
|
|
||||||
|
|
||||||
__ac_permissions__ = (
|
|
||||||
('Add Z Psycopg Database Connections',
|
|
||||||
('manage_addZPsycopgConnectionForm', 'manage_addZPsycopgConnection')),)
|
|
||||||
|
|
||||||
# add icons
|
|
||||||
|
|
||||||
misc_={'conn': ImageFile('icons/DBAdapterFolder_icon.gif', globals())}
|
|
||||||
|
|
||||||
for icon in ('table', 'view', 'stable', 'what', 'field', 'text', 'bin',
|
|
||||||
'int', 'float', 'date', 'time', 'datetime'):
|
|
||||||
misc_[icon] = ImageFile('icons/%s.gif' % icon, globals())
|
|
||||||
|
|
||||||
|
|
||||||
## zope-specific psycopg typecasters ##
|
|
||||||
|
|
||||||
# convert an ISO timestamp string from postgres to a Zope DateTime object
|
|
||||||
def _cast_DateTime(iso, curs):
|
|
||||||
if iso:
|
|
||||||
if iso in ['-infinity', 'infinity']:
|
|
||||||
return iso
|
|
||||||
else:
|
|
||||||
return DateTime(iso)
|
|
||||||
|
|
||||||
# convert an ISO date string from postgres to a Zope DateTime object
|
|
||||||
def _cast_Date(iso, curs):
|
|
||||||
if iso:
|
|
||||||
if iso in ['-infinity', 'infinity']:
|
|
||||||
return iso
|
|
||||||
else:
|
|
||||||
return DateTime(iso)
|
|
||||||
|
|
||||||
# Convert a time string from postgres to a Zope DateTime object.
|
|
||||||
# NOTE: we set the day as today before feeding to DateTime so
|
|
||||||
# that it has the same DST settings.
|
|
||||||
def _cast_Time(iso, curs):
|
|
||||||
if iso:
|
|
||||||
if iso in ['-infinity', 'infinity']:
|
|
||||||
return iso
|
|
||||||
else:
|
|
||||||
return DateTime(time.strftime('%Y-%m-%d %H:%M:%S',
|
|
||||||
time.localtime(time.time())[:3]+
|
|
||||||
time.strptime(iso[:8], "%H:%M:%S")[3:]))
|
|
||||||
|
|
||||||
# NOTE: we don't cast intervals anymore because they are passed
|
|
||||||
# untouched to Zope.
|
|
||||||
def _cast_Interval(iso, curs):
|
|
||||||
return iso
|
|
||||||
|
|
||||||
ZDATETIME = new_type((1184, 1114), "ZDATETIME", _cast_DateTime)
|
|
||||||
ZINTERVAL = new_type((1186,), "ZINTERVAL", _cast_Interval)
|
|
||||||
ZDATE = new_type((1082,), "ZDATE", _cast_Date)
|
|
||||||
ZTIME = new_type((1083,), "ZTIME", _cast_Time)
|
|
||||||
|
|
||||||
|
|
||||||
## table browsing helpers ##
|
|
||||||
|
|
||||||
class TableBrowserCollection(Acquisition.Implicit):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class Browser(Base):
|
|
||||||
def __getattr__(self, name):
|
|
||||||
try:
|
|
||||||
return self._d[name]
|
|
||||||
except KeyError:
|
|
||||||
raise AttributeError, name
|
|
||||||
|
|
||||||
class values:
|
|
||||||
def len(self):
|
|
||||||
return 1
|
|
||||||
|
|
||||||
def __getitem__(self, i):
|
|
||||||
try:
|
|
||||||
return self._d[i]
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
self._d = self._f()
|
|
||||||
return self._d[i]
|
|
||||||
|
|
||||||
class TableBrowser(Browser, Acquisition.Implicit):
|
|
||||||
icon = 'what'
|
|
||||||
Description = check = ''
|
|
||||||
info = HTMLFile('table_info', globals())
|
|
||||||
menu = HTMLFile('table_menu', globals())
|
|
||||||
|
|
||||||
def tpValues(self):
|
|
||||||
v = values()
|
|
||||||
v._f = self.tpValues_
|
|
||||||
return v
|
|
||||||
|
|
||||||
def tpValues_(self):
|
|
||||||
r=[]
|
|
||||||
tname=self.__name__
|
|
||||||
for d in self._c.columns(tname):
|
|
||||||
b=ColumnBrowser()
|
|
||||||
b._d=d
|
|
||||||
try: b.icon=field_icons[d['Type']]
|
|
||||||
except: pass
|
|
||||||
b.TABLE_NAME=tname
|
|
||||||
r.append(b)
|
|
||||||
return r
|
|
||||||
|
|
||||||
def tpId(self): return self._d['TABLE_NAME']
|
|
||||||
def tpURL(self): return "Table/%s" % self._d['TABLE_NAME']
|
|
||||||
def Name(self): return self._d['TABLE_NAME']
|
|
||||||
def Type(self): return self._d['TABLE_TYPE']
|
|
||||||
|
|
||||||
manage_designInput=HTMLFile('designInput',globals())
|
|
||||||
def manage_buildInput(self, id, source, default, REQUEST=None):
|
|
||||||
"Create a database method for an input form"
|
|
||||||
args=[]
|
|
||||||
values=[]
|
|
||||||
names=[]
|
|
||||||
columns=self._columns
|
|
||||||
for i in range(len(source)):
|
|
||||||
s=source[i]
|
|
||||||
if s=='Null': continue
|
|
||||||
c=columns[i]
|
|
||||||
d=default[i]
|
|
||||||
t=c['Type']
|
|
||||||
n=c['Name']
|
|
||||||
names.append(n)
|
|
||||||
if s=='Argument':
|
|
||||||
values.append("<dtml-sqlvar %s type=%s>'" %
|
|
||||||
(n, vartype(t)))
|
|
||||||
a='%s%s' % (n, boboType(t))
|
|
||||||
if d: a="%s=%s" % (a,d)
|
|
||||||
args.append(a)
|
|
||||||
elif s=='Property':
|
|
||||||
values.append("<dtml-sqlvar %s type=%s>'" %
|
|
||||||
(n, vartype(t)))
|
|
||||||
else:
|
|
||||||
if isStringType(t):
|
|
||||||
if find(d,"\'") >= 0: d=join(split(d,"\'"),"''")
|
|
||||||
values.append("'%s'" % d)
|
|
||||||
elif d:
|
|
||||||
values.append(str(d))
|
|
||||||
else:
|
|
||||||
raise ValueError, (
|
|
||||||
'no default was given for <em>%s</em>' % n)
|
|
||||||
|
|
||||||
class ColumnBrowser(Browser):
|
|
||||||
icon='field'
|
|
||||||
|
|
||||||
def check(self):
|
|
||||||
return ('\t<input type=checkbox name="%s.%s">' %
|
|
||||||
(self.TABLE_NAME, self._d['Name']))
|
|
||||||
def tpId(self): return self._d['Name']
|
|
||||||
def tpURL(self): return "Column/%s" % self._d['Name']
|
|
||||||
def Description(self):
|
|
||||||
d=self._d
|
|
||||||
if d['Scale']:
|
|
||||||
return " %(Type)s(%(Precision)s,%(Scale)s) %(Nullable)s" % d
|
|
||||||
else:
|
|
||||||
return " %(Type)s(%(Precision)s) %(Nullable)s" % d
|
|
||||||
|
|
||||||
table_icons={
|
|
||||||
'TABLE': 'table',
|
|
||||||
'VIEW':'view',
|
|
||||||
'SYSTEM_TABLE': 'stable',
|
|
||||||
}
|
|
||||||
|
|
||||||
field_icons={
|
|
||||||
NUMBER.name: 'i',
|
|
||||||
STRING.name: 'text',
|
|
||||||
DATETIME.name: 'date',
|
|
||||||
INTEGER.name: 'int',
|
|
||||||
FLOAT.name: 'float',
|
|
||||||
BOOLEAN.name: 'bin',
|
|
||||||
ROWID.name: 'int'
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
# ZPsycopgDA/__init__.py - ZPsycopgDA Zope product
|
|
||||||
#
|
|
||||||
# Copyright (C) 2004-2010 Federico Di Gregorio <fog@debian.org>
|
|
||||||
#
|
|
||||||
# psycopg2 is free software: you can redistribute it and/or modify it
|
|
||||||
# under the terms of the GNU Lesser General Public License as published
|
|
||||||
# by the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# psycopg2 is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
||||||
# License for more details.
|
|
||||||
|
|
||||||
# Import modules needed by _psycopg to allow tools like py2exe to do
|
|
||||||
# their work without bothering about the module dependencies.
|
|
||||||
|
|
||||||
__doc__ = "ZPsycopg Database Adapter Registration."
|
|
||||||
__version__ = '2.4.6'
|
|
||||||
|
|
||||||
import DA
|
|
||||||
|
|
||||||
def initialize(context):
|
|
||||||
context.registerClass(
|
|
||||||
DA.Connection,
|
|
||||||
permission = 'Add Z Psycopg 2 Database Connections',
|
|
||||||
constructors = (DA.manage_addZPsycopgConnectionForm,
|
|
||||||
DA.manage_addZPsycopgConnection),
|
|
||||||
icon = 'icons/DBAdapterFolder_icon.gif')
|
|
@ -1,209 +0,0 @@
|
|||||||
# ZPsycopgDA/db.py - query execution
|
|
||||||
#
|
|
||||||
# Copyright (C) 2004-2010 Federico Di Gregorio <fog@debian.org>
|
|
||||||
#
|
|
||||||
# psycopg2 is free software: you can redistribute it and/or modify it
|
|
||||||
# under the terms of the GNU Lesser General Public License as published
|
|
||||||
# by the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# psycopg2 is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
||||||
# License for more details.
|
|
||||||
|
|
||||||
# Import modules needed by _psycopg to allow tools like py2exe to do
|
|
||||||
# their work without bothering about the module dependencies.
|
|
||||||
|
|
||||||
from Shared.DC.ZRDB.TM import TM
|
|
||||||
from Shared.DC.ZRDB import dbi_db
|
|
||||||
|
|
||||||
from ZODB.POSException import ConflictError
|
|
||||||
|
|
||||||
import site
|
|
||||||
import pool
|
|
||||||
|
|
||||||
import psycopg2
|
|
||||||
from psycopg2.extensions import INTEGER, LONGINTEGER, FLOAT, BOOLEAN, DATE, TIME
|
|
||||||
from psycopg2.extensions import TransactionRollbackError, register_type
|
|
||||||
from psycopg2 import NUMBER, STRING, ROWID, DATETIME
|
|
||||||
|
|
||||||
|
|
||||||
# the DB object, managing all the real query work
|
|
||||||
|
|
||||||
class DB(TM, dbi_db.DB):
|
|
||||||
|
|
||||||
_p_oid = _p_changed = _registered = None
|
|
||||||
|
|
||||||
def __init__(self, dsn, tilevel, typecasts, enc='utf-8'):
|
|
||||||
self.dsn = dsn
|
|
||||||
self.tilevel = tilevel
|
|
||||||
self.typecasts = typecasts
|
|
||||||
if enc is None or enc == "":
|
|
||||||
self.encoding = "utf-8"
|
|
||||||
else:
|
|
||||||
self.encoding = enc
|
|
||||||
self.failures = 0
|
|
||||||
self.calls = 0
|
|
||||||
self.make_mappings()
|
|
||||||
|
|
||||||
def getconn(self, init=True):
|
|
||||||
# if init is False we are trying to get hold on an already existing
|
|
||||||
# connection, so we avoid to (re)initialize it risking errors.
|
|
||||||
conn = pool.getconn(self.dsn)
|
|
||||||
if init:
|
|
||||||
# use set_session where available as in these versions
|
|
||||||
# set_isolation_level generates an extra query.
|
|
||||||
if psycopg2.__version__ >= '2.4.2':
|
|
||||||
conn.set_session(isolation_level=int(self.tilevel))
|
|
||||||
else:
|
|
||||||
conn.set_isolation_level(int(self.tilevel))
|
|
||||||
conn.set_client_encoding(self.encoding)
|
|
||||||
for tc in self.typecasts:
|
|
||||||
register_type(tc, conn)
|
|
||||||
return conn
|
|
||||||
|
|
||||||
def putconn(self, close=False):
|
|
||||||
try:
|
|
||||||
conn = pool.getconn(self.dsn, False)
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
pool.putconn(self.dsn, conn, close)
|
|
||||||
|
|
||||||
def getcursor(self):
|
|
||||||
conn = self.getconn(False)
|
|
||||||
return conn.cursor()
|
|
||||||
|
|
||||||
def _finish(self, *ignored):
|
|
||||||
try:
|
|
||||||
conn = self.getconn(False)
|
|
||||||
conn.commit()
|
|
||||||
self.putconn()
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def _abort(self, *ignored):
|
|
||||||
try:
|
|
||||||
conn = self.getconn(False)
|
|
||||||
conn.rollback()
|
|
||||||
self.putconn()
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def open(self):
|
|
||||||
# this will create a new pool for our DSN if not already existing,
|
|
||||||
# then get and immediately release a connection
|
|
||||||
self.getconn()
|
|
||||||
self.putconn()
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
# FIXME: if this connection is closed we flush all the pool associated
|
|
||||||
# with the current DSN; does this makes sense?
|
|
||||||
pool.flushpool(self.dsn)
|
|
||||||
|
|
||||||
def sortKey(self):
|
|
||||||
return 1
|
|
||||||
|
|
||||||
def make_mappings(self):
|
|
||||||
"""Generate the mappings used later by self.convert_description()."""
|
|
||||||
self.type_mappings = {}
|
|
||||||
for t, s in [(INTEGER,'i'), (LONGINTEGER, 'i'), (NUMBER, 'n'),
|
|
||||||
(BOOLEAN,'n'), (ROWID, 'i'),
|
|
||||||
(DATETIME, 'd'), (DATE, 'd'), (TIME, 'd')]:
|
|
||||||
for v in t.values:
|
|
||||||
self.type_mappings[v] = (t, s)
|
|
||||||
|
|
||||||
def convert_description(self, desc, use_psycopg_types=False):
|
|
||||||
"""Convert DBAPI-2.0 description field to Zope format."""
|
|
||||||
items = []
|
|
||||||
for name, typ, width, ds, p, scale, null_ok in desc:
|
|
||||||
m = self.type_mappings.get(typ, (STRING, 's'))
|
|
||||||
items.append({
|
|
||||||
'name': name,
|
|
||||||
'type': use_psycopg_types and m[0] or m[1],
|
|
||||||
'width': width,
|
|
||||||
'precision': p,
|
|
||||||
'scale': scale,
|
|
||||||
'null': null_ok,
|
|
||||||
})
|
|
||||||
return items
|
|
||||||
|
|
||||||
## tables and rows ##
|
|
||||||
|
|
||||||
def tables(self, rdb=0, _care=('TABLE', 'VIEW')):
|
|
||||||
self._register()
|
|
||||||
c = self.getcursor()
|
|
||||||
c.execute(
|
|
||||||
"SELECT t.tablename AS NAME, 'TABLE' AS TYPE "
|
|
||||||
" FROM pg_tables t WHERE tableowner <> 'postgres' "
|
|
||||||
"UNION SELECT v.viewname AS NAME, 'VIEW' AS TYPE "
|
|
||||||
" FROM pg_views v WHERE viewowner <> 'postgres' "
|
|
||||||
"UNION SELECT t.tablename AS NAME, 'SYSTEM_TABLE\' AS TYPE "
|
|
||||||
" FROM pg_tables t WHERE tableowner = 'postgres' "
|
|
||||||
"UNION SELECT v.viewname AS NAME, 'SYSTEM_TABLE' AS TYPE "
|
|
||||||
"FROM pg_views v WHERE viewowner = 'postgres'")
|
|
||||||
res = []
|
|
||||||
for name, typ in c.fetchall():
|
|
||||||
if typ in _care:
|
|
||||||
res.append({'TABLE_NAME': name, 'TABLE_TYPE': typ})
|
|
||||||
self.putconn()
|
|
||||||
return res
|
|
||||||
|
|
||||||
def columns(self, table_name):
|
|
||||||
self._register()
|
|
||||||
c = self.getcursor()
|
|
||||||
try:
|
|
||||||
r = c.execute('SELECT * FROM "%s" WHERE 1=0' % table_name)
|
|
||||||
except:
|
|
||||||
return ()
|
|
||||||
self.putconn()
|
|
||||||
return self.convert_description(c.description, True)
|
|
||||||
|
|
||||||
## query execution ##
|
|
||||||
|
|
||||||
def query(self, query_string, max_rows=None, query_data=None):
|
|
||||||
self._register()
|
|
||||||
self.calls = self.calls+1
|
|
||||||
|
|
||||||
desc = ()
|
|
||||||
res = []
|
|
||||||
nselects = 0
|
|
||||||
|
|
||||||
c = self.getcursor()
|
|
||||||
|
|
||||||
try:
|
|
||||||
for qs in [x for x in query_string.split('\0') if x]:
|
|
||||||
try:
|
|
||||||
if query_data:
|
|
||||||
c.execute(qs, query_data)
|
|
||||||
else:
|
|
||||||
c.execute(qs)
|
|
||||||
except TransactionRollbackError:
|
|
||||||
# Ha, here we have to look like we are the ZODB raising conflict errrors, raising ZPublisher.Publish.Retry just doesn't work
|
|
||||||
#logging.debug("Serialization Error, retrying transaction", exc_info=True)
|
|
||||||
raise ConflictError("TransactionRollbackError from psycopg2")
|
|
||||||
except psycopg2.OperationalError:
|
|
||||||
#logging.exception("Operational error on connection, closing it.")
|
|
||||||
try:
|
|
||||||
# Only close our connection
|
|
||||||
self.putconn(True)
|
|
||||||
except:
|
|
||||||
#logging.debug("Something went wrong when we tried to close the pool", exc_info=True)
|
|
||||||
pass
|
|
||||||
if c.description is not None:
|
|
||||||
nselects += 1
|
|
||||||
if c.description != desc and nselects > 1:
|
|
||||||
raise psycopg2.ProgrammingError(
|
|
||||||
'multiple selects in single query not allowed')
|
|
||||||
if max_rows:
|
|
||||||
res = c.fetchmany(max_rows)
|
|
||||||
else:
|
|
||||||
res = c.fetchall()
|
|
||||||
desc = c.description
|
|
||||||
self.failures = 0
|
|
||||||
|
|
||||||
except StandardError, err:
|
|
||||||
self._abort()
|
|
||||||
raise err
|
|
||||||
|
|
||||||
return self.convert_description(desc), res
|
|
@ -1,108 +0,0 @@
|
|||||||
<dtml-var manage_page_header>
|
|
||||||
|
|
||||||
<dtml-var "manage_form_title(this(), _,
|
|
||||||
form_title='Add Z Psycopg 2 Database Connection',
|
|
||||||
help_product='ZPsycopgDA',
|
|
||||||
help_topic='ZPsycopgDA-Method-Add.stx'
|
|
||||||
)">
|
|
||||||
|
|
||||||
<p class="form-help">
|
|
||||||
A Zope Psycopg 2 Database Connection is used to connect and execute
|
|
||||||
queries on a PostgreSQL database.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="form-help">
|
|
||||||
In the form below <em>Connection String</em> (also called the Data Source Name
|
|
||||||
or DSN for short) is a string... (TODO: finish docs)
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<form action="manage_addZPsycopgConnection" method="POST">
|
|
||||||
<table cellspacing="0" cellpadding="2" border="0">
|
|
||||||
<tr>
|
|
||||||
<td align="left" valign="top">
|
|
||||||
<div class="form-label">
|
|
||||||
Id
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td align="left" valign="top">
|
|
||||||
<input type="text" name="id" size="40"
|
|
||||||
value="Psycopg2_database_connection" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="left" valign="top">
|
|
||||||
<div class="form-optional">
|
|
||||||
Title
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td align="left" valign="top">
|
|
||||||
<input type="text" name="title" size="40"
|
|
||||||
value="Z Psycopg 2 Database Connection"/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="left" valign="top">
|
|
||||||
<div class="form-label">
|
|
||||||
Connection string
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td align="left" valign="top">
|
|
||||||
<input type="text" name="connection_string" size="40" value="" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="left" valign="top">
|
|
||||||
<div class="form-label">
|
|
||||||
Connect immediately
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td align="left" valign="top">
|
|
||||||
<input type="checkbox" name="check" value="YES" checked="YES" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="left" valign="top">
|
|
||||||
<div class="form-label">
|
|
||||||
Use Zope's internal DateTime
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td align="left" valign="top">
|
|
||||||
<input type="checkbox" name="zdatetime" value="YES" checked="YES" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="left" valign="top">
|
|
||||||
<div class="form-label">
|
|
||||||
Transaction isolation level
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td align="left" valign="top">
|
|
||||||
<select name="tilevel:int">
|
|
||||||
<option value="4">Read uncommitted</option>
|
|
||||||
<option value="1">Read committed</option>
|
|
||||||
<option value="2" selected="YES">Repeatable read</option>
|
|
||||||
<option value="3">Serializable</option>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="left" valign="top">
|
|
||||||
<div class="form-label">
|
|
||||||
Encoding
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td align="left" valign="top">
|
|
||||||
<input type="text" name="encoding" size="40" value="" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="left" valign="top" colspan="2">
|
|
||||||
<div class="form-element">
|
|
||||||
<input class="form-element" type="submit" name="submit" value=" Add " />
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<dtml-var manage_page_footer>
|
|
@ -1,11 +0,0 @@
|
|||||||
<html>
|
|
||||||
<head><title><dtml-var title_or_id >tables</title></head>
|
|
||||||
<body bgcolor="#FFFFFF" link="#000099" vlink="#555555" alink="#77003B">
|
|
||||||
<dtml-var manage_tabs>
|
|
||||||
<dtml-tree header="info">
|
|
||||||
<IMG SRC="<dtml-var SCRIPT_NAME >/misc_/ZPsycopgDA/<dtml-var icon>"
|
|
||||||
ALT="<dtml-var Type>" BORDER="0">
|
|
||||||
<dtml-var Name><dtml-var Description>
|
|
||||||
</dtml-tree>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,84 +0,0 @@
|
|||||||
<dtml-var manage_page_header>
|
|
||||||
<dtml-var manage_tabs>
|
|
||||||
|
|
||||||
<form action="manage_edit" method="POST">
|
|
||||||
<table cellspacing="0" cellpadding="2" border="0">
|
|
||||||
<tr>
|
|
||||||
<td align="left" valign="top">
|
|
||||||
<div class="form-optional">
|
|
||||||
Title
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td align="left" valign="top">
|
|
||||||
<input type="text" name="title" size="40"
|
|
||||||
value="&dtml-title;"/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="left" valign="top">
|
|
||||||
<div class="form-label">
|
|
||||||
Connection string
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td align="left" valign="top">
|
|
||||||
<input type="text" name="connection_string" size="40"
|
|
||||||
value="&dtml-connection_string;" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="left" valign="top">
|
|
||||||
<div class="form-label">
|
|
||||||
Use Zope's internal DateTime
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td align="left" valign="top">
|
|
||||||
<input type="checkbox" name="zdatetime" value="YES"
|
|
||||||
<dtml-if expr="zdatetime">checked="YES"</dtml-if> />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="left" valign="top">
|
|
||||||
<div class="form-label">
|
|
||||||
Transaction isolation level
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td align="left" valign="top">
|
|
||||||
<select name="tilevel:int">
|
|
||||||
<option value="4"
|
|
||||||
<dtml-if expr="tilevel==4">selected="YES"</dtml-if>>
|
|
||||||
Read uncommitted</option>
|
|
||||||
<option value="1"
|
|
||||||
<dtml-if expr="tilevel==1">selected="YES"</dtml-if>>
|
|
||||||
Read committed</option>
|
|
||||||
<option value="2"
|
|
||||||
<dtml-if expr="tilevel==2">selected="YES"</dtml-if>>
|
|
||||||
Repeatable read</option>
|
|
||||||
<option value="3"
|
|
||||||
<dtml-if expr="tilevel==3">selected="YES"</dtml-if>>
|
|
||||||
Serializable</option>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="left" valign="top">
|
|
||||||
<div class="form-label">
|
|
||||||
Encoding
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td align="left" valign="top">
|
|
||||||
<input type="text" name="encoding" size="40"
|
|
||||||
value="&dtml-encoding;" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="left" valign="top" colspan="2">
|
|
||||||
<div class="form-element">
|
|
||||||
<input class="form-element" type="submit" name="submit"
|
|
||||||
value=" Save Changes " />
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<dtml-var manage_page_footer>
|
|
@ -1,7 +0,0 @@
|
|||||||
<dtml-var standard_html_header>
|
|
||||||
|
|
||||||
<dtml-var TABLE_TYPE><dtml-if TABLE_OWNER>
|
|
||||||
owned by <dtml-var TABLE_OWNER></dtml-if>
|
|
||||||
<dtml-if REMARKS><br><dtml-var REMARKS></dtml-if>
|
|
||||||
|
|
||||||
<dtml-var standard_html_footer>
|
|
Before Width: | Height: | Size: 897 B |
Before Width: | Height: | Size: 924 B |
Before Width: | Height: | Size: 930 B |
Before Width: | Height: | Size: 925 B |
Before Width: | Height: | Size: 915 B |
Before Width: | Height: | Size: 929 B |
Before Width: | Height: | Size: 918 B |
Before Width: | Height: | Size: 884 B |
Before Width: | Height: | Size: 878 B |
Before Width: | Height: | Size: 918 B |
Before Width: | Height: | Size: 926 B |
Before Width: | Height: | Size: 893 B |
Before Width: | Height: | Size: 894 B |
@ -1,193 +0,0 @@
|
|||||||
# ZPsycopgDA/pool.py - ZPsycopgDA Zope product: connection pooling
|
|
||||||
#
|
|
||||||
# Copyright (C) 2004-2010 Federico Di Gregorio <fog@debian.org>
|
|
||||||
#
|
|
||||||
# psycopg2 is free software: you can redistribute it and/or modify it
|
|
||||||
# under the terms of the GNU Lesser General Public License as published
|
|
||||||
# by the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# psycopg2 is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
||||||
# License for more details.
|
|
||||||
|
|
||||||
# Import modules needed by _psycopg to allow tools like py2exe to do
|
|
||||||
# their work without bothering about the module dependencies.
|
|
||||||
|
|
||||||
# All the connections are held in a pool of pools, directly accessible by the
|
|
||||||
# ZPsycopgDA code in db.py.
|
|
||||||
|
|
||||||
import threading
|
|
||||||
import psycopg2
|
|
||||||
from psycopg2.pool import PoolError
|
|
||||||
|
|
||||||
|
|
||||||
class AbstractConnectionPool(object):
|
|
||||||
"""Generic key-based pooling code."""
|
|
||||||
|
|
||||||
def __init__(self, minconn, maxconn, *args, **kwargs):
|
|
||||||
"""Initialize the connection pool.
|
|
||||||
|
|
||||||
New 'minconn' connections are created immediately calling 'connfunc'
|
|
||||||
with given parameters. The connection pool will support a maximum of
|
|
||||||
about 'maxconn' connections.
|
|
||||||
"""
|
|
||||||
self.minconn = minconn
|
|
||||||
self.maxconn = maxconn
|
|
||||||
self.closed = False
|
|
||||||
|
|
||||||
self._args = args
|
|
||||||
self._kwargs = kwargs
|
|
||||||
|
|
||||||
self._pool = []
|
|
||||||
self._used = {}
|
|
||||||
self._rused = {} # id(conn) -> key map
|
|
||||||
self._keys = 0
|
|
||||||
|
|
||||||
for i in range(self.minconn):
|
|
||||||
self._connect()
|
|
||||||
|
|
||||||
def _connect(self, key=None):
|
|
||||||
"""Create a new connection and assign it to 'key' if not None."""
|
|
||||||
conn = psycopg2.connect(*self._args, **self._kwargs)
|
|
||||||
if key is not None:
|
|
||||||
self._used[key] = conn
|
|
||||||
self._rused[id(conn)] = key
|
|
||||||
else:
|
|
||||||
self._pool.append(conn)
|
|
||||||
return conn
|
|
||||||
|
|
||||||
def _getkey(self):
|
|
||||||
"""Return a new unique key."""
|
|
||||||
self._keys += 1
|
|
||||||
return self._keys
|
|
||||||
|
|
||||||
def _getconn(self, key=None):
|
|
||||||
"""Get a free connection and assign it to 'key' if not None."""
|
|
||||||
if self.closed: raise PoolError("connection pool is closed")
|
|
||||||
if key is None: key = self._getkey()
|
|
||||||
|
|
||||||
if key in self._used:
|
|
||||||
return self._used[key]
|
|
||||||
|
|
||||||
if self._pool:
|
|
||||||
self._used[key] = conn = self._pool.pop()
|
|
||||||
self._rused[id(conn)] = key
|
|
||||||
return conn
|
|
||||||
else:
|
|
||||||
if len(self._used) == self.maxconn:
|
|
||||||
raise PoolError("connection pool exausted")
|
|
||||||
return self._connect(key)
|
|
||||||
|
|
||||||
def _putconn(self, conn, key=None, close=False):
|
|
||||||
"""Put away a connection."""
|
|
||||||
if self.closed: raise PoolError("connection pool is closed")
|
|
||||||
if key is None: key = self._rused[id(conn)]
|
|
||||||
|
|
||||||
if not key:
|
|
||||||
raise PoolError("trying to put unkeyed connection")
|
|
||||||
|
|
||||||
if len(self._pool) < self.minconn and not close:
|
|
||||||
self._pool.append(conn)
|
|
||||||
else:
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
# here we check for the presence of key because it can happen that a
|
|
||||||
# thread tries to put back a connection after a call to close
|
|
||||||
if not self.closed or key in self._used:
|
|
||||||
del self._used[key]
|
|
||||||
del self._rused[id(conn)]
|
|
||||||
|
|
||||||
def _closeall(self):
|
|
||||||
"""Close all connections.
|
|
||||||
|
|
||||||
Note that this can lead to some code fail badly when trying to use
|
|
||||||
an already closed connection. If you call .closeall() make sure
|
|
||||||
your code can deal with it.
|
|
||||||
"""
|
|
||||||
if self.closed: raise PoolError("connection pool is closed")
|
|
||||||
for conn in self._pool + list(self._used.values()):
|
|
||||||
try:
|
|
||||||
conn.close()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
self.closed = True
|
|
||||||
|
|
||||||
|
|
||||||
class PersistentConnectionPool(AbstractConnectionPool):
|
|
||||||
"""A pool that assigns persistent connections to different threads.
|
|
||||||
|
|
||||||
Note that this connection pool generates by itself the required keys
|
|
||||||
using the current thread id. This means that until a thread puts away
|
|
||||||
a connection it will always get the same connection object by successive
|
|
||||||
`!getconn()` calls. This also means that a thread can't use more than one
|
|
||||||
single connection from the pool.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, minconn, maxconn, *args, **kwargs):
|
|
||||||
"""Initialize the threading lock."""
|
|
||||||
import threading
|
|
||||||
AbstractConnectionPool.__init__(
|
|
||||||
self, minconn, maxconn, *args, **kwargs)
|
|
||||||
self._lock = threading.Lock()
|
|
||||||
|
|
||||||
# we we'll need the thread module, to determine thread ids, so we
|
|
||||||
# import it here and copy it in an instance variable
|
|
||||||
import thread
|
|
||||||
self.__thread = thread
|
|
||||||
|
|
||||||
def getconn(self):
|
|
||||||
"""Generate thread id and return a connection."""
|
|
||||||
key = self.__thread.get_ident()
|
|
||||||
self._lock.acquire()
|
|
||||||
try:
|
|
||||||
return self._getconn(key)
|
|
||||||
finally:
|
|
||||||
self._lock.release()
|
|
||||||
|
|
||||||
def putconn(self, conn=None, close=False):
|
|
||||||
"""Put away an unused connection."""
|
|
||||||
key = self.__thread.get_ident()
|
|
||||||
self._lock.acquire()
|
|
||||||
try:
|
|
||||||
if not conn: conn = self._used[key]
|
|
||||||
self._putconn(conn, key, close)
|
|
||||||
finally:
|
|
||||||
self._lock.release()
|
|
||||||
|
|
||||||
def closeall(self):
|
|
||||||
"""Close all connections (even the one currently in use.)"""
|
|
||||||
self._lock.acquire()
|
|
||||||
try:
|
|
||||||
self._closeall()
|
|
||||||
finally:
|
|
||||||
self._lock.release()
|
|
||||||
|
|
||||||
|
|
||||||
_connections_pool = {}
|
|
||||||
_connections_lock = threading.Lock()
|
|
||||||
|
|
||||||
def getpool(dsn, create=True):
|
|
||||||
_connections_lock.acquire()
|
|
||||||
try:
|
|
||||||
if not _connections_pool.has_key(dsn) and create:
|
|
||||||
_connections_pool[dsn] = \
|
|
||||||
PersistentConnectionPool(4, 200, dsn)
|
|
||||||
finally:
|
|
||||||
_connections_lock.release()
|
|
||||||
return _connections_pool[dsn]
|
|
||||||
|
|
||||||
def flushpool(dsn):
|
|
||||||
_connections_lock.acquire()
|
|
||||||
try:
|
|
||||||
_connections_pool[dsn].closeall()
|
|
||||||
del _connections_pool[dsn]
|
|
||||||
finally:
|
|
||||||
_connections_lock.release()
|
|
||||||
|
|
||||||
def getconn(dsn, create=True):
|
|
||||||
return getpool(dsn, create=create).getconn()
|
|
||||||
|
|
||||||
def putconn(dsn, conn, close=False):
|
|
||||||
getpool(dsn).putconn(conn, close=close)
|
|
@ -1,47 +0,0 @@
|
|||||||
#
|
|
||||||
# Extensible User Folder
|
|
||||||
#
|
|
||||||
# (C) Copyright 2000-2004 The Internet (Aust) Pty Ltd
|
|
||||||
# ACN: 082 081 472 ABN: 83 082 081 472
|
|
||||||
# All Rights Reserved
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
||||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
# SUCH DAMAGE.
|
|
||||||
#
|
|
||||||
# Author: Andrew Milton <akm@theinternet.com.au>
|
|
||||||
# $Id: __init__.py,v 1.1 2004/11/10 14:15:34 akm Exp $
|
|
||||||
|
|
||||||
#import etcAuthSource
|
|
||||||
#import httpsAuthSource
|
|
||||||
#import mysqlAuthSource
|
|
||||||
import pgAuthSource
|
|
||||||
#import pgAuthSourceAlt
|
|
||||||
#import radiusAuthSource
|
|
||||||
#import smbAuthSource
|
|
||||||
#import usAuthSource
|
|
||||||
#import zodbAuthSource
|
|
||||||
#import zodbBTreeAuthSource
|
|
||||||
|
|
||||||
#
|
|
||||||
# These have special requirements for external libraries
|
|
||||||
# that my not be present.
|
|
||||||
#
|
|
||||||
|
|
||||||
# try:
|
|
||||||
# import nisAuthSource
|
|
||||||
# except:
|
|
||||||
# pass
|
|
||||||
|
|
||||||
# try:
|
|
||||||
# import LDAPAuthSource
|
|
||||||
# except:
|
|
||||||
# pass
|
|
@ -1,4 +0,0 @@
|
|||||||
*.pyc
|
|
||||||
*.pyo
|
|
||||||
*~
|
|
||||||
.*.swp
|
|
@ -1,2 +0,0 @@
|
|||||||
# $Id: __init__.py,v 1.1 2004/11/10 14:15:36 akm Exp $
|
|
||||||
import pgAuthSource
|
|
@ -1,40 +0,0 @@
|
|||||||
<dtml-var "DialogHeader(_.None,_,DialogTitle='Add Postgresql Authentication Source')">
|
|
||||||
<FORM ACTION="&dtml-URL;" METHOD="POST">
|
|
||||||
<dtml-in "REQUEST.form.keys()">
|
|
||||||
<input type="HIDDEN" name="<dtml-var sequence-item>" value="<dtml-var "REQUEST[_.getitem('sequence-item',0)]">">
|
|
||||||
</dtml-in>
|
|
||||||
<input type="HIDDEN" name="doProp" value="1">
|
|
||||||
<TABLE CELLSPACING="2">
|
|
||||||
<tr><th><dtml-babel src="'en'">Database Connection</dtml-babel>:</th>
|
|
||||||
<td>
|
|
||||||
<select name="pgauth_connection">
|
|
||||||
<dtml-in "SQLConnectionIDs()">
|
|
||||||
<option value="<dtml-var sequence-item>">
|
|
||||||
<dtml-var sequence-key></option>
|
|
||||||
</dtml-in>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th><dtml-babel src="'en'">Table Name</dtml-babel>:</th>
|
|
||||||
<td><input type="text" name="pgauth_table" value="passwd"></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th><dtml-babel src="'en'">Username Column</dtml-babel>:</th>
|
|
||||||
<td><input type="text" name="pgauth_usernameColumn" value="username"></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th><dtml-babel src="'en'">Password Column</dtml-babel>:</th>
|
|
||||||
<td><input type="text" name="pgauth_passwordColumn" value="password"></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th><dtml-babel src="'en'">Roles Column</dtml-babel>:</th>
|
|
||||||
<td><input type="text" name="pgauth_rolesColumn" value="roles"></td>
|
|
||||||
</tr>
|
|
||||||
<TR>
|
|
||||||
<TD></TD>
|
|
||||||
<TD><BR><INPUT TYPE="SUBMIT" VALUE="<dtml-babel src="'en'">Add</dtml-babel>"></TD>
|
|
||||||
</TR>
|
|
||||||
</TABLE>
|
|
||||||
</FORM>
|
|
||||||
<dtml-var DialogFooter>
|
|
@ -1,37 +0,0 @@
|
|||||||
<dtml-var "DialogHeader(_.None,_,DialogTitle='Postgresql Authentication Source',dialog_width='100%')">
|
|
||||||
<dtml-var manage_tabs>
|
|
||||||
<FORM ACTION="manage_editAuthSource" METHOD="POST">
|
|
||||||
<TABLE CELLSPACING="2">
|
|
||||||
<tr><th><dtml-babel src="'en'">Database Connection</dtml-babel>:</th>
|
|
||||||
<td>
|
|
||||||
<select name="pgauth_connection">
|
|
||||||
<dtml-in "SQLConnectionIDs()">
|
|
||||||
<option value="<dtml-var sequence-item>"<dtml-if "currentAuthSource.connection==_['sequence-item']"> SELECTED</dtml-if>>
|
|
||||||
<dtml-var sequence-key></option>
|
|
||||||
</dtml-in>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th><dtml-babel src="'en'">Table Name</dtml-babel>:</th>
|
|
||||||
<td><input type="text" name="pgauth_table" value="<dtml-var "currentAuthSource.table">"></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th><dtml-babel src="'en'">Username Column</dtml-babel>:</th>
|
|
||||||
<td><input type="text" name="pgauth_usernameColumn" value="<dtml-var "currentAuthSource.usernameColumn">"></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th><dtml-babel src="'en'">Password Column</dtml-babel>:</th>
|
|
||||||
<td><input type="text" name="pgauth_passwordColumn" value="<dtml-var "currentAuthSource.passwordColumn">"></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th><dtml-babel src="'en'">Roles Column</dtml-babel>:</th>
|
|
||||||
<td><input type="text" name="pgauth_rolesColumn" value="<dtml-var "currentAuthSource.rolesColumn">"></td>
|
|
||||||
</tr>
|
|
||||||
<TR>
|
|
||||||
<TD></TD>
|
|
||||||
<TD><BR><INPUT TYPE="SUBMIT" VALUE=" <dtml-babel src="'en'">Edit</dtml-babel> "></TD>
|
|
||||||
</TR>
|
|
||||||
</TABLE>
|
|
||||||
</FORM>
|
|
||||||
<dtml-var DialogFooter>
|
|
@ -1,333 +0,0 @@
|
|||||||
#
|
|
||||||
# Extensible User Folder
|
|
||||||
#
|
|
||||||
# Postgres Authentication Source for exUserFolder
|
|
||||||
#
|
|
||||||
# (C) Copyright 2000,2001 The Internet (Aust) Pty Ltd
|
|
||||||
# ACN: 082 081 472 ABN: 83 082 081 472
|
|
||||||
# All Rights Reserved
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
||||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
# SUCH DAMAGE.
|
|
||||||
#
|
|
||||||
# Author: Andrew Milton <akm@theinternet.com.au>
|
|
||||||
# $Id: pgAuthSource.py,v 1.1 2004/11/10 14:15:36 akm Exp $
|
|
||||||
|
|
||||||
#
|
|
||||||
# This class only authenticates users, it stores no properties.
|
|
||||||
#
|
|
||||||
|
|
||||||
import string,Acquisition
|
|
||||||
|
|
||||||
from Globals import HTMLFile, MessageDialog, INSTANCE_HOME
|
|
||||||
|
|
||||||
from OFS.Folder import Folder
|
|
||||||
|
|
||||||
from Products.ZSQLMethods.SQL import SQL
|
|
||||||
|
|
||||||
from Products.exUserFolder.exUserFolder import exUserFolder
|
|
||||||
from Products.exUserFolder.Plugins import PluginRegister
|
|
||||||
|
|
||||||
try:
|
|
||||||
from crypt import crypt
|
|
||||||
except:
|
|
||||||
from Products.exUserFolder.fcrypt.fcrypt import crypt
|
|
||||||
|
|
||||||
# debug XXX
|
|
||||||
# def xLOG(msg):
|
|
||||||
# f = open('/tmp/debug.log','a')
|
|
||||||
# f.write(msg+'\n')
|
|
||||||
# f.close()
|
|
||||||
|
|
||||||
def manage_addpgAuthSource(self, REQUEST):
|
|
||||||
""" Add a Postgres Auth Source """
|
|
||||||
|
|
||||||
connection=REQUEST['pgauth_connection']
|
|
||||||
table=REQUEST['pgauth_table']
|
|
||||||
usernameColumn=REQUEST['pgauth_usernameColumn']
|
|
||||||
passwordColumn=REQUEST['pgauth_passwordColumn']
|
|
||||||
rolesColumn=REQUEST['pgauth_rolesColumn']
|
|
||||||
o = pgAuthSource(connection, table, usernameColumn, passwordColumn,
|
|
||||||
rolesColumn)
|
|
||||||
self._setObject('pgAuthSource', o, None, None, 0)
|
|
||||||
o=getattr(self,'pgAuthSource')
|
|
||||||
if hasattr(o, 'postInitialisation'):
|
|
||||||
o.postInitialisation(REQUEST)
|
|
||||||
|
|
||||||
self.currentAuthSource=o
|
|
||||||
return ''
|
|
||||||
|
|
||||||
manage_addpgAuthSourceForm=HTMLFile('manage_addpgAuthSourceForm', globals())
|
|
||||||
manage_editpgAuthSourceForm=HTMLFile('manage_editpgAuthSourceForm', globals())
|
|
||||||
|
|
||||||
class pgAuthSource(Folder):
|
|
||||||
""" Authenticate Users against a Postgres Database """
|
|
||||||
|
|
||||||
meta_type='Authentication Source'
|
|
||||||
title='Postgresql Authentication'
|
|
||||||
|
|
||||||
icon ='misc_/exUserFolder/exUserFolderPlugin.gif'
|
|
||||||
|
|
||||||
manage_tabs=Acquisition.Acquired
|
|
||||||
|
|
||||||
manage_editForm=manage_editpgAuthSourceForm
|
|
||||||
|
|
||||||
#
|
|
||||||
# You can define this to go off and do the authentication instead of
|
|
||||||
# using the basic one inside the User Object
|
|
||||||
#
|
|
||||||
remoteAuthMethod=None
|
|
||||||
|
|
||||||
def __init__(self, connection, table, usernameColumn, passwordColumn,
|
|
||||||
rolesColumn):
|
|
||||||
self.id='pgAuthSource'
|
|
||||||
self.connection=connection
|
|
||||||
self.table=table
|
|
||||||
self.usernameColumn=usernameColumn
|
|
||||||
self.passwordColumn=passwordColumn
|
|
||||||
self.rolesColumn=rolesColumn
|
|
||||||
self.addSQLQueries()
|
|
||||||
|
|
||||||
def manage_editAuthSource(self, REQUEST):
|
|
||||||
""" Edit a Postgres Auth Source """
|
|
||||||
|
|
||||||
self.connection=REQUEST['pgauth_connection']
|
|
||||||
self.table=REQUEST['pgauth_table']
|
|
||||||
self.usernameColumn=REQUEST['pgauth_usernameColumn']
|
|
||||||
self.passwordColumn=REQUEST['pgauth_passwordColumn']
|
|
||||||
self.rolesColumn=REQUEST['pgauth_rolesColumn']
|
|
||||||
self.delSQLQueries()
|
|
||||||
self.addSQLQueries() # Re-add queries with new parameters
|
|
||||||
|
|
||||||
def createUser(self, username, password, roles):
|
|
||||||
""" Add A Username """
|
|
||||||
|
|
||||||
if type(roles) != type([]):
|
|
||||||
if roles:
|
|
||||||
roles=list(roles)
|
|
||||||
else:
|
|
||||||
roles=[]
|
|
||||||
|
|
||||||
rolestring=''
|
|
||||||
for role in roles:
|
|
||||||
rolestring=rolestring+role+','
|
|
||||||
|
|
||||||
rolestring=rolestring[:-1]
|
|
||||||
secret=self.cryptPassword(username, password)
|
|
||||||
self.sqlInsertUser(username=username,
|
|
||||||
password=secret,
|
|
||||||
roles=rolestring)
|
|
||||||
self._v_lastUser={}
|
|
||||||
|
|
||||||
def updateUser(self, username, password, roles):
|
|
||||||
if type(roles) != type([]):
|
|
||||||
if roles:
|
|
||||||
roles=list(roles)
|
|
||||||
else:
|
|
||||||
roles=[]
|
|
||||||
|
|
||||||
rolestring=''
|
|
||||||
for role in roles:
|
|
||||||
print role
|
|
||||||
rolestring=rolestring+role+','
|
|
||||||
|
|
||||||
rolestring=rolestring[:-1]
|
|
||||||
|
|
||||||
# Don't change passwords if it's null
|
|
||||||
if password:
|
|
||||||
secret=self.cryptPassword(username, password)
|
|
||||||
self.sqlUpdateUserPassword(username=username,
|
|
||||||
password=secret)
|
|
||||||
|
|
||||||
self.sqlUpdateUser(username=username,
|
|
||||||
roles=rolestring)
|
|
||||||
self._v_lastUser={}
|
|
||||||
|
|
||||||
def delSQLQueries(self):
|
|
||||||
sqllist=self.objectIds('Z SQL Method')
|
|
||||||
self.manage_delObjects(ids=sqllist)
|
|
||||||
|
|
||||||
def addSQLQueries(self):
|
|
||||||
sqlListUsers=SQL(
|
|
||||||
'sqlListUsers',
|
|
||||||
'List All Users',
|
|
||||||
self.connection,
|
|
||||||
'table=%s'%(self.table),
|
|
||||||
_sqlListUsers)
|
|
||||||
|
|
||||||
self._setObject('sqlListUsers', sqlListUsers)
|
|
||||||
|
|
||||||
sqlListOneUser=SQL(
|
|
||||||
'sqlListOneUser',
|
|
||||||
'List ONE User',
|
|
||||||
self.connection,
|
|
||||||
'table=%s usernameColumn=%s username:string'%(
|
|
||||||
self.table, self.usernameColumn),
|
|
||||||
_sqlListOneUser)
|
|
||||||
|
|
||||||
self._setObject('sqlListOneUser', sqlListOneUser)
|
|
||||||
|
|
||||||
sqlDeleteOneUser=SQL(
|
|
||||||
'sqlDeleteOneUser',
|
|
||||||
'Delete One User',
|
|
||||||
self.connection,
|
|
||||||
'table=%s usernameColumn=%s username:string'%(
|
|
||||||
self.table,self.usernameColumn),
|
|
||||||
_sqlDeleteOneUser)
|
|
||||||
|
|
||||||
self._setObject('sqlDeleteOneUser', sqlDeleteOneUser)
|
|
||||||
|
|
||||||
sqlInsertUser=SQL(
|
|
||||||
'sqlInsertUser',
|
|
||||||
'Insert One User',
|
|
||||||
self.connection,
|
|
||||||
'table=%s usernameColumn=%s passwordColumn=%s rolesColumn=%s username:string password:string roles:string'%(
|
|
||||||
self.table, self.usernameColumn, self.passwordColumn, self.rolesColumn),
|
|
||||||
_sqlInsertUser)
|
|
||||||
|
|
||||||
self._setObject('sqlInsertUser', sqlInsertUser)
|
|
||||||
|
|
||||||
sqlUpdateUser=SQL(
|
|
||||||
'sqlUpdateUser',
|
|
||||||
'Update User',
|
|
||||||
self.connection,
|
|
||||||
'table=%s rolesColumn=%s username:string roles:string'%(self.table, self.rolesColumn),
|
|
||||||
_sqlUpdateUser)
|
|
||||||
|
|
||||||
self._setObject('sqlUpdateUser', sqlUpdateUser)
|
|
||||||
|
|
||||||
sqlUpdateUserPassword=SQL(
|
|
||||||
'sqlUpdateUserPassword',
|
|
||||||
'Update just the password',
|
|
||||||
self.connection,
|
|
||||||
'table=%s usernameColumn=%s passwordColumn=%s username:string password:string'%(self.table, self.usernameColumn, self.passwordColumn),
|
|
||||||
_sqlUpdateUserPassword)
|
|
||||||
|
|
||||||
self._setObject('sqlUpdateUserPassword', sqlUpdateUserPassword)
|
|
||||||
|
|
||||||
def cryptPassword_old(self, username, password):
|
|
||||||
salt =username[:2]
|
|
||||||
secret = crypt(password, salt)
|
|
||||||
return secret
|
|
||||||
|
|
||||||
def deleteUsers(self, userids):
|
|
||||||
for uid in userids:
|
|
||||||
self.sqlDeleteOneUser(username=uid)
|
|
||||||
self._v_lastUser={}
|
|
||||||
|
|
||||||
def listUserNames(self):
|
|
||||||
"""Returns a real list of user names """
|
|
||||||
users = []
|
|
||||||
result=self.sqlListUsers()
|
|
||||||
for n in result:
|
|
||||||
username=sqlattr(n,self.usernameColumn)
|
|
||||||
users.append(username)
|
|
||||||
return users
|
|
||||||
|
|
||||||
def listUsers(self):
|
|
||||||
"""Returns a list of user names or [] if no users exist"""
|
|
||||||
users = []
|
|
||||||
result=self.sqlListUsers()
|
|
||||||
for n in result:
|
|
||||||
roles=[]
|
|
||||||
username=sqlattr(n,self.usernameColumn)
|
|
||||||
if sqlattr(n, self.rolesColumn):
|
|
||||||
roles=string.split(sqlattr(n,self.rolesColumn),',')
|
|
||||||
password=sqlattr(n, self.passwordColumn)
|
|
||||||
N={'username':username, 'password':password, 'roles':roles}
|
|
||||||
users.append(N)
|
|
||||||
return users
|
|
||||||
|
|
||||||
def listOneUser(self,username):
|
|
||||||
#xLOG('pg.listOneUser(%s)' % username)
|
|
||||||
if getattr(self, '_v_lastUser', {}):
|
|
||||||
if self._v_lastUser['username']==username:
|
|
||||||
return self._v_lastUser['users']
|
|
||||||
#xLOG('pg.listOneUser continuing')
|
|
||||||
users = []
|
|
||||||
result=self.sqlListOneUser(username=username)
|
|
||||||
#xLOG('pg.listOneUser result=%s' % result)
|
|
||||||
for n in result:
|
|
||||||
roles=[]
|
|
||||||
username=sqlattr(n,self.usernameColumn)
|
|
||||||
password=sqlattr(n,self.passwordColumn)
|
|
||||||
if sqlattr(n, self.rolesColumn):
|
|
||||||
roles=string.split(sqlattr(n,self.rolesColumn),',') #Andreas
|
|
||||||
N={'username':username, 'password':password, 'roles':roles}
|
|
||||||
users.append(N)
|
|
||||||
self._v_lastUser={}
|
|
||||||
self._v_lastUser['username']=username
|
|
||||||
self._v_lastUser['users']=users
|
|
||||||
return users
|
|
||||||
|
|
||||||
def postInitialisation(self, REQUEST):
|
|
||||||
self._v_lastUser={}
|
|
||||||
|
|
||||||
pgAuthReg=PluginRegister('pgAuthSource', 'Postgresql Authentication Source',
|
|
||||||
pgAuthSource, manage_addpgAuthSourceForm,
|
|
||||||
manage_addpgAuthSource,
|
|
||||||
manage_editpgAuthSourceForm)
|
|
||||||
exUserFolder.authSources['pgAuthSource']=pgAuthReg
|
|
||||||
|
|
||||||
from string import upper, lower
|
|
||||||
import Missing
|
|
||||||
mt=type(Missing.Value)
|
|
||||||
|
|
||||||
def typeconv(val):
|
|
||||||
if type(val)==mt:
|
|
||||||
return ''
|
|
||||||
return val
|
|
||||||
|
|
||||||
def sqlattr(ob, attr):
|
|
||||||
name=attr
|
|
||||||
if hasattr(ob, attr):
|
|
||||||
return typeconv(getattr(ob, attr))
|
|
||||||
attr=upper(attr)
|
|
||||||
if hasattr(ob, attr):
|
|
||||||
return typeconv(getattr(ob, attr))
|
|
||||||
attr=lower(attr)
|
|
||||||
if hasattr(ob, attr):
|
|
||||||
return typeconv(getattr(ob, attr))
|
|
||||||
raise NameError, name
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_sqlListUsers="""
|
|
||||||
SELECT * FROM <dtml-var table>
|
|
||||||
"""
|
|
||||||
|
|
||||||
_sqlListOneUser="""
|
|
||||||
SELECT * FROM <dtml-var table>
|
|
||||||
where <dtml-var usernameColumn>=<dtml-sqlvar username type=string>
|
|
||||||
"""
|
|
||||||
|
|
||||||
_sqlDeleteOneUser="""
|
|
||||||
DELETE FROM <dtml-var table>
|
|
||||||
where <dtml-var usernameColumn>=<dtml-sqlvar username type=string>
|
|
||||||
"""
|
|
||||||
|
|
||||||
_sqlInsertUser="""
|
|
||||||
INSERT INTO <dtml-var table> (<dtml-var usernameColumn>, <dtml-var passwordColumn>, <dtml-var rolesColumn>)
|
|
||||||
VALUES (<dtml-sqlvar username type=string>,
|
|
||||||
<dtml-sqlvar password type=string>,
|
|
||||||
<dtml-sqlvar roles type=string>)
|
|
||||||
"""
|
|
||||||
|
|
||||||
_sqlUpdateUserPassword="""
|
|
||||||
UPDATE <dtml-var table> set <dtml-var passwordColumn>=<dtml-sqlvar password type=string>
|
|
||||||
WHERE <dtml-var usernameColumn>=<dtml-sqlvar username type=string>
|
|
||||||
"""
|
|
||||||
|
|
||||||
_sqlUpdateUser="""
|
|
||||||
UPDATE <dtml-var table> set <dtml-var rolesColumn>=<dtml-sqlvar roles type=string>
|
|
||||||
WHERE <dtml-var usernameColumn>=<dtml-sqlvar username type=string>
|
|
||||||
"""
|
|
@ -1,865 +0,0 @@
|
|||||||
Changes for 0.50.1
|
|
||||||
|
|
||||||
Add a README.Upgrading file to explain the impact of the 0.50.0 source
|
|
||||||
restructure, since people don't seem to be reading this file. --akm
|
|
||||||
|
|
||||||
Fix the default docLogin to use &dtml-URL as the default destination.
|
|
||||||
I porked the fcrypt import. It obviously doesn't get imported here since
|
|
||||||
I have a crypt module installed. -- akm
|
|
||||||
|
|
||||||
Fixed; https://sourceforge.net/tracker/?func=detail&aid=1084903&group_id=36318&atid=416446
|
|
||||||
thanks to vigine -- akm
|
|
||||||
|
|
||||||
Changes for 0.50.0
|
|
||||||
|
|
||||||
Restructured Source Tree. This will make this version incompatible with
|
|
||||||
previous versions, as the classes have moved. This breaks upgrading existing
|
|
||||||
installs unless you keep the old classes around. If you only use external
|
|
||||||
Auth/Prop/Group sources, you will probably be unaffected.
|
|
||||||
|
|
||||||
o Auth Sources moved to single directory
|
|
||||||
o Prop Sources moved to single directory
|
|
||||||
o Group Sources moved to single directory
|
|
||||||
o Docs moved to doc directory
|
|
||||||
--akm
|
|
||||||
|
|
||||||
Added Pluggable Crypto methods. Any authSource that contains a
|
|
||||||
cryptPassword method, will have it's method called, otherwise the
|
|
||||||
method selected by the user is called. --akm
|
|
||||||
|
|
||||||
Removed the cryptPassword method from existing Auth Sources. --akm
|
|
||||||
|
|
||||||
docLoginRedirect is no longer used. --akm
|
|
||||||
|
|
||||||
Changes for 0.20.2
|
|
||||||
BLAH! I missed some LDAP changes! --akm
|
|
||||||
|
|
||||||
Changes for 0.20.1
|
|
||||||
|
|
||||||
Fix import problem for pgPropSource --akm
|
|
||||||
Add performance boost to pgAuthSource and pgPropSource --akm
|
|
||||||
Make zodbAuthSource.listUsernames return a list. --akm
|
|
||||||
Update some LDAP Auth source bugs. --akm
|
|
||||||
Change references to "Authorisation" to "Authentication" since XUF
|
|
||||||
auth sources authenticate, they don't authorise. --akm
|
|
||||||
Changed the <h3> tags to <b> tags in the manage_adds.
|
|
||||||
|
|
||||||
Changes for 0.20.0
|
|
||||||
|
|
||||||
Fix:
|
|
||||||
https://sourceforge.net/tracker/index.php?func=detail&aid=547327&group_id=36318&atid=416446
|
|
||||||
https://sourceforge.net/tracker/index.php?func=detail&aid=616485&group_id=36318&atid=416448
|
|
||||||
https://sourceforge.net/tracker/index.php?func=detail&aid=594081&group_id=36318&atid=416448
|
|
||||||
https://sourceforge.net/tracker/index.php?func=detail&aid=594526&group_id=36318&atid=416448
|
|
||||||
|
|
||||||
Added LDAPAuthSource, based on the auth_ldap module for Apache
|
|
||||||
(http://www.rudedog.org/auth_ldap/) and the NDS Auth Source of
|
|
||||||
Phil Harris (AKA ftmpsh). This is only lightly tested, I don't have
|
|
||||||
the LDAP resources here to test all the features. Binding using uid/
|
|
||||||
cn and using various filters works (if the userPassword item is
|
|
||||||
present). This needs more testing by people with better LDAP setups
|
|
||||||
that I do. --akm
|
|
||||||
|
|
||||||
Padded docLoginRedirect to prevent IE from displaying "Friendly" error
|
|
||||||
messages when -D flag not present when running Zope --akm.
|
|
||||||
|
|
||||||
Update UZG to contain entry for LDAPAuthSource. Reformat text
|
|
||||||
slightly. --akm
|
|
||||||
|
|
||||||
Propogate "unable to auth" here requests up. This means the Manager
|
|
||||||
doesn't get locked out in cookie mode after adding an XUF instance.
|
|
||||||
It also means that people using a non-existant username at this level
|
|
||||||
get thrown up a level higher. This might not be what people want to
|
|
||||||
happen. --akm
|
|
||||||
|
|
||||||
Added method makeRedirectPath which is called from docLoginRedirect.
|
|
||||||
This makes the destination include any querystring that was present
|
|
||||||
when needing to redirect. -- akm.
|
|
||||||
|
|
||||||
Removed some Class globals from exUseFolder.py. These are now set
|
|
||||||
in __set_state__ if not present in the class so that upgrading users
|
|
||||||
don't get a crash (hopefully). -- akm.
|
|
||||||
|
|
||||||
pgPropSource was losing track of properties under heavy load.
|
|
||||||
Only noticable if you were setting and deleting a lot of temporary
|
|
||||||
properties. There is a global property timeout for pgPropSource. --akm
|
|
||||||
|
|
||||||
Jason Gibson <jason.gibson@sbcglobal.net> provided a nisAuthSource,
|
|
||||||
I've added it here --akm.
|
|
||||||
|
|
||||||
Refactored validate method to behave a lot more like BasicUserFolder.
|
|
||||||
Among other things, this fixes the issue where a local role could not
|
|
||||||
be granted to a user and granted permissions on the same object. --mb
|
|
||||||
|
|
||||||
Add NuxUserGroups support (previously on NuxUserGroups_support_branch)
|
|
||||||
and group sources. --bmh, mb
|
|
||||||
|
|
||||||
Now passes authFailedCode to Membership Login Page, The Default Login
|
|
||||||
Page as defined in the README.Membership will correctly display reason
|
|
||||||
for login being required --cab
|
|
||||||
|
|
||||||
Fixed Edit management pages for user-supplied auth and property
|
|
||||||
sources --bmh
|
|
||||||
|
|
||||||
Removed overriding of __len__ to return the number of users. This was
|
|
||||||
causing performance problems during authentication. See
|
|
||||||
http://sourceforge.net/mailarchive/message.php?msg_id=2230743 for
|
|
||||||
details. WARNING: this means using len(acl_users) to get the number
|
|
||||||
of users will no longer work! If you were using this trick, please
|
|
||||||
use len(acl_users.listUsers()) instead. --bmh
|
|
||||||
|
|
||||||
Make title property editable --bmh
|
|
||||||
|
|
||||||
Make Group Sources changeable dynamically after the acl_users folder has
|
|
||||||
been created --bmh
|
|
||||||
|
|
||||||
Inital import of https Auth source. Also, added a listUsers method
|
|
||||||
to the zodbBTreeProps source to support listUsers. -- jsb <jonah at cloud9.net>
|
|
||||||
|
|
||||||
Changes for 0.10.10
|
|
||||||
|
|
||||||
Added mysql Auth and mysql Prop source and mysql.sql schema. Just a
|
|
||||||
copy of the appropriate pg source with sql that works with myqsl -cab
|
|
||||||
|
|
||||||
Fixed negative user cache lookup in std_validade so that it actually
|
|
||||||
works for users being authenticated thru basic auth, especially if
|
|
||||||
they're authenticating in outer user folders -- rochael
|
|
||||||
|
|
||||||
Made smbAuthSource catch NetBIOSTimeout errors during authentication -- rochael
|
|
||||||
|
|
||||||
Fixed dtml/mainUser.dtml to be virtualhost-sensitive when displaying user
|
|
||||||
icons -- rochael
|
|
||||||
|
|
||||||
Updated UZG per user request. Fixed numbering, added information about
|
|
||||||
addition parameters like Negative Caching.
|
|
||||||
|
|
||||||
Changes for 0.10.9
|
|
||||||
|
|
||||||
Made dummyZBabelTag compatible to replace the NoBabel in OrderedFolder
|
|
||||||
while keeping its functionality in XUF -- cab
|
|
||||||
|
|
||||||
Changed _doAddUser, _doChangeUser to work with the public interface for
|
|
||||||
userfolders introduced in Zope2.5. Optional keyword arguments can now
|
|
||||||
be passed to _doAddUser and _doChangeUser.
|
|
||||||
|
|
||||||
PropertySource: Please note that createUser and updateUser, when called
|
|
||||||
from _doAddUser and _doChangeUser, will no longer be passed a REQUEST,
|
|
||||||
but a mapping with items from REQUEST updated with those from the
|
|
||||||
optional keyword arguments. -- pj
|
|
||||||
|
|
||||||
Fixed the problem with upgrading from 0.10.7 and below that didn't
|
|
||||||
account for existing XUF's not having a MessageDialog in their
|
|
||||||
contents. Now unless specificy replace it will use the MessageDialog
|
|
||||||
provided. Added how to do that to FAQ and README.Membership --cab
|
|
||||||
|
|
||||||
Made docLoginRedirect provide an absolute URL --bmh
|
|
||||||
|
|
||||||
MessageDialog in common no longer uses mangage_page_header and
|
|
||||||
mangage_page_footer v--cab
|
|
||||||
|
|
||||||
Changes for 0.10.8
|
|
||||||
|
|
||||||
Added the ability for members to change properties, and a default page
|
|
||||||
in the README.Membership to show how to do it --cab
|
|
||||||
|
|
||||||
MessageDialog is now an object in the ZODB that can be changed to fit
|
|
||||||
the site --cab
|
|
||||||
|
|
||||||
Now with 100% guaranteed race-condition-free UserCache goodness! Those
|
|
||||||
subclassing XUFUser, you will have to change your code. See User.py
|
|
||||||
for details. --mb
|
|
||||||
|
|
||||||
zodbBTreePropSource was returning None instead of the requested
|
|
||||||
default value, when called with (e.g.) someuser.getProperty('shoesize',13).
|
|
||||||
(Other property sources didn't have that bug.)
|
|
||||||
--davidc@debian.org
|
|
||||||
|
|
||||||
The tutorial loginform was wrong for Membership in README.Membership
|
|
||||||
|
|
||||||
Seems delProperty has never worked.. fixed --akm
|
|
||||||
Seems delProperty for pgPropSource has never worked.. fixed --akm
|
|
||||||
|
|
||||||
Fixed Basic Auth not auth problem. --akm
|
|
||||||
Fixed Basic Auth not cache problem. --akm
|
|
||||||
Fixed Cached Users bypassing some auth checks. --akm
|
|
||||||
|
|
||||||
Added usPropSource, which allows users to supply property methods TTW.
|
|
||||||
--bmh
|
|
||||||
|
|
||||||
Changes for 0.10.7
|
|
||||||
|
|
||||||
PropertyEditor had a typo in dtml and was casting int to None. --zxc
|
|
||||||
|
|
||||||
BasicAuth is now broken the other way, it'll allow any user to validate
|
|
||||||
with any password. --akm
|
|
||||||
|
|
||||||
Negative cache checking move was bogus. --akm
|
|
||||||
|
|
||||||
redirectToLogin didn't have a security declaration so 2.5.0 refused to
|
|
||||||
work in cookie mode *sigh* --akm
|
|
||||||
|
|
||||||
Fixed the 'None' object has no attribute 'load' setstate errors that
|
|
||||||
could crop up on propSources, and preemptively took care of the
|
|
||||||
authSources as well. Also fixed some of the weirder bugs relating to
|
|
||||||
user object acquisition context. --mb
|
|
||||||
|
|
||||||
Bug fixes from sf applied. --akm
|
|
||||||
|
|
||||||
Changes for 0.10.6
|
|
||||||
|
|
||||||
dummyZBabelTag used the python 2 re, which broke installations using
|
|
||||||
python 1.5 which still used the now deprecated regex, changed it to
|
|
||||||
catch the exception and use regex instead for python 1.5, else still
|
|
||||||
use re --cab
|
|
||||||
|
|
||||||
The redirectToLogin without Membership had a little logic problem where it
|
|
||||||
would basically garantee the existence of a query string, with at least a
|
|
||||||
lonely question mark even when there was no query string in the original
|
|
||||||
URL --rochael
|
|
||||||
|
|
||||||
smbAuthSource needed to cast NULL role properties to an empty list --akm
|
|
||||||
|
|
||||||
smbAuthSource had some dodgey zLOGing in it. --akm
|
|
||||||
|
|
||||||
smbAuthSource had some methods that should return [] instead of None. --akm
|
|
||||||
|
|
||||||
s/postgres/RADIUS/ in the radiusAuthSource DTML --akm
|
|
||||||
|
|
||||||
cookie_validate no longer pulls you from the cache if you're
|
|
||||||
logging in (which means your cookie wouldn't get set). --akm
|
|
||||||
|
|
||||||
Cookies are no longer expired if you're successfully authenticated but
|
|
||||||
merely unauthorized. --mb
|
|
||||||
|
|
||||||
Basic auth resynched with standard user folder, trying to fix
|
|
||||||
some basic auth issues. --akm.
|
|
||||||
|
|
||||||
Negative cache checking now performed outside of the two specific
|
|
||||||
validate methods. --akm.
|
|
||||||
|
|
||||||
A fairly innocuous print debug statement turned into a zLOG at error
|
|
||||||
level, removed --akm.
|
|
||||||
|
|
||||||
Clean up smbAuthSource log messages, and quieten. Only truly
|
|
||||||
exceptional cases are now logged above BLATHER. --mb
|
|
||||||
|
|
||||||
Changes for 0.10.5
|
|
||||||
|
|
||||||
Membership redirecting to login was still broken. It should be better
|
|
||||||
now (twice) --akm
|
|
||||||
|
|
||||||
logout() wasn't clearing the advanced cookie. --akm
|
|
||||||
|
|
||||||
Negative Cache Value wasn't being passed through to the XUF constructor. --akm
|
|
||||||
Log Users Out DTML code was broken, should work now. --akm
|
|
||||||
|
|
||||||
The User object now contains the authSource as well as the propSource,
|
|
||||||
making access to roles for custom User-objects possible. --dlk
|
|
||||||
|
|
||||||
Following akm's advice, fixed manage_beforeDelete to use two separate
|
|
||||||
try:except blocks to ensure that if cache-removal fails, deleting
|
|
||||||
the container.__allow_groups__ property is attempted. This should
|
|
||||||
fix the problem where deleted xuf instances remain as "ghost" products
|
|
||||||
causing interference with newer versions of xuf, and also fixes the
|
|
||||||
problem where deleting a xuf acl_users in a folder makes that folder
|
|
||||||
inaccessible. --dlk
|
|
||||||
|
|
||||||
Fixed cache_delete that was missing the "self" parameter in the method
|
|
||||||
defintion. --dlk
|
|
||||||
|
|
||||||
Fixed xcache_delete that was missing the "self" parameter in the method
|
|
||||||
definition --akm d8)
|
|
||||||
|
|
||||||
These previous two fix the problems with manage_beforeDelete, but, it
|
|
||||||
will stay the same for now --akm.
|
|
||||||
|
|
||||||
Fixed cache_deleteCookieCache that was missing the "self" parameter in
|
|
||||||
the method defintion. --dlk ;)
|
|
||||||
|
|
||||||
Changes for 0.10.4
|
|
||||||
|
|
||||||
The instructions for File Based Auth were incorrect in the UZG --akm
|
|
||||||
|
|
||||||
redirectToLogin was totally wrong for membership... --akm
|
|
||||||
docLogin was fixed for VHM use. --akm
|
|
||||||
|
|
||||||
Advanced Cookie Mode has changed so that it no longer sends the username
|
|
||||||
and password. Instead a hash is used as a key into a module level cache.
|
|
||||||
This should be 100% more secure than standard cookie mode, and removes
|
|
||||||
the stupid back doors I enabled in the previous version. This work was
|
|
||||||
based on conversations I had with Stuart Bishop (I basically lifted
|
|
||||||
the hashing scheme from GUF). This makes use of the Module level cache
|
|
||||||
code. --akm
|
|
||||||
|
|
||||||
There was a code cleanup and a slight reorganisation of some files. --akm
|
|
||||||
|
|
||||||
The main User Object has migrated to XUFUser and simarly with the
|
|
||||||
AnonUser. There is now an empty [Anon]User class that has XUFUser as
|
|
||||||
it's base. This allows people to create custom User Objects without
|
|
||||||
jumping through hoops (and simplifies maintaining patches) --akm
|
|
||||||
|
|
||||||
Cache Code has changed again. Now there is a module level cache, so
|
|
||||||
that auth data is shared between threads for a single XUF (thanks to
|
|
||||||
Stuart Bishop for an enlightening discussion on this and other issues,
|
|
||||||
and thanks to Chris McDonough for talking me through setting up module
|
|
||||||
level globals [and sending me some code to work from]) --akm
|
|
||||||
|
|
||||||
A Negative User Cache now exists. This is only generally useful for
|
|
||||||
use with remote auth sources where repeatedly trying to auth non-existant
|
|
||||||
users is very expensive (where they are authed at a higher level).
|
|
||||||
You can enable this on creation or from the parameters screen (positive
|
|
||||||
time in seconds enables). --akm
|
|
||||||
|
|
||||||
Domain checking code finally removed. --akm
|
|
||||||
|
|
||||||
zodbBTreePropSource changed to be friendlier about users that exist
|
|
||||||
in remote locations (i.e. aren't create as such through the ZMI). -- akm
|
|
||||||
|
|
||||||
Changed some 'print's in the code to use zLOG.LOG
|
|
||||||
instead. Files affected so far (more to follow): -- rochael
|
|
||||||
|
|
||||||
* exUserFolder.py
|
|
||||||
* basicMemberSource/basicMemberSource.py
|
|
||||||
* zodbBTreePropSource/zodbBTreePropSource.py
|
|
||||||
* zodbPropSource/zodbPropSource.py
|
|
||||||
|
|
||||||
Changed a couple things in smbAuthSource.py: -- rbanffy
|
|
||||||
|
|
||||||
* Method _authenticate_retry now logs several kinds of information
|
|
||||||
for debugging and diagnostics.
|
|
||||||
|
|
||||||
* Modified socket.error handling in _authenticate_retry: changed
|
|
||||||
"raise" to "return 0".
|
|
||||||
|
|
||||||
* Since this generated more problems (failed authentications) than
|
|
||||||
it solved (our impression it was not right not to return 0 in an
|
|
||||||
auth fail even due to a communications malfunction), we also
|
|
||||||
changed socket.error handling to retry no mather what errno tells
|
|
||||||
us (it said different things for the same problem under Windows
|
|
||||||
and Linux).
|
|
||||||
|
|
||||||
* In order to prevent infinite retries, changed retry handling a
|
|
||||||
bit. It now retries 3 times. Real-use data will tell us if we
|
|
||||||
should increase or not retries. To better convey the meaning of
|
|
||||||
the parameter, changed "retry_depth" to "retries". I strongly
|
|
||||||
advise the use of credential caching with smbAuthSource, tough, as
|
|
||||||
it reduces socket errors and load on the domain controllers.
|
|
||||||
|
|
||||||
Changes for 0.10.3.1
|
|
||||||
|
|
||||||
Readded support for I18N without ZBabel installation, somehow missed
|
|
||||||
during the transition to SF CVS.
|
|
||||||
|
|
||||||
Some text changes as well as an update to the dictionary while we're
|
|
||||||
at it. No functional changes for this release though.
|
|
||||||
|
|
||||||
Changes for 0.10.3
|
|
||||||
|
|
||||||
Missed a few LoginRequireds.
|
|
||||||
|
|
||||||
Fixed a bug with __allow_groups__ not being set after paste
|
|
||||||
(probably also not after import).
|
|
||||||
|
|
||||||
The sources are now sorted by name in the drop down box..
|
|
||||||
|
|
||||||
a BTree version of zodbAuthSource
|
|
||||||
a BTree version of zodbPropSource
|
|
||||||
|
|
||||||
These aren't really all that different to the originals that were
|
|
||||||
provided by Alex, but, they use BTrees instead of PersistentMappings,
|
|
||||||
and try to avoid various persistence problems associated with dicts.
|
|
||||||
Both versions will continue to be supported.
|
|
||||||
|
|
||||||
Patches from SF applied.
|
|
||||||
|
|
||||||
Advanced Cookie Mode added.
|
|
||||||
This mode adds a rotor cipher around the cookie. A secret is provided
|
|
||||||
in order to encode the cookie. The username and password are placed
|
|
||||||
within a small class which is pickled and then encrypted and then
|
|
||||||
base64 encoded for transport. There is also a timestamp inside the cookie,
|
|
||||||
so the ultra-paranoid of you can rotate the cookie based on the timestamp
|
|
||||||
inside.
|
|
||||||
|
|
||||||
Abstracted out the setting and decoding of cookies.
|
|
||||||
|
|
||||||
Changes for 0.10.2
|
|
||||||
|
|
||||||
all raise 'LoginRequired' <- raise 'Unauthorized'
|
|
||||||
|
|
||||||
Raising unauthorizes breaks a million things. CMF people can just
|
|
||||||
put up with configuring their portal properly.
|
|
||||||
|
|
||||||
Radius resynced with version from sourceforge.
|
|
||||||
manage_tabs redone to be ZBabel'd and to look like standard tabs.
|
|
||||||
|
|
||||||
German Language added to the ZBabel dictionary.
|
|
||||||
|
|
||||||
|
|
||||||
Changes for 0.10.1
|
|
||||||
|
|
||||||
all raise 'LoginRequired' -> raise 'Unauthorized'
|
|
||||||
|
|
||||||
Bug in etcAuthSource listUsers fixed,
|
|
||||||
and cryptPassword also fixed to get the actual salt.
|
|
||||||
|
|
||||||
Zope 2.4.3 has dicked with security settings again.. I've had a round
|
|
||||||
of permission whacking.
|
|
||||||
|
|
||||||
Buggy handling of empty role lists was fixed.
|
|
||||||
|
|
||||||
Change to smbAuthSource to use string.lower on usernames for
|
|
||||||
python 1.5.2 compatibility?
|
|
||||||
|
|
||||||
|
|
||||||
Changes for 0.10.0
|
|
||||||
|
|
||||||
Added explicit roles for manage_editUser and friends, to allow
|
|
||||||
the "Manage users" permission to be useful to non-Manager Users.
|
|
||||||
Thanks to Heimo Laukkanen <huima@fountainpark.org> for reporting this
|
|
||||||
one.
|
|
||||||
|
|
||||||
zodbAuthSource made more persistent <alex@quad.com.ar>
|
|
||||||
zodbPropSource was blowing when deleting temporary properties.
|
|
||||||
|
|
||||||
XUF is now ZBabel'd which means you can view XUF in different languages
|
|
||||||
for logging in and installation, if your browser locale is set up.
|
|
||||||
You will need the latest ZBabel installed. The translation file is in the
|
|
||||||
I18N directory.
|
|
||||||
|
|
||||||
Import this (using Import/Export in ZODB) at the same level as your
|
|
||||||
ZBabelTower, and then import it from ZBabel. If you have ZBabel installed,
|
|
||||||
but, your application can't find a ZBabelTower, because of a bug in the
|
|
||||||
current dtml-fish tag, you might experience some problems. This ZBabel
|
|
||||||
bug should be fixed sometime soon.
|
|
||||||
|
|
||||||
You do not need ZBabel installed to run XUF, XUF installs a dummy
|
|
||||||
interface for ZBabel so that XUF can continue to run (sorry folks it
|
|
||||||
defaults to Australian English).
|
|
||||||
|
|
||||||
getUserNames() was returning the wrong stuff (notably affected TheJester's
|
|
||||||
WorkOrders Product)
|
|
||||||
|
|
||||||
There is a now an 'Advanced Postgres' Auth Source that uses a seperate
|
|
||||||
Roles table and a 'more relational' layout. The schema is with the
|
|
||||||
auth source in pgAuthSourceAlt. Contributed by
|
|
||||||
Adam Manock <abmanock@earthlink.net>
|
|
||||||
|
|
||||||
If you had a membership source and had specified a login page, XUF was
|
|
||||||
still using the stock docLogin instead of the membership specified page
|
|
||||||
(for redirectToLogin, exceptions still raise the docLogin).
|
|
||||||
|
|
||||||
I changed the icon to something a *little* less hideous
|
|
||||||
|
|
||||||
Leonardo Rochael Almeida <leo@hiper.com.br> made the following changes
|
|
||||||
to smbAuthSource
|
|
||||||
|
|
||||||
* Added a 'winsserver' constructor parameter and a '_winsserver'
|
|
||||||
instance variable to the 'smbAuthSource' class. This variable should
|
|
||||||
be the empty string, meaning that the authenticaton host will be
|
|
||||||
looked up by broadcast, or an IP address string pointing to a WINS
|
|
||||||
server.
|
|
||||||
|
|
||||||
* Modified the dtml templates to ask for the above mentioned WINS
|
|
||||||
server (and also to replace 'Add' with 'Change' in
|
|
||||||
'manage_editsmbAuthSourceForm').
|
|
||||||
|
|
||||||
* Refactored the smbAuthSource class to isolate all smb interaction
|
|
||||||
inside well defined methods.
|
|
||||||
|
|
||||||
|
|
||||||
Changes for 0.9.0
|
|
||||||
|
|
||||||
Messages are now sent back to the docLogin form. There's a file called
|
|
||||||
LoginRequiredMessages.py where the messages are kept for now (it might
|
|
||||||
end up a run-time configurable thing later).
|
|
||||||
|
|
||||||
There's a new docLogin.dtml file on disk that shows how to use the new
|
|
||||||
messages. Because docLogin is in the ZODB this won't be automatically
|
|
||||||
upgraded.
|
|
||||||
|
|
||||||
Idle Session Timeouts are in (this is the reason for the minor bump).
|
|
||||||
If you flick the switch, then users are forced back to the login form
|
|
||||||
(with a message saying their session timed out), when they're removed
|
|
||||||
from the cache.
|
|
||||||
|
|
||||||
I made some adjustments to the tabs on the management interface because
|
|
||||||
they were too big, and I cleaned it up a bit for times when they run
|
|
||||||
together.
|
|
||||||
|
|
||||||
The internal API was inconsistent, so that's been updated.
|
|
||||||
AuthSources no longer need to provide getUsers(), it was never
|
|
||||||
being called anyway since exUserFolder built it's own.
|
|
||||||
listUsers now returns the same data as listOneUser, this is used in
|
|
||||||
other places as if it were a list of listOneUser calls.
|
|
||||||
|
|
||||||
Fixed pgAuthSource to deal with NULL rather than empty roles
|
|
||||||
columns (legacy columns).
|
|
||||||
|
|
||||||
Changed Home Directory creation to use copy & paste functions to
|
|
||||||
copy the skeleton data.
|
|
||||||
|
|
||||||
Changes for 0.8.5
|
|
||||||
|
|
||||||
I forgot to update the schema file for userproperties to reflect
|
|
||||||
the temporary properties flag.
|
|
||||||
|
|
||||||
Checks for existing cache weren't being performed before removing users
|
|
||||||
from it, when their data was updated.
|
|
||||||
|
|
||||||
Reversed the order for checking in cookie_validate, to allow logging
|
|
||||||
in as a new user, when session tracking was on. Also now you can
|
|
||||||
login as a different user, without logging out first, which might
|
|
||||||
be useful to some people.
|
|
||||||
|
|
||||||
etcAuthSource now looks for the correct salt from the file for
|
|
||||||
encrypting the user supplied password
|
|
||||||
|
|
||||||
Changes for 0.8.4
|
|
||||||
|
|
||||||
Activating Session Tracking and then adding a new user when there
|
|
||||||
were none in the XUF was broken.
|
|
||||||
|
|
||||||
Changes for 0.8.3
|
|
||||||
|
|
||||||
The idle users are flushed from the cache when you ask for the list
|
|
||||||
of cache users (since it's iterating over the whole list anyway). So
|
|
||||||
you can manually clear your cache by looking at the Cache Stats page.
|
|
||||||
|
|
||||||
If you display the list of logged in users on your site, then your cache
|
|
||||||
will be flushed for you automagically.
|
|
||||||
|
|
||||||
Allowed a destination to be sent to redirectToLogin to allow you to
|
|
||||||
manually override the destination after logging in.
|
|
||||||
|
|
||||||
Added in a __setstate__ for pgPropSource to deal with new ZSQL Methods
|
|
||||||
being added.
|
|
||||||
|
|
||||||
Changes for 0.8.2
|
|
||||||
A number of bugs related to temp properties fixed in pgPropSource
|
|
||||||
|
|
||||||
FTP Access to folders protected with cookie_mode has been fixed, it
|
|
||||||
now reverts to std_auth (which handles the FTP connection fine), since
|
|
||||||
FTP auths are handled by getting a "Basic" auth tag coming through, which
|
|
||||||
should never happen in cookie mode.
|
|
||||||
|
|
||||||
This has the knock-on effect of authenticating users that auth from a
|
|
||||||
higher acl_users that doesn't use cookies, 'more' correctly now. Which is
|
|
||||||
if you have a user defined above, and in XUF and the XUF user has less
|
|
||||||
permissions, it'll 401 you if you don't have permissions locally
|
|
||||||
(which is the correct behaviour). This bit me in the arse when I changed it,
|
|
||||||
and I'm still leaving it this way. d8)
|
|
||||||
|
|
||||||
Users are now flushed from the cache when you edit them (in case you changed
|
|
||||||
roles), so that new roles should take effect immediately.
|
|
||||||
|
|
||||||
The credential cache now uses the (Zope) builtin BTree Module for caching
|
|
||||||
rather than the AVL Tree implementation. There was a nasty issue with users
|
|
||||||
appearing multiple times in the AVL Tree which sucked.
|
|
||||||
|
|
||||||
There is a report of the Radius Auth Source being broken (most likely
|
|
||||||
by me), if your radius source stops working, you can try copying the
|
|
||||||
py-radius.py file from sourceforge over the top of radius.py. If someone
|
|
||||||
gives me a traceback, I can fix it. I don't seem to be having problems,
|
|
||||||
but, I don't have a full time RADIUS source either.
|
|
||||||
|
|
||||||
|
|
||||||
Changes for 0.8.1
|
|
||||||
|
|
||||||
A bug in _doAddUser was fixed
|
|
||||||
A bug in the User Object unconditionally calling the prop source was fixed.
|
|
||||||
|
|
||||||
|
|
||||||
Changes for 0.8.0
|
|
||||||
|
|
||||||
Experimental "Session Tracking" added (why is it called that? we don't really
|
|
||||||
track anything, just associate arbitrary data with anonymous users).
|
|
||||||
This relies on the credential cache being active. Your session will
|
|
||||||
automatically expire when the anonymous user is so idle that they are
|
|
||||||
expired from the cache. This is not currently acceptable (to me), but,
|
|
||||||
it might be to other people, I await feedback on how sessions should expire
|
|
||||||
gracefully.
|
|
||||||
|
|
||||||
Updated the README.txt file to point at the UZG and to explain the
|
|
||||||
version numbering system.
|
|
||||||
|
|
||||||
All this time you couldn't delete properties from a user... who knew?
|
|
||||||
It's fixed now.
|
|
||||||
|
|
||||||
Temporary properties now available, you can setTempProperty() on a
|
|
||||||
user object, and also flushTempProperties() on a user object.
|
|
||||||
Temporary properties are accessed like normal properties, and can be
|
|
||||||
deleted in the same way. flushTempProperties is there to do a quick
|
|
||||||
flush of all the crap you might have inserted (useful for sessions).
|
|
||||||
If your user is flushed from the cache, then all temp properties will
|
|
||||||
also be removed at that point.
|
|
||||||
|
|
||||||
Propsource providers should look at the new temp properties stuff and
|
|
||||||
update accordingly.
|
|
||||||
|
|
||||||
Alex provided a whole heap of patches to make basicMembership more usable,
|
|
||||||
well make it actually work.
|
|
||||||
|
|
||||||
Matt Behrens supplied patches to prevent null logins and to allow case
|
|
||||||
insensitive logins for smbAuthSource
|
|
||||||
|
|
||||||
Added a basic FAQ.
|
|
||||||
|
|
||||||
|
|
||||||
Changes for 0.7.10
|
|
||||||
|
|
||||||
Active Users type functionality was added. The new function is called
|
|
||||||
getUserCacheUsers(). It returns a list of dicts;
|
|
||||||
|
|
||||||
{'username': theusername, 'lastAccessed': float_value}
|
|
||||||
|
|
||||||
lastAccessed represents the last time the user touched something.
|
|
||||||
The Cache Stats page shows an example usage showing idle time (very cool
|
|
||||||
I think :-)
|
|
||||||
|
|
||||||
The logout method was not correctly removing users from the cache,
|
|
||||||
although the cookie was removed, so logins were still enforced. I'm not
|
|
||||||
sure of any side-effects related to it, but,
|
|
||||||
|
|
||||||
Some permissions were a little too liberal, including allowing arbitrary
|
|
||||||
users to set and get Properties on the acl_users folder.
|
|
||||||
|
|
||||||
Copy/Paste support for pasting exUserFolders into the root was added.
|
|
||||||
I'm not sure I like the way this is done. I haven't found any side effects
|
|
||||||
so far, but, just be wary. Adding an exUserFolder to the root becomes
|
|
||||||
semi-trivial now. Create one in a sub-folder. Login as the emergency user.
|
|
||||||
CUT the exUserFolder. Delete the standard acl_users folder. Paste exUserFolder.
|
|
||||||
You should be away. At least it worked fine for me... YMMV
|
|
||||||
|
|
||||||
_doChangeUser and _doDelUsers added so users can be altered and deleted
|
|
||||||
like for Standard UserFolder.
|
|
||||||
|
|
||||||
_createInitialUser added so there should always be your initUser (hopefully)
|
|
||||||
when you create your exUserFolder.
|
|
||||||
|
|
||||||
Emergency User checking brought into line with Standard Folder
|
|
||||||
|
|
||||||
__creatable_by_emergency_user_ added and returns 1 to explicitly allow this.
|
|
||||||
|
|
||||||
Unenlightened Zopistas Guide updated to have a 'Recipe' like section.
|
|
||||||
Currently contains a section about adding exUserFolders from python.
|
|
||||||
|
|
||||||
|
|
||||||
Changes for 0.7.9
|
|
||||||
|
|
||||||
RADIUS authSource had a problem with non-integers being extracted from
|
|
||||||
REQUEST (I wish someone at DC would fix this already). I worked around
|
|
||||||
this problem
|
|
||||||
|
|
||||||
Default port for RADIUS is now 1812 in line with the IANA sanctioned list.
|
|
||||||
|
|
||||||
Unenlightened Zopistas Guide to exUserFolder version 0.0 included,
|
|
||||||
covers installation and authentication sources, and the most common
|
|
||||||
configuration mistake (or misunderstanding).
|
|
||||||
|
|
||||||
I almost released with the daggy management screens all Purple or SkyBlue,
|
|
||||||
so consider yoursevles lucky. This would have been the "Blue" release.
|
|
||||||
|
|
||||||
Changes for 0.7.8
|
|
||||||
|
|
||||||
zodbPropSource had a bug that must have been there since 0.0.0 where
|
|
||||||
_p_changed wasn't being called on create, update, or delete user.
|
|
||||||
Thanks to Bouke Scheurwater for spotting that one.
|
|
||||||
|
|
||||||
Alex provided a number of patched to fix a whole bunch of goofy stuff
|
|
||||||
with Basic Member Source that was stupidly wrong.
|
|
||||||
|
|
||||||
Matt Behrens provided a patch to allow emergency user to own exUserFolders
|
|
||||||
and some of the sources. I've grudgingly updated all the sources to allow
|
|
||||||
this. It's just a hey nonny nonny to people using it as a root authenticator
|
|
||||||
now.
|
|
||||||
|
|
||||||
Matt Behrens also provided a patch to fix 'broken pipe' problems with
|
|
||||||
smbAuthSource.
|
|
||||||
|
|
||||||
pySMB is now at 0.2 for smbAuthSource WARNING: This will try to use DES
|
|
||||||
encrypted passwords. Apparently it should be ok if your server doesn't want
|
|
||||||
them. However if it breaks, unpack the pySMB distribution in the
|
|
||||||
smbAuthSource directory, there are registry examples there to turn
|
|
||||||
it off. It unfortunately needs the mxCrypto tools for encrypted passwords
|
|
||||||
to work. When I've got a bit more time, I'll see if I can make it use
|
|
||||||
crypt or fcrypt if available instead.
|
|
||||||
|
|
||||||
Explicit checks for the emergency user were placed into the cookie_validate
|
|
||||||
routines. I suspect this may have been the cause of some grief with people
|
|
||||||
doing weird things like trying to make it the root auth folder.
|
|
||||||
|
|
||||||
Changes for 0.7.7
|
|
||||||
|
|
||||||
Some Auth sources had problems coping with no roles being selected when
|
|
||||||
a user was created from the management interface, the stock ones were fixed.
|
|
||||||
|
|
||||||
I screwed up some of the DTML, and forgot to change the loading of two of
|
|
||||||
the methods from the dtml directory.
|
|
||||||
|
|
||||||
NO MORE TRACEBACKS ON LOGIN FORMS, there is a little redirector dtml file
|
|
||||||
dtml/docLoginRedirect that redirects to acl_users/docLogin with destination
|
|
||||||
set to take them back to where they were going. If you have a custom loginPage
|
|
||||||
change the redirector dtml to point to your new page.
|
|
||||||
|
|
||||||
standard_html swapped for manage_page on Management Pages. Hopefully
|
|
||||||
this doesn't break someone with an old copy of Zope.
|
|
||||||
|
|
||||||
Credential Caching is now available by default for all Authentication Sources,
|
|
||||||
upgrading installs will get this defaulted to 0 for no caching. You can alter
|
|
||||||
the cache level from the Parameters Tab. Authors of external sources should
|
|
||||||
remove any internal auth caching they're doing, and allow the user to decide
|
|
||||||
how long to cache the credentials for.
|
|
||||||
|
|
||||||
|
|
||||||
Changes for 0.7.6
|
|
||||||
|
|
||||||
smbAuthSource included. Doesn't require any external libraries, or compiling.
|
|
||||||
Uses pySMB from Micheal Teo <michaelteo@bigfoot.com>
|
|
||||||
|
|
||||||
Changes for 0.7.5
|
|
||||||
The Management Interface now batches the user list by 10. This isn't
|
|
||||||
configurable at the moment (just change the dtml).
|
|
||||||
|
|
||||||
The code was re-organised slightly, with all the DTML moving into its
|
|
||||||
own directory for core.
|
|
||||||
|
|
||||||
radiusAuthSource added, but, is so far untested. It is a direct port of
|
|
||||||
ZRadius for GUF, but, I haven't had a chance to setup a RADIUS server to
|
|
||||||
test it out.
|
|
||||||
|
|
||||||
You can add properties to a user from the management interface.
|
|
||||||
|
|
||||||
List Properties on users can be added and edited, if I can work out a decent
|
|
||||||
way to edit Dicts/Mappings, I'll add that feature in.
|
|
||||||
|
|
||||||
This paves the way for defining a set of properties in the Membership
|
|
||||||
source, so it can create a Signup and Edit page for you automatically.
|
|
||||||
You will also be able to specify which properties the user can edit, or
|
|
||||||
roles required to edit a property, this will be in a later release though.
|
|
||||||
|
|
||||||
pgPropSource was updated to take into account non-scalar types, and now
|
|
||||||
pickles all data going into the database, this means ints will stay as ints,
|
|
||||||
et al.
|
|
||||||
There is code in there to cope with older properties coming out as strings.
|
|
||||||
The Schema remains the same.
|
|
||||||
|
|
||||||
Changes for 0.7.2
|
|
||||||
Changes to make it work with older version of python
|
|
||||||
Some minor bug fixes for membership.
|
|
||||||
|
|
||||||
Changes for 0.7.1
|
|
||||||
DTML Change for cmfPropSource
|
|
||||||
|
|
||||||
Changes for 0.7.0
|
|
||||||
exUserFolder was a little too liberal in removing its cruft, this is now
|
|
||||||
fixed.
|
|
||||||
|
|
||||||
cmfPropSource was provided by Alan Runyan which is a layer around the CMF
|
|
||||||
property stuff. It's conditionally imported, so if you don't have CMF
|
|
||||||
installed you don't need to worry that'll it'll break.
|
|
||||||
|
|
||||||
Property Sources are optional, and there is a NULL Property Source for this
|
|
||||||
purpose.
|
|
||||||
|
|
||||||
Membership hooks, and a rough start at membership (basicMemberSource),
|
|
||||||
which has some usable functionality (you MUST read README.Membership before
|
|
||||||
using this).
|
|
||||||
|
|
||||||
Membership Sources are optional and there is a NULL Membership Source for
|
|
||||||
this purpose.
|
|
||||||
|
|
||||||
|
|
||||||
Changes for 0.6.2
|
|
||||||
exUserFolder was leaving cruft around when it was being deleted from
|
|
||||||
Folders. The cruft should now be obliterated if you delete an exUserFolder.
|
|
||||||
|
|
||||||
Changes for 0.6.1
|
|
||||||
Ownership tab enabled, for those sick monkeys that want to use it as a root
|
|
||||||
Folder (there are some).
|
|
||||||
|
|
||||||
fcrypt got the __init__.py that was missing from the 0.6.0 release
|
|
||||||
zodbAuthSource updated to pull in fcrypt if crypt was missing.
|
|
||||||
|
|
||||||
Changes for 0.6.0
|
|
||||||
|
|
||||||
Updated for 2.4.1 / Python 2.1
|
|
||||||
Bug in pgPropSource not deleting users from the property cache fixed.
|
|
||||||
Bug with Local Roles not getting what it expected fixed.
|
|
||||||
Alex Verstraeten provided zodbAuthSource, there's a README.zodbAuthSource,
|
|
||||||
and the same README inside the zodbAuthSource directory.
|
|
||||||
fcrypt is now included and used if crypt cannot be imported. More information
|
|
||||||
on fcrypt can be found at http://home.clear.net.nz/pages/c.evans/sw/. This
|
|
||||||
should help particularly Windows users a lot.
|
|
||||||
Rudimentary API doc included.
|
|
||||||
|
|
||||||
Changes for 0.5.0
|
|
||||||
|
|
||||||
A serious bug in zodbPropSource was fixed.
|
|
||||||
|
|
||||||
There is now the option of providing a 'Remote Auth' function for
|
|
||||||
validating. This allows things like IMAP/LDAP auth sources to do their
|
|
||||||
authentication, since they don't return passwords you can use in general.
|
|
||||||
|
|
||||||
There's already a 3rd Party solution that provides IMAP/POP3 authentication,
|
|
||||||
using the new API.
|
|
||||||
|
|
||||||
Changes for 0.4.6
|
|
||||||
|
|
||||||
Minor dtml hacks
|
|
||||||
|
|
||||||
Changes for 0.4.5
|
|
||||||
|
|
||||||
Hooks for 'editing' Authentication and Property Sources were added, along
|
|
||||||
with the relevant methods in each of the sources.
|
|
||||||
|
|
||||||
The management interfaces got a little overhaul, just to make them
|
|
||||||
a little different (yes I know everything I do looks the same). The two
|
|
||||||
I didn't want to mess with still have the acquired management interfaces.
|
|
||||||
|
|
||||||
A fix for the ZODB Property Source which was missing a few methods.
|
|
||||||
|
|
||||||
Changes for 0.4.0
|
|
||||||
|
|
||||||
Based on an idea from Martin von Loewis, I added in support for defining
|
|
||||||
roles for etcAuthSource. This basically uses the current Prop source to
|
|
||||||
store a 'roles' property. The default role is still there as well for
|
|
||||||
those of you who might be using it.
|
|
||||||
|
|
||||||
Changes for 0.3.0
|
|
||||||
|
|
||||||
Adrien Hernot noticed that properties for new users using zodbPropSource
|
|
||||||
were causing havoc, and that the version.txt file was completely wrong.
|
|
||||||
Andreas also noticed the version.txt was wrong.
|
|
||||||
|
|
||||||
I've been bugged enough by the pair of them to change the single +=
|
|
||||||
into 1.5.2 compliant syntax.
|
|
||||||
|
|
||||||
I don't make any claims about it working under 1.5.2 though.
|
|
||||||
|
|
||||||
Changes for 0.2.0
|
|
||||||
|
|
||||||
Even more embarassment...
|
|
||||||
|
|
||||||
Andreas Heckel provided fixes for some stupid things I left out including;
|
|
||||||
|
|
||||||
o Fixing the way I was handling multiple roles coming out of the database
|
|
||||||
o The wrong icon in the user display
|
|
||||||
o Alerting me to the fact that pgPropSource didn't actually have a
|
|
||||||
deleteUsers hook
|
|
||||||
o Providing a schema for automatically deleting properties in postgres
|
|
||||||
if you delete a user from the auth source (you have to be using both
|
|
||||||
pg sources for this to work, and they'd have to be in the same database)
|
|
||||||
I've put Andreas schema into the distribution, if you want to use
|
|
||||||
exUserFolder as a straight pgUserFolder, you'll also need to edit
|
|
||||||
exUserFolder.py and comment out the line indicated in deleteUsers()
|
|
||||||
|
|
||||||
Changes for 0.1.0
|
|
||||||
|
|
||||||
Pretty embarassing really.
|
|
||||||
|
|
||||||
M. Adam Kendall (DaJoker) found some stupid things in the 0.0.0 release
|
|
||||||
including the fact you couldn't edit user properties, or update them,
|
|
||||||
or actually change a user in anyway.
|
|
||||||
|
|
||||||
I also discovered I was resetting the password to empty if you left it
|
|
||||||
empty..
|
|
@ -1,4 +0,0 @@
|
|||||||
import pass_crypt
|
|
||||||
import pass_md5
|
|
||||||
import pass_sha
|
|
||||||
import pass_plain
|
|
@ -1,4 +0,0 @@
|
|||||||
*.pyc
|
|
||||||
*.pyo
|
|
||||||
*~
|
|
||||||
.*.swp
|
|
@ -1,35 +0,0 @@
|
|||||||
2001-05-05 Carey Evans <careye@spamcop.net>
|
|
||||||
|
|
||||||
* fcrypt.py: Add module doc string for pydoc, and other globals
|
|
||||||
for pydoc as well. Add __all__ for Python 2.1, and add
|
|
||||||
underscores to the front of private variables and functions.
|
|
||||||
(_set_key): Remove overly clever copying of globals into default
|
|
||||||
parameters, explicitly copying _shift2 and _skb before the loop.
|
|
||||||
(_body): Copy _SPtrans explicitly, as above. Remove CR_ENCRYPT
|
|
||||||
inline function, and reroll unrolled loop using the contents of
|
|
||||||
this function. Result: more readable code, and a 400% speedup!
|
|
||||||
(crypt): Add doc string for pydoc and doctest.
|
|
||||||
(_test): New function for doctest.
|
|
||||||
|
|
||||||
* setup.py: Add fields for PKG-INFO metadata.
|
|
||||||
|
|
||||||
* README: Add URL of distutils installation manual.
|
|
||||||
|
|
||||||
* LICENSE: Add note about license on fcrypt.py being the union of
|
|
||||||
my license on the Python code and Eric Young's on the original C.
|
|
||||||
|
|
||||||
2001-03-24 Carey Evans <careye@spamcop.net>
|
|
||||||
|
|
||||||
* setup.py: Move license to separate file. Change email address
|
|
||||||
to SpamCop forwardder. Update version to 1.1.
|
|
||||||
|
|
||||||
* fcrypt.py: Update license text and email address.
|
|
||||||
(crypt): Fix bug where passwords longer than eight characters were
|
|
||||||
not truncated.
|
|
||||||
|
|
||||||
* README: Update crypt module URL. Remove license text, and add
|
|
||||||
pointer to LICENSE file. Update email address.
|
|
||||||
|
|
||||||
* MANIFEST.in: Add LICENSE, ChangeLog and MANIFEST.in.
|
|
||||||
|
|
||||||
* LICENSE: New file.
|
|
@ -1,77 +0,0 @@
|
|||||||
fcrypt.py copyrights and license
|
|
||||||
--------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
The Python code by Carey Evans has the following license, which is the
|
|
||||||
original Python license with the serial numbers filed off, and the
|
|
||||||
restrictions on advertising removed.
|
|
||||||
|
|
||||||
Copyright (C) 2001, 2001 Carey Evans <careye@spamcop.net>
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and distribute this software and its
|
|
||||||
documentation for any purpose and without fee is hereby granted,
|
|
||||||
provided that the above copyright notice appear in all copies and that
|
|
||||||
both that copyright notice and this permission notice appear in
|
|
||||||
supporting documentation.
|
|
||||||
|
|
||||||
CAREY EVANS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
||||||
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
|
||||||
EVENT SHALL CAREY EVANS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
|
||||||
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
|
|
||||||
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
||||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
||||||
PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
|
|
||||||
|
|
||||||
The original C code on which this module was based has the following
|
|
||||||
more restrictive license, so the source for fcrypt.py should be
|
|
||||||
considered to be covered by the union of my license and Eric Young's.
|
|
||||||
|
|
||||||
This library is free for commercial and non-commercial use as long as
|
|
||||||
the following conditions are aheared to. The following conditions
|
|
||||||
apply to all code found in this distribution, be it the RC4, RSA,
|
|
||||||
lhash, DES, etc., code; not just the SSL code. The SSL documentation
|
|
||||||
included with this distribution is covered by the same copyright terms
|
|
||||||
except that the holder is Tim Hudson (tjh@mincom.oz.au).
|
|
||||||
|
|
||||||
Copyright remains Eric Young's, and as such any Copyright notices in
|
|
||||||
the code are not to be removed.
|
|
||||||
If this package is used in a product, Eric Young should be given attribution
|
|
||||||
as the author of the parts of the library used.
|
|
||||||
This can be in the form of a textual message at program startup or
|
|
||||||
in documentation (online or textual) provided with the package.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions
|
|
||||||
are met:
|
|
||||||
1. Redistributions of source code must retain the copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in the
|
|
||||||
documentation and/or other materials provided with the distribution.
|
|
||||||
3. All advertising materials mentioning features or use of this software
|
|
||||||
must display the following acknowledgement:
|
|
||||||
"This product includes cryptographic software written by
|
|
||||||
Eric Young (eay@mincom.oz.au)"
|
|
||||||
The word 'cryptographic' can be left out if the rouines from the library
|
|
||||||
being used are not cryptographic related :-).
|
|
||||||
4. If you include any Windows specific code (or a derivative thereof) from
|
|
||||||
the apps directory (application code) you must include an acknowledgement:
|
|
||||||
"This product includes software written by Tim Hudson (tjh@mincom.oz.au)"
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
SUCH DAMAGE.
|
|
||||||
|
|
||||||
The licence and distribution terms for any publically available version or
|
|
||||||
derivative of this code cannot be changed. i.e. this code cannot simply be
|
|
||||||
copied and put under another distribution licence
|
|
||||||
[including the GNU Public Licence.]
|
|
@ -1 +0,0 @@
|
|||||||
include LICENSE ChangeLog MANIFEST.in
|
|
@ -1,13 +0,0 @@
|
|||||||
Metadata-Version: 1.0
|
|
||||||
Name: fcrypt
|
|
||||||
Version: 1.2
|
|
||||||
Summary: The Unix password crypt function.
|
|
||||||
Home-page: http://home.clear.net.nz/pages/c.evans/sw/
|
|
||||||
Author: Carey Evans
|
|
||||||
Author-email: careye@spamcop.net
|
|
||||||
License: BSD
|
|
||||||
Description: A pure Python implementation of the Unix DES password crypt function,
|
|
||||||
based on Eric Young's fcrypt.c. It works with any version of Python
|
|
||||||
from version 1.5 or higher, and because it's pure Python it doesn't
|
|
||||||
need a C compiler to install it.
|
|
||||||
Platform: UNKNOWN
|
|
@ -1,33 +0,0 @@
|
|||||||
fcrypt.py
|
|
||||||
---------
|
|
||||||
|
|
||||||
This is a pure Python implementation of the Unix DES password crypt
|
|
||||||
function. It was ported from C code by Eric Young (eay@mincom.oz.au).
|
|
||||||
See the file LICENSE for copyright and license details.
|
|
||||||
|
|
||||||
This module is packaged with Distutils. If you have this installed,
|
|
||||||
or it came with your version of Python, you can install it by typing:
|
|
||||||
|
|
||||||
python setup.py install
|
|
||||||
|
|
||||||
If not, you can just copy `fcrypt.py' into a directory on your Python
|
|
||||||
library path, or into the same directory as the program that wants to
|
|
||||||
use it.
|
|
||||||
|
|
||||||
For more information, see the documentation for Python's built-in
|
|
||||||
crypt module at:
|
|
||||||
|
|
||||||
http://www.python.org/doc/current/lib/module-crypt.html
|
|
||||||
|
|
||||||
Eric Young's fcrypt.c is available from:
|
|
||||||
|
|
||||||
ftp://ftp.psy.uq.oz.au/pub/Crypto/DES/
|
|
||||||
|
|
||||||
For more Distutils information, see:
|
|
||||||
|
|
||||||
http://www.python.org/doc/current/inst/inst.html
|
|
||||||
http://www.python.org/sigs/distutils-sig/
|
|
||||||
|
|
||||||
--
|
|
||||||
Carey Evans <careye@spamcop.net>
|
|
||||||
5 May 2001
|
|
@ -1 +0,0 @@
|
|||||||
import fcrypt
|
|
@ -1,602 +0,0 @@
|
|||||||
# fcrypt.py
|
|
||||||
|
|
||||||
"""Unix crypt(3) password hash algorithm.
|
|
||||||
|
|
||||||
This is a port to Python of the standard Unix password crypt function.
|
|
||||||
It's a single self-contained source file that works with any version
|
|
||||||
of Python from version 1.5 or higher. The code is based on Eric
|
|
||||||
Young's optimised crypt in C.
|
|
||||||
|
|
||||||
Python fcrypt is intended for users whose Python installation has not
|
|
||||||
had the crypt module enabled, or whose C library doesn't include the
|
|
||||||
crypt function. See the documentation for the Python crypt module for
|
|
||||||
more information:
|
|
||||||
|
|
||||||
http://www.python.org/doc/current/lib/module-crypt.html
|
|
||||||
|
|
||||||
The crypt() function is a one-way hash function, intended to hide a
|
|
||||||
password such that the only way to find out the original password is
|
|
||||||
to guess values until you get a match. If you need to encrypt and
|
|
||||||
decrypt data, this is not the module for you.
|
|
||||||
|
|
||||||
There are at least two packages providing Python cryptography support:
|
|
||||||
M2Crypto at <http://www.pobox.org.sg/home/ngps/m2/>, and amkCrypto at
|
|
||||||
<http://www.amk.ca/python/code/crypto.html>.
|
|
||||||
|
|
||||||
Functions:
|
|
||||||
|
|
||||||
crypt() -- return hashed password
|
|
||||||
"""
|
|
||||||
|
|
||||||
__author__ = 'Carey Evans <careye@spamcop.net>'
|
|
||||||
__version__ = '1.2'
|
|
||||||
__date__ = '6 May 2001'
|
|
||||||
__credits__ = '''michal j wallace for inspiring me to write this.
|
|
||||||
Eric Young for the C code this module was copied from.'''
|
|
||||||
|
|
||||||
__all__ = ['crypt']
|
|
||||||
|
|
||||||
|
|
||||||
# Copyright (C) 2000, 2001 Carey Evans <careye@spamcop.net>
|
|
||||||
#
|
|
||||||
# Permission to use, copy, modify, and distribute this software and
|
|
||||||
# its documentation for any purpose and without fee is hereby granted,
|
|
||||||
# provided that the above copyright notice appear in all copies and
|
|
||||||
# that both that copyright notice and this permission notice appear in
|
|
||||||
# supporting documentation.
|
|
||||||
#
|
|
||||||
# CAREY EVANS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
||||||
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
|
||||||
# EVENT SHALL CAREY EVANS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
|
||||||
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
|
|
||||||
# USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
||||||
# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
||||||
# PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
|
|
||||||
# Based on C code by Eric Young (eay@mincom.oz.au), which has the
|
|
||||||
# following copyright. Especially note condition 3, which imposes
|
|
||||||
# extra restrictions on top of the standard Python license used above.
|
|
||||||
#
|
|
||||||
# The fcrypt.c source is available from:
|
|
||||||
# ftp://ftp.psy.uq.oz.au/pub/Crypto/DES/
|
|
||||||
|
|
||||||
# ----- BEGIN fcrypt.c LICENSE -----
|
|
||||||
#
|
|
||||||
# This library is free for commercial and non-commercial use as long as
|
|
||||||
# the following conditions are aheared to. The following conditions
|
|
||||||
# apply to all code found in this distribution, be it the RC4, RSA,
|
|
||||||
# lhash, DES, etc., code; not just the SSL code. The SSL documentation
|
|
||||||
# included with this distribution is covered by the same copyright terms
|
|
||||||
# except that the holder is Tim Hudson (tjh@mincom.oz.au).
|
|
||||||
#
|
|
||||||
# Copyright remains Eric Young's, and as such any Copyright notices in
|
|
||||||
# the code are not to be removed.
|
|
||||||
# If this package is used in a product, Eric Young should be given attribution
|
|
||||||
# as the author of the parts of the library used.
|
|
||||||
# This can be in the form of a textual message at program startup or
|
|
||||||
# in documentation (online or textual) provided with the package.
|
|
||||||
#
|
|
||||||
# Redistribution and use in source and binary forms, with or without
|
|
||||||
# modification, are permitted provided that the following conditions
|
|
||||||
# are met:
|
|
||||||
# 1. Redistributions of source code must retain the copyright
|
|
||||||
# notice, this list of conditions and the following disclaimer.
|
|
||||||
# 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
# notice, this list of conditions and the following disclaimer in the
|
|
||||||
# documentation and/or other materials provided with the distribution.
|
|
||||||
# 3. All advertising materials mentioning features or use of this software
|
|
||||||
# must display the following acknowledgement:
|
|
||||||
# "This product includes cryptographic software written by
|
|
||||||
# Eric Young (eay@mincom.oz.au)"
|
|
||||||
# The word 'cryptographic' can be left out if the rouines from the library
|
|
||||||
# being used are not cryptographic related :-).
|
|
||||||
# 4. If you include any Windows specific code (or a derivative thereof) from
|
|
||||||
# the apps directory (application code) you must include an acknowledgement:
|
|
||||||
# "This product includes software written by Tim Hudson (tjh@mincom.oz.au)"
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
|
|
||||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
||||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
# SUCH DAMAGE.
|
|
||||||
#
|
|
||||||
# The licence and distribution terms for any publically available version or
|
|
||||||
# derivative of this code cannot be changed. i.e. this code cannot simply be
|
|
||||||
# copied and put under another distribution licence
|
|
||||||
# [including the GNU Public Licence.]
|
|
||||||
#
|
|
||||||
# ----- END fcrypt.c LICENSE -----
|
|
||||||
|
|
||||||
|
|
||||||
import string, struct
|
|
||||||
|
|
||||||
|
|
||||||
_ITERATIONS = 16
|
|
||||||
|
|
||||||
_SPtrans = (
|
|
||||||
# nibble 0
|
|
||||||
[ 0x00820200, 0x00020000, 0x80800000, 0x80820200,
|
|
||||||
0x00800000, 0x80020200, 0x80020000, 0x80800000,
|
|
||||||
0x80020200, 0x00820200, 0x00820000, 0x80000200,
|
|
||||||
0x80800200, 0x00800000, 0x00000000, 0x80020000,
|
|
||||||
0x00020000, 0x80000000, 0x00800200, 0x00020200,
|
|
||||||
0x80820200, 0x00820000, 0x80000200, 0x00800200,
|
|
||||||
0x80000000, 0x00000200, 0x00020200, 0x80820000,
|
|
||||||
0x00000200, 0x80800200, 0x80820000, 0x00000000,
|
|
||||||
0x00000000, 0x80820200, 0x00800200, 0x80020000,
|
|
||||||
0x00820200, 0x00020000, 0x80000200, 0x00800200,
|
|
||||||
0x80820000, 0x00000200, 0x00020200, 0x80800000,
|
|
||||||
0x80020200, 0x80000000, 0x80800000, 0x00820000,
|
|
||||||
0x80820200, 0x00020200, 0x00820000, 0x80800200,
|
|
||||||
0x00800000, 0x80000200, 0x80020000, 0x00000000,
|
|
||||||
0x00020000, 0x00800000, 0x80800200, 0x00820200,
|
|
||||||
0x80000000, 0x80820000, 0x00000200, 0x80020200 ],
|
|
||||||
|
|
||||||
# nibble 1
|
|
||||||
[ 0x10042004, 0x00000000, 0x00042000, 0x10040000,
|
|
||||||
0x10000004, 0x00002004, 0x10002000, 0x00042000,
|
|
||||||
0x00002000, 0x10040004, 0x00000004, 0x10002000,
|
|
||||||
0x00040004, 0x10042000, 0x10040000, 0x00000004,
|
|
||||||
0x00040000, 0x10002004, 0x10040004, 0x00002000,
|
|
||||||
0x00042004, 0x10000000, 0x00000000, 0x00040004,
|
|
||||||
0x10002004, 0x00042004, 0x10042000, 0x10000004,
|
|
||||||
0x10000000, 0x00040000, 0x00002004, 0x10042004,
|
|
||||||
0x00040004, 0x10042000, 0x10002000, 0x00042004,
|
|
||||||
0x10042004, 0x00040004, 0x10000004, 0x00000000,
|
|
||||||
0x10000000, 0x00002004, 0x00040000, 0x10040004,
|
|
||||||
0x00002000, 0x10000000, 0x00042004, 0x10002004,
|
|
||||||
0x10042000, 0x00002000, 0x00000000, 0x10000004,
|
|
||||||
0x00000004, 0x10042004, 0x00042000, 0x10040000,
|
|
||||||
0x10040004, 0x00040000, 0x00002004, 0x10002000,
|
|
||||||
0x10002004, 0x00000004, 0x10040000, 0x00042000 ],
|
|
||||||
|
|
||||||
# nibble 2
|
|
||||||
[ 0x41000000, 0x01010040, 0x00000040, 0x41000040,
|
|
||||||
0x40010000, 0x01000000, 0x41000040, 0x00010040,
|
|
||||||
0x01000040, 0x00010000, 0x01010000, 0x40000000,
|
|
||||||
0x41010040, 0x40000040, 0x40000000, 0x41010000,
|
|
||||||
0x00000000, 0x40010000, 0x01010040, 0x00000040,
|
|
||||||
0x40000040, 0x41010040, 0x00010000, 0x41000000,
|
|
||||||
0x41010000, 0x01000040, 0x40010040, 0x01010000,
|
|
||||||
0x00010040, 0x00000000, 0x01000000, 0x40010040,
|
|
||||||
0x01010040, 0x00000040, 0x40000000, 0x00010000,
|
|
||||||
0x40000040, 0x40010000, 0x01010000, 0x41000040,
|
|
||||||
0x00000000, 0x01010040, 0x00010040, 0x41010000,
|
|
||||||
0x40010000, 0x01000000, 0x41010040, 0x40000000,
|
|
||||||
0x40010040, 0x41000000, 0x01000000, 0x41010040,
|
|
||||||
0x00010000, 0x01000040, 0x41000040, 0x00010040,
|
|
||||||
0x01000040, 0x00000000, 0x41010000, 0x40000040,
|
|
||||||
0x41000000, 0x40010040, 0x00000040, 0x01010000 ],
|
|
||||||
|
|
||||||
# nibble 3
|
|
||||||
[ 0x00100402, 0x04000400, 0x00000002, 0x04100402,
|
|
||||||
0x00000000, 0x04100000, 0x04000402, 0x00100002,
|
|
||||||
0x04100400, 0x04000002, 0x04000000, 0x00000402,
|
|
||||||
0x04000002, 0x00100402, 0x00100000, 0x04000000,
|
|
||||||
0x04100002, 0x00100400, 0x00000400, 0x00000002,
|
|
||||||
0x00100400, 0x04000402, 0x04100000, 0x00000400,
|
|
||||||
0x00000402, 0x00000000, 0x00100002, 0x04100400,
|
|
||||||
0x04000400, 0x04100002, 0x04100402, 0x00100000,
|
|
||||||
0x04100002, 0x00000402, 0x00100000, 0x04000002,
|
|
||||||
0x00100400, 0x04000400, 0x00000002, 0x04100000,
|
|
||||||
0x04000402, 0x00000000, 0x00000400, 0x00100002,
|
|
||||||
0x00000000, 0x04100002, 0x04100400, 0x00000400,
|
|
||||||
0x04000000, 0x04100402, 0x00100402, 0x00100000,
|
|
||||||
0x04100402, 0x00000002, 0x04000400, 0x00100402,
|
|
||||||
0x00100002, 0x00100400, 0x04100000, 0x04000402,
|
|
||||||
0x00000402, 0x04000000, 0x04000002, 0x04100400 ],
|
|
||||||
|
|
||||||
# nibble 4
|
|
||||||
[ 0x02000000, 0x00004000, 0x00000100, 0x02004108,
|
|
||||||
0x02004008, 0x02000100, 0x00004108, 0x02004000,
|
|
||||||
0x00004000, 0x00000008, 0x02000008, 0x00004100,
|
|
||||||
0x02000108, 0x02004008, 0x02004100, 0x00000000,
|
|
||||||
0x00004100, 0x02000000, 0x00004008, 0x00000108,
|
|
||||||
0x02000100, 0x00004108, 0x00000000, 0x02000008,
|
|
||||||
0x00000008, 0x02000108, 0x02004108, 0x00004008,
|
|
||||||
0x02004000, 0x00000100, 0x00000108, 0x02004100,
|
|
||||||
0x02004100, 0x02000108, 0x00004008, 0x02004000,
|
|
||||||
0x00004000, 0x00000008, 0x02000008, 0x02000100,
|
|
||||||
0x02000000, 0x00004100, 0x02004108, 0x00000000,
|
|
||||||
0x00004108, 0x02000000, 0x00000100, 0x00004008,
|
|
||||||
0x02000108, 0x00000100, 0x00000000, 0x02004108,
|
|
||||||
0x02004008, 0x02004100, 0x00000108, 0x00004000,
|
|
||||||
0x00004100, 0x02004008, 0x02000100, 0x00000108,
|
|
||||||
0x00000008, 0x00004108, 0x02004000, 0x02000008 ],
|
|
||||||
|
|
||||||
# nibble 5
|
|
||||||
[ 0x20000010, 0x00080010, 0x00000000, 0x20080800,
|
|
||||||
0x00080010, 0x00000800, 0x20000810, 0x00080000,
|
|
||||||
0x00000810, 0x20080810, 0x00080800, 0x20000000,
|
|
||||||
0x20000800, 0x20000010, 0x20080000, 0x00080810,
|
|
||||||
0x00080000, 0x20000810, 0x20080010, 0x00000000,
|
|
||||||
0x00000800, 0x00000010, 0x20080800, 0x20080010,
|
|
||||||
0x20080810, 0x20080000, 0x20000000, 0x00000810,
|
|
||||||
0x00000010, 0x00080800, 0x00080810, 0x20000800,
|
|
||||||
0x00000810, 0x20000000, 0x20000800, 0x00080810,
|
|
||||||
0x20080800, 0x00080010, 0x00000000, 0x20000800,
|
|
||||||
0x20000000, 0x00000800, 0x20080010, 0x00080000,
|
|
||||||
0x00080010, 0x20080810, 0x00080800, 0x00000010,
|
|
||||||
0x20080810, 0x00080800, 0x00080000, 0x20000810,
|
|
||||||
0x20000010, 0x20080000, 0x00080810, 0x00000000,
|
|
||||||
0x00000800, 0x20000010, 0x20000810, 0x20080800,
|
|
||||||
0x20080000, 0x00000810, 0x00000010, 0x20080010 ],
|
|
||||||
|
|
||||||
# nibble 6
|
|
||||||
[ 0x00001000, 0x00000080, 0x00400080, 0x00400001,
|
|
||||||
0x00401081, 0x00001001, 0x00001080, 0x00000000,
|
|
||||||
0x00400000, 0x00400081, 0x00000081, 0x00401000,
|
|
||||||
0x00000001, 0x00401080, 0x00401000, 0x00000081,
|
|
||||||
0x00400081, 0x00001000, 0x00001001, 0x00401081,
|
|
||||||
0x00000000, 0x00400080, 0x00400001, 0x00001080,
|
|
||||||
0x00401001, 0x00001081, 0x00401080, 0x00000001,
|
|
||||||
0x00001081, 0x00401001, 0x00000080, 0x00400000,
|
|
||||||
0x00001081, 0x00401000, 0x00401001, 0x00000081,
|
|
||||||
0x00001000, 0x00000080, 0x00400000, 0x00401001,
|
|
||||||
0x00400081, 0x00001081, 0x00001080, 0x00000000,
|
|
||||||
0x00000080, 0x00400001, 0x00000001, 0x00400080,
|
|
||||||
0x00000000, 0x00400081, 0x00400080, 0x00001080,
|
|
||||||
0x00000081, 0x00001000, 0x00401081, 0x00400000,
|
|
||||||
0x00401080, 0x00000001, 0x00001001, 0x00401081,
|
|
||||||
0x00400001, 0x00401080, 0x00401000, 0x00001001 ],
|
|
||||||
|
|
||||||
# nibble 7
|
|
||||||
[ 0x08200020, 0x08208000, 0x00008020, 0x00000000,
|
|
||||||
0x08008000, 0x00200020, 0x08200000, 0x08208020,
|
|
||||||
0x00000020, 0x08000000, 0x00208000, 0x00008020,
|
|
||||||
0x00208020, 0x08008020, 0x08000020, 0x08200000,
|
|
||||||
0x00008000, 0x00208020, 0x00200020, 0x08008000,
|
|
||||||
0x08208020, 0x08000020, 0x00000000, 0x00208000,
|
|
||||||
0x08000000, 0x00200000, 0x08008020, 0x08200020,
|
|
||||||
0x00200000, 0x00008000, 0x08208000, 0x00000020,
|
|
||||||
0x00200000, 0x00008000, 0x08000020, 0x08208020,
|
|
||||||
0x00008020, 0x08000000, 0x00000000, 0x00208000,
|
|
||||||
0x08200020, 0x08008020, 0x08008000, 0x00200020,
|
|
||||||
0x08208000, 0x00000020, 0x00200020, 0x08008000,
|
|
||||||
0x08208020, 0x00200000, 0x08200000, 0x08000020,
|
|
||||||
0x00208000, 0x00008020, 0x08008020, 0x08200000,
|
|
||||||
0x00000020, 0x08208000, 0x00208020, 0x00000000,
|
|
||||||
0x08000000, 0x08200020, 0x00008000, 0x00208020 ] )
|
|
||||||
|
|
||||||
_skb = (
|
|
||||||
# for C bits (numbered as per FIPS 46) 1 2 3 4 5 6
|
|
||||||
[ 0x00000000, 0x00000010, 0x20000000, 0x20000010,
|
|
||||||
0x00010000, 0x00010010, 0x20010000, 0x20010010,
|
|
||||||
0x00000800, 0x00000810, 0x20000800, 0x20000810,
|
|
||||||
0x00010800, 0x00010810, 0x20010800, 0x20010810,
|
|
||||||
0x00000020, 0x00000030, 0x20000020, 0x20000030,
|
|
||||||
0x00010020, 0x00010030, 0x20010020, 0x20010030,
|
|
||||||
0x00000820, 0x00000830, 0x20000820, 0x20000830,
|
|
||||||
0x00010820, 0x00010830, 0x20010820, 0x20010830,
|
|
||||||
0x00080000, 0x00080010, 0x20080000, 0x20080010,
|
|
||||||
0x00090000, 0x00090010, 0x20090000, 0x20090010,
|
|
||||||
0x00080800, 0x00080810, 0x20080800, 0x20080810,
|
|
||||||
0x00090800, 0x00090810, 0x20090800, 0x20090810,
|
|
||||||
0x00080020, 0x00080030, 0x20080020, 0x20080030,
|
|
||||||
0x00090020, 0x00090030, 0x20090020, 0x20090030,
|
|
||||||
0x00080820, 0x00080830, 0x20080820, 0x20080830,
|
|
||||||
0x00090820, 0x00090830, 0x20090820, 0x20090830 ],
|
|
||||||
|
|
||||||
# for C bits (numbered as per FIPS 46) 7 8 10 11 12 13
|
|
||||||
[ 0x00000000, 0x02000000, 0x00002000, 0x02002000,
|
|
||||||
0x00200000, 0x02200000, 0x00202000, 0x02202000,
|
|
||||||
0x00000004, 0x02000004, 0x00002004, 0x02002004,
|
|
||||||
0x00200004, 0x02200004, 0x00202004, 0x02202004,
|
|
||||||
0x00000400, 0x02000400, 0x00002400, 0x02002400,
|
|
||||||
0x00200400, 0x02200400, 0x00202400, 0x02202400,
|
|
||||||
0x00000404, 0x02000404, 0x00002404, 0x02002404,
|
|
||||||
0x00200404, 0x02200404, 0x00202404, 0x02202404,
|
|
||||||
0x10000000, 0x12000000, 0x10002000, 0x12002000,
|
|
||||||
0x10200000, 0x12200000, 0x10202000, 0x12202000,
|
|
||||||
0x10000004, 0x12000004, 0x10002004, 0x12002004,
|
|
||||||
0x10200004, 0x12200004, 0x10202004, 0x12202004,
|
|
||||||
0x10000400, 0x12000400, 0x10002400, 0x12002400,
|
|
||||||
0x10200400, 0x12200400, 0x10202400, 0x12202400,
|
|
||||||
0x10000404, 0x12000404, 0x10002404, 0x12002404,
|
|
||||||
0x10200404, 0x12200404, 0x10202404, 0x12202404 ],
|
|
||||||
|
|
||||||
# for C bits (numbered as per FIPS 46) 14 15 16 17 19 20
|
|
||||||
[ 0x00000000, 0x00000001, 0x00040000, 0x00040001,
|
|
||||||
0x01000000, 0x01000001, 0x01040000, 0x01040001,
|
|
||||||
0x00000002, 0x00000003, 0x00040002, 0x00040003,
|
|
||||||
0x01000002, 0x01000003, 0x01040002, 0x01040003,
|
|
||||||
0x00000200, 0x00000201, 0x00040200, 0x00040201,
|
|
||||||
0x01000200, 0x01000201, 0x01040200, 0x01040201,
|
|
||||||
0x00000202, 0x00000203, 0x00040202, 0x00040203,
|
|
||||||
0x01000202, 0x01000203, 0x01040202, 0x01040203,
|
|
||||||
0x08000000, 0x08000001, 0x08040000, 0x08040001,
|
|
||||||
0x09000000, 0x09000001, 0x09040000, 0x09040001,
|
|
||||||
0x08000002, 0x08000003, 0x08040002, 0x08040003,
|
|
||||||
0x09000002, 0x09000003, 0x09040002, 0x09040003,
|
|
||||||
0x08000200, 0x08000201, 0x08040200, 0x08040201,
|
|
||||||
0x09000200, 0x09000201, 0x09040200, 0x09040201,
|
|
||||||
0x08000202, 0x08000203, 0x08040202, 0x08040203,
|
|
||||||
0x09000202, 0x09000203, 0x09040202, 0x09040203 ],
|
|
||||||
|
|
||||||
# for C bits (numbered as per FIPS 46) 21 23 24 26 27 28
|
|
||||||
[ 0x00000000, 0x00100000, 0x00000100, 0x00100100,
|
|
||||||
0x00000008, 0x00100008, 0x00000108, 0x00100108,
|
|
||||||
0x00001000, 0x00101000, 0x00001100, 0x00101100,
|
|
||||||
0x00001008, 0x00101008, 0x00001108, 0x00101108,
|
|
||||||
0x04000000, 0x04100000, 0x04000100, 0x04100100,
|
|
||||||
0x04000008, 0x04100008, 0x04000108, 0x04100108,
|
|
||||||
0x04001000, 0x04101000, 0x04001100, 0x04101100,
|
|
||||||
0x04001008, 0x04101008, 0x04001108, 0x04101108,
|
|
||||||
0x00020000, 0x00120000, 0x00020100, 0x00120100,
|
|
||||||
0x00020008, 0x00120008, 0x00020108, 0x00120108,
|
|
||||||
0x00021000, 0x00121000, 0x00021100, 0x00121100,
|
|
||||||
0x00021008, 0x00121008, 0x00021108, 0x00121108,
|
|
||||||
0x04020000, 0x04120000, 0x04020100, 0x04120100,
|
|
||||||
0x04020008, 0x04120008, 0x04020108, 0x04120108,
|
|
||||||
0x04021000, 0x04121000, 0x04021100, 0x04121100,
|
|
||||||
0x04021008, 0x04121008, 0x04021108, 0x04121108 ],
|
|
||||||
|
|
||||||
# for D bits (numbered as per FIPS 46) 1 2 3 4 5 6
|
|
||||||
[ 0x00000000, 0x10000000, 0x00010000, 0x10010000,
|
|
||||||
0x00000004, 0x10000004, 0x00010004, 0x10010004,
|
|
||||||
0x20000000, 0x30000000, 0x20010000, 0x30010000,
|
|
||||||
0x20000004, 0x30000004, 0x20010004, 0x30010004,
|
|
||||||
0x00100000, 0x10100000, 0x00110000, 0x10110000,
|
|
||||||
0x00100004, 0x10100004, 0x00110004, 0x10110004,
|
|
||||||
0x20100000, 0x30100000, 0x20110000, 0x30110000,
|
|
||||||
0x20100004, 0x30100004, 0x20110004, 0x30110004,
|
|
||||||
0x00001000, 0x10001000, 0x00011000, 0x10011000,
|
|
||||||
0x00001004, 0x10001004, 0x00011004, 0x10011004,
|
|
||||||
0x20001000, 0x30001000, 0x20011000, 0x30011000,
|
|
||||||
0x20001004, 0x30001004, 0x20011004, 0x30011004,
|
|
||||||
0x00101000, 0x10101000, 0x00111000, 0x10111000,
|
|
||||||
0x00101004, 0x10101004, 0x00111004, 0x10111004,
|
|
||||||
0x20101000, 0x30101000, 0x20111000, 0x30111000,
|
|
||||||
0x20101004, 0x30101004, 0x20111004, 0x30111004 ],
|
|
||||||
|
|
||||||
# for D bits (numbered as per FIPS 46) 8 9 11 12 13 14
|
|
||||||
[ 0x00000000, 0x08000000, 0x00000008, 0x08000008,
|
|
||||||
0x00000400, 0x08000400, 0x00000408, 0x08000408,
|
|
||||||
0x00020000, 0x08020000, 0x00020008, 0x08020008,
|
|
||||||
0x00020400, 0x08020400, 0x00020408, 0x08020408,
|
|
||||||
0x00000001, 0x08000001, 0x00000009, 0x08000009,
|
|
||||||
0x00000401, 0x08000401, 0x00000409, 0x08000409,
|
|
||||||
0x00020001, 0x08020001, 0x00020009, 0x08020009,
|
|
||||||
0x00020401, 0x08020401, 0x00020409, 0x08020409,
|
|
||||||
0x02000000, 0x0A000000, 0x02000008, 0x0A000008,
|
|
||||||
0x02000400, 0x0A000400, 0x02000408, 0x0A000408,
|
|
||||||
0x02020000, 0x0A020000, 0x02020008, 0x0A020008,
|
|
||||||
0x02020400, 0x0A020400, 0x02020408, 0x0A020408,
|
|
||||||
0x02000001, 0x0A000001, 0x02000009, 0x0A000009,
|
|
||||||
0x02000401, 0x0A000401, 0x02000409, 0x0A000409,
|
|
||||||
0x02020001, 0x0A020001, 0x02020009, 0x0A020009,
|
|
||||||
0x02020401, 0x0A020401, 0x02020409, 0x0A020409 ],
|
|
||||||
|
|
||||||
# for D bits (numbered as per FIPS 46) 16 17 18 19 20 21
|
|
||||||
[ 0x00000000, 0x00000100, 0x00080000, 0x00080100,
|
|
||||||
0x01000000, 0x01000100, 0x01080000, 0x01080100,
|
|
||||||
0x00000010, 0x00000110, 0x00080010, 0x00080110,
|
|
||||||
0x01000010, 0x01000110, 0x01080010, 0x01080110,
|
|
||||||
0x00200000, 0x00200100, 0x00280000, 0x00280100,
|
|
||||||
0x01200000, 0x01200100, 0x01280000, 0x01280100,
|
|
||||||
0x00200010, 0x00200110, 0x00280010, 0x00280110,
|
|
||||||
0x01200010, 0x01200110, 0x01280010, 0x01280110,
|
|
||||||
0x00000200, 0x00000300, 0x00080200, 0x00080300,
|
|
||||||
0x01000200, 0x01000300, 0x01080200, 0x01080300,
|
|
||||||
0x00000210, 0x00000310, 0x00080210, 0x00080310,
|
|
||||||
0x01000210, 0x01000310, 0x01080210, 0x01080310,
|
|
||||||
0x00200200, 0x00200300, 0x00280200, 0x00280300,
|
|
||||||
0x01200200, 0x01200300, 0x01280200, 0x01280300,
|
|
||||||
0x00200210, 0x00200310, 0x00280210, 0x00280310,
|
|
||||||
0x01200210, 0x01200310, 0x01280210, 0x01280310 ],
|
|
||||||
|
|
||||||
# for D bits (numbered as per FIPS 46) 22 23 24 25 27 28
|
|
||||||
[ 0x00000000, 0x04000000, 0x00040000, 0x04040000,
|
|
||||||
0x00000002, 0x04000002, 0x00040002, 0x04040002,
|
|
||||||
0x00002000, 0x04002000, 0x00042000, 0x04042000,
|
|
||||||
0x00002002, 0x04002002, 0x00042002, 0x04042002,
|
|
||||||
0x00000020, 0x04000020, 0x00040020, 0x04040020,
|
|
||||||
0x00000022, 0x04000022, 0x00040022, 0x04040022,
|
|
||||||
0x00002020, 0x04002020, 0x00042020, 0x04042020,
|
|
||||||
0x00002022, 0x04002022, 0x00042022, 0x04042022,
|
|
||||||
0x00000800, 0x04000800, 0x00040800, 0x04040800,
|
|
||||||
0x00000802, 0x04000802, 0x00040802, 0x04040802,
|
|
||||||
0x00002800, 0x04002800, 0x00042800, 0x04042800,
|
|
||||||
0x00002802, 0x04002802, 0x00042802, 0x04042802,
|
|
||||||
0x00000820, 0x04000820, 0x00040820, 0x04040820,
|
|
||||||
0x00000822, 0x04000822, 0x00040822, 0x04040822,
|
|
||||||
0x00002820, 0x04002820, 0x00042820, 0x04042820,
|
|
||||||
0x00002822, 0x04002822, 0x00042822, 0x04042822 ] )
|
|
||||||
|
|
||||||
_shifts2 = (0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0)
|
|
||||||
|
|
||||||
_con_salt = [
|
|
||||||
0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,
|
|
||||||
0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,0xE0,0xE1,
|
|
||||||
0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,
|
|
||||||
0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,
|
|
||||||
0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,
|
|
||||||
0xFA,0xFB,0xFC,0xFD,0xFE,0xFF,0x00,0x01,
|
|
||||||
0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,
|
|
||||||
0x0A,0x0B,0x05,0x06,0x07,0x08,0x09,0x0A,
|
|
||||||
0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12,
|
|
||||||
0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,
|
|
||||||
0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,
|
|
||||||
0x23,0x24,0x25,0x20,0x21,0x22,0x23,0x24,
|
|
||||||
0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,
|
|
||||||
0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0x34,
|
|
||||||
0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,
|
|
||||||
0x3D,0x3E,0x3F,0x40,0x41,0x42,0x43,0x44 ]
|
|
||||||
|
|
||||||
_cov_2char = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
|
|
||||||
|
|
||||||
|
|
||||||
def _HPERM_OP(a):
|
|
||||||
"""Clever bit manipulation."""
|
|
||||||
t = ((a << 18) ^ a) & 0xcccc0000
|
|
||||||
return a ^ t ^ ((t >> 18) & 0x3fff)
|
|
||||||
|
|
||||||
def _PERM_OP(a,b,n,m):
|
|
||||||
"""Cleverer bit manipulation."""
|
|
||||||
t = ((a >> n) ^ b) & m
|
|
||||||
b = b ^ t
|
|
||||||
a = a ^ (t << n)
|
|
||||||
return a,b
|
|
||||||
|
|
||||||
|
|
||||||
def _set_key(password):
|
|
||||||
"""Generate DES key schedule from ASCII password."""
|
|
||||||
|
|
||||||
c,d = struct.unpack('<ii', password)
|
|
||||||
c = (c & 0x7f7f7f7f) << 1
|
|
||||||
d = (d & 0x7f7f7f7f) << 1
|
|
||||||
|
|
||||||
d,c = _PERM_OP(d,c,4,0x0f0f0f0f)
|
|
||||||
c = _HPERM_OP(c)
|
|
||||||
d = _HPERM_OP(d)
|
|
||||||
d,c = _PERM_OP(d,c,1,0x55555555)
|
|
||||||
c,d = _PERM_OP(c,d,8,0x00ff00ff)
|
|
||||||
d,c = _PERM_OP(d,c,1,0x55555555)
|
|
||||||
|
|
||||||
# Any sign-extended bits are masked off.
|
|
||||||
d = (((d & 0x000000ff) << 16) | (d & 0x0000ff00) |
|
|
||||||
((d & 0x00ff0000) >> 16) | ((c >> 4) & 0x0f000000))
|
|
||||||
c = c & 0x0fffffff
|
|
||||||
|
|
||||||
# Copy globals into local variables for loop.
|
|
||||||
shifts2 = _shifts2
|
|
||||||
skbc0, skbc1, skbc2, skbc3, skbd0, skbd1, skbd2, skbd3 = _skb
|
|
||||||
|
|
||||||
k = [0] * (_ITERATIONS * 2)
|
|
||||||
|
|
||||||
for i in range(_ITERATIONS):
|
|
||||||
# Only operates on top 28 bits.
|
|
||||||
if shifts2[i]:
|
|
||||||
c = (c >> 2) | (c << 26)
|
|
||||||
d = (d >> 2) | (d << 26)
|
|
||||||
else:
|
|
||||||
c = (c >> 1) | (c << 27)
|
|
||||||
d = (d >> 1) | (d << 27)
|
|
||||||
c = c & 0x0fffffff
|
|
||||||
d = d & 0x0fffffff
|
|
||||||
|
|
||||||
s = ( skbc0[ c & 0x3f ] |
|
|
||||||
skbc1[((c>> 6) & 0x03) | ((c>> 7) & 0x3c)] |
|
|
||||||
skbc2[((c>>13) & 0x0f) | ((c>>14) & 0x30)] |
|
|
||||||
skbc3[((c>>20) & 0x01) |
|
|
||||||
((c>>21) & 0x06) | ((c>>22) & 0x38)] )
|
|
||||||
|
|
||||||
t = ( skbd0[ d & 0x3f ] |
|
|
||||||
skbd1[((d>> 7) & 0x03) | ((d>> 8) & 0x3c)] |
|
|
||||||
skbd2[((d>>15) & 0x3f) ] |
|
|
||||||
skbd3[((d>>21) & 0x0f) | ((d>>22) & 0x30)] )
|
|
||||||
|
|
||||||
k[2*i] = ((t << 16) | (s & 0x0000ffff)) & 0xffffffff
|
|
||||||
s = (s >> 16) | (t & 0xffff0000)
|
|
||||||
|
|
||||||
# Top bit of s may be 1.
|
|
||||||
s = (s << 4) | ((s >> 28) & 0x0f)
|
|
||||||
k[2*i + 1] = s & 0xffffffff
|
|
||||||
|
|
||||||
return k
|
|
||||||
|
|
||||||
|
|
||||||
def _body(ks, E0, E1):
|
|
||||||
"""Use the key schedule ks and salt E0, E1 to create the password hash."""
|
|
||||||
|
|
||||||
# Copy global variable into locals for loop.
|
|
||||||
SP0, SP1, SP2, SP3, SP4, SP5, SP6, SP7 = _SPtrans
|
|
||||||
|
|
||||||
inner = range(0, _ITERATIONS*2, 2)
|
|
||||||
l = r = 0
|
|
||||||
for j in range(25):
|
|
||||||
l,r = r,l
|
|
||||||
for i in inner:
|
|
||||||
t = r ^ ((r >> 16) & 0xffff)
|
|
||||||
u = t & E0
|
|
||||||
t = t & E1
|
|
||||||
u = u ^ (u << 16) ^ r ^ ks[i]
|
|
||||||
t = t ^ (t << 16) ^ r ^ ks[i+1]
|
|
||||||
t = ((t >> 4) & 0x0fffffff) | (t << 28)
|
|
||||||
|
|
||||||
l,r = r,(SP1[(t ) & 0x3f] ^ SP3[(t>> 8) & 0x3f] ^
|
|
||||||
SP5[(t>>16) & 0x3f] ^ SP7[(t>>24) & 0x3f] ^
|
|
||||||
SP0[(u ) & 0x3f] ^ SP2[(u>> 8) & 0x3f] ^
|
|
||||||
SP4[(u>>16) & 0x3f] ^ SP6[(u>>24) & 0x3f] ^ l)
|
|
||||||
|
|
||||||
l = ((l >> 1) & 0x7fffffff) | ((l & 0x1) << 31)
|
|
||||||
r = ((r >> 1) & 0x7fffffff) | ((r & 0x1) << 31)
|
|
||||||
|
|
||||||
r,l = _PERM_OP(r, l, 1, 0x55555555)
|
|
||||||
l,r = _PERM_OP(l, r, 8, 0x00ff00ff)
|
|
||||||
r,l = _PERM_OP(r, l, 2, 0x33333333)
|
|
||||||
l,r = _PERM_OP(l, r, 16, 0x0000ffff)
|
|
||||||
r,l = _PERM_OP(r, l, 4, 0x0f0f0f0f)
|
|
||||||
|
|
||||||
return l,r
|
|
||||||
|
|
||||||
|
|
||||||
def crypt(password, salt):
|
|
||||||
"""Generate an encrypted hash from the passed password. If the password
|
|
||||||
is longer than eight characters, only the first eight will be used.
|
|
||||||
|
|
||||||
The first two characters of the salt are used to modify the encryption
|
|
||||||
algorithm used to generate in the hash in one of 4096 different ways.
|
|
||||||
The characters for the salt must be alphanumeric, '.' or '/'.
|
|
||||||
|
|
||||||
The returned hash begins with the two characters of the salt, and
|
|
||||||
should be passed as the salt to verify the password.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
>>> from fcrypt import crypt
|
|
||||||
>>> password = 'AlOtBsOl'
|
|
||||||
>>> salt = 'cE'
|
|
||||||
>>> hash = crypt(password, salt)
|
|
||||||
>>> hash
|
|
||||||
'cEpWz5IUCShqM'
|
|
||||||
>>> crypt(password, hash) == hash
|
|
||||||
1
|
|
||||||
>>> crypt('IaLaIoK', hash) == hash
|
|
||||||
0
|
|
||||||
|
|
||||||
In practice, you would read the password using something like the
|
|
||||||
getpass module, and generate the salt randomly:
|
|
||||||
|
|
||||||
>>> import random, string
|
|
||||||
>>> saltchars = string.letters + string.digits + './'
|
|
||||||
>>> salt = random.choice(saltchars) + random.choice(saltchars)
|
|
||||||
"""
|
|
||||||
|
|
||||||
if len(salt) < 2:
|
|
||||||
salt = salt + 'AA'
|
|
||||||
|
|
||||||
Eswap0 = _con_salt[ord(salt[0])]
|
|
||||||
Eswap1 = _con_salt[ord(salt[1])] << 4
|
|
||||||
|
|
||||||
ks = _set_key((password + '\0\0\0\0\0\0\0\0')[:8])
|
|
||||||
out1,out2 = _body(ks, Eswap0, Eswap1)
|
|
||||||
|
|
||||||
# Convert numbers to big-endian...
|
|
||||||
be1, be2 = struct.unpack('>ii', struct.pack('<ii', out1, out2))
|
|
||||||
# then extract 24-bit subsets.
|
|
||||||
b24 = [(be1 >> 8) & 0xffffff,
|
|
||||||
((be1 << 16) & 0xff0000) | ((be2 >> 16) & 0xffff),
|
|
||||||
(be2 << 8) & 0xffff00]
|
|
||||||
|
|
||||||
# Convert to ASCII encoding, 4 characters for each 24 bits.
|
|
||||||
res = [salt[0], salt[1]]
|
|
||||||
for b in b24:
|
|
||||||
for i in range(18, -6, -6):
|
|
||||||
res.append(_cov_2char[(b >> i) & 0x3f])
|
|
||||||
|
|
||||||
return string.join(res[:13], '')
|
|
||||||
|
|
||||||
def _test():
|
|
||||||
"""Run doctest on fcrypt module."""
|
|
||||||
import doctest, fcrypt
|
|
||||||
return doctest.testmod(fcrypt)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
_test()
|
|
@ -1,22 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
# distutils setup script for fcrypt.
|
|
||||||
#
|
|
||||||
# Copyright (C) 2000, 2001 Carey Evans <careye@spamcop.net>
|
|
||||||
|
|
||||||
from distutils.core import setup
|
|
||||||
|
|
||||||
setup( name = 'fcrypt',
|
|
||||||
version = '1.2',
|
|
||||||
description = 'The Unix password crypt function.',
|
|
||||||
author = 'Carey Evans',
|
|
||||||
author_email = 'careye@spamcop.net',
|
|
||||||
url = 'http://home.clear.net.nz/pages/c.evans/sw/',
|
|
||||||
licence = 'BSD',
|
|
||||||
long_description = """\
|
|
||||||
A pure Python implementation of the Unix DES password crypt function,
|
|
||||||
based on Eric Young's fcrypt.c. It works with any version of Python
|
|
||||||
from version 1.5 or higher, and because it's pure Python it doesn't
|
|
||||||
need a C compiler to install it.""",
|
|
||||||
|
|
||||||
py_modules = ['fcrypt'] )
|
|
@ -1,44 +0,0 @@
|
|||||||
#
|
|
||||||
# Extensible User Folder
|
|
||||||
#
|
|
||||||
# (C) Copyright 2000-2004 The Internet (Aust) Pty Ltd
|
|
||||||
# ACN: 082 081 472 ABN: 83 082 081 472
|
|
||||||
# All Rights Reserved
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
||||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
# SUCH DAMAGE.
|
|
||||||
#
|
|
||||||
# Author: Andrew Milton <akm@theinternet.com.au>
|
|
||||||
# $Id: pass_crypt.py,v 1.3 2004/11/18 09:24:46 akm Exp $
|
|
||||||
|
|
||||||
from Products.exUserFolder.exUserFolder import exUserFolder
|
|
||||||
from Products.exUserFolder.Plugins import CryptoPluginRegister
|
|
||||||
|
|
||||||
try:
|
|
||||||
from crypt import crypt
|
|
||||||
except:
|
|
||||||
from fcrypt.fcrypt import crypt
|
|
||||||
|
|
||||||
|
|
||||||
def cryptPassword(authSource, username, password):
|
|
||||||
u = authSource.listOneUser(username)
|
|
||||||
if not u:
|
|
||||||
salt = username[:2]
|
|
||||||
else:
|
|
||||||
salt=u[0]['password'][:2]
|
|
||||||
|
|
||||||
secret = crypt(password, salt)
|
|
||||||
return secret
|
|
||||||
|
|
||||||
|
|
||||||
CryptPlugin=CryptoPluginRegister('Crypt', 'crypt', 'Crypt', cryptPassword)
|
|
||||||
exUserFolder.cryptoSources['Crypt']=CryptPlugin
|
|
@ -1,47 +0,0 @@
|
|||||||
#
|
|
||||||
# Extensible User Folder
|
|
||||||
#
|
|
||||||
# (C) Copyright 2000-2004 The Internet (Aust) Pty Ltd
|
|
||||||
# ACN: 082 081 472 ABN: 83 082 081 472
|
|
||||||
# All Rights Reserved
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
||||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
# SUCH DAMAGE.
|
|
||||||
#
|
|
||||||
# Author: Andrew Milton <akm@theinternet.com.au>
|
|
||||||
# $Id: pass_md5.py,v 1.1 2004/11/10 14:15:52 akm Exp $
|
|
||||||
|
|
||||||
import md5, base64, string
|
|
||||||
|
|
||||||
from Products.exUserFolder.exUserFolder import exUserFolder
|
|
||||||
from Products.exUserFolder.Plugins import CryptoPluginRegister
|
|
||||||
|
|
||||||
# Simple digest
|
|
||||||
def cryptPassword(authSource, username, password):
|
|
||||||
digest = md5.new()
|
|
||||||
digest.update(password)
|
|
||||||
digest = digest.digest()
|
|
||||||
secret = string.strip(base64.encodestring(digest))
|
|
||||||
return secret
|
|
||||||
|
|
||||||
# Digest includes username
|
|
||||||
# So two passwords for different users hash differently
|
|
||||||
def cryptPassword2(authSource, username, password):
|
|
||||||
newPass = username+':'+password
|
|
||||||
return cryptPassword(authSource, username, newPass)
|
|
||||||
|
|
||||||
|
|
||||||
MD5Plugin1=CryptoPluginRegister('MD51', 'MD5', 'MD5 Password Only', cryptPassword)
|
|
||||||
exUserFolder.cryptoSources['MD51']=MD5Plugin1
|
|
||||||
|
|
||||||
MD5Plugin2=CryptoPluginRegister('MD52', 'MD5', 'MD5 Username + Password', cryptPassword2)
|
|
||||||
exUserFolder.cryptoSources['MD52']=MD5Plugin2
|
|
@ -1,31 +0,0 @@
|
|||||||
#
|
|
||||||
# Extensible User Folder
|
|
||||||
#
|
|
||||||
# (C) Copyright 2000-2004 The Internet (Aust) Pty Ltd
|
|
||||||
# ACN: 082 081 472 ABN: 83 082 081 472
|
|
||||||
# All Rights Reserved
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
||||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
# SUCH DAMAGE.
|
|
||||||
#
|
|
||||||
# Author: Andrew Milton <akm@theinternet.com.au>
|
|
||||||
# $Id: pass_plain.py,v 1.1 2004/11/10 14:15:52 akm Exp $
|
|
||||||
|
|
||||||
from Products.exUserFolder.exUserFolder import exUserFolder
|
|
||||||
from Products.exUserFolder.Plugins import CryptoPluginRegister
|
|
||||||
|
|
||||||
# Simple digest
|
|
||||||
def cryptPassword(authSource, username, password):
|
|
||||||
return password
|
|
||||||
|
|
||||||
PlainPlugin=CryptoPluginRegister('Plaintext', 'Plaintext', 'No Encryption', cryptPassword)
|
|
||||||
exUserFolder.cryptoSources['Plaintext']=PlainPlugin
|
|
@ -1,41 +0,0 @@
|
|||||||
#
|
|
||||||
# Extensible User Folder
|
|
||||||
#
|
|
||||||
# (C) Copyright 2000-2004 The Internet (Aust) Pty Ltd
|
|
||||||
# ACN: 082 081 472 ABN: 83 082 081 472
|
|
||||||
# All Rights Reserved
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
||||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
# SUCH DAMAGE.
|
|
||||||
#
|
|
||||||
# Author: Andrew Milton <akm@theinternet.com.au>
|
|
||||||
# $Id: pass_sha.py,v 1.1 2004/11/10 14:15:52 akm Exp $
|
|
||||||
|
|
||||||
import sha
|
|
||||||
from base64 import encodestring
|
|
||||||
|
|
||||||
from Products.exUserFolder.exUserFolder import exUserFolder
|
|
||||||
from Products.exUserFolder.Plugins import CryptoPluginRegister
|
|
||||||
|
|
||||||
|
|
||||||
def cryptPassword(authSource, username, password):
|
|
||||||
return encodestring(sha.new(password).digest())
|
|
||||||
|
|
||||||
def cryptPassword2(authSource, username, password):
|
|
||||||
newPass = username+':'+password
|
|
||||||
return cryptPassword(authSource, username, newPass)
|
|
||||||
|
|
||||||
SHAPlugin1=CryptoPluginRegister('SHA1', 'SHA', 'SHA Password Only', cryptPassword)
|
|
||||||
exUserFolder.cryptoSources['SHA1']=SHAPlugin1
|
|
||||||
|
|
||||||
SHAPlugin2=CryptoPluginRegister('SHA2', 'SHA', 'SHA Username + Password', cryptPassword2)
|
|
||||||
exUserFolder.cryptoSources['SHA2']=SHAPlugin2
|
|
@ -1,4 +0,0 @@
|
|||||||
*.pyc
|
|
||||||
*.pyo
|
|
||||||
*~
|
|
||||||
.*.swp
|
|
@ -1,20 +0,0 @@
|
|||||||
# This script interrogates the old-skool NuxUserGroups_support_branch
|
|
||||||
# group structure and outputs a tab-delimited file you can send to
|
|
||||||
# loadOldGroups. Just in case anyone is using it. :-)
|
|
||||||
#
|
|
||||||
# Matt Behrens <matt.behrens@kohler.com>
|
|
||||||
|
|
||||||
def getOldGroups(self):
|
|
||||||
"Reconstruct a group list from the old-style _groups property"
|
|
||||||
from string import join
|
|
||||||
props = self.currentPropSource.userProperties
|
|
||||||
groups = {}
|
|
||||||
for username in props.keys():
|
|
||||||
for groupname in props[username].getProperty('_groups', ()):
|
|
||||||
if not groups.has_key(groupname):
|
|
||||||
groups[groupname] = []
|
|
||||||
groups[groupname].append(username)
|
|
||||||
out = ''
|
|
||||||
for groupname in groups.keys():
|
|
||||||
out = out + '%s %s\n' % (groupname, join(groups[groupname], ' '))
|
|
||||||
return out
|
|
@ -1,26 +0,0 @@
|
|||||||
# This takes 'old_groups.txt' from var (create it using getOldGroups)
|
|
||||||
# and sets up all the groups therein using NuxUserGroups calls. This
|
|
||||||
# will load a group source if you need to do such a thing.
|
|
||||||
#
|
|
||||||
# Matt Behrens <matt.behrens@kohler.com>
|
|
||||||
|
|
||||||
def loadOldGroups(self):
|
|
||||||
from os.path import join as pathJoin
|
|
||||||
from string import split, strip
|
|
||||||
|
|
||||||
groups_file = open(pathJoin(CLIENT_HOME, 'old_groups.txt'), 'r')
|
|
||||||
out = ''
|
|
||||||
for group_line in groups_file.readlines():
|
|
||||||
group_line_elements = split(strip(group_line), ' ')
|
|
||||||
group_name = group_line_elements[0]
|
|
||||||
group_members = group_line_elements[1:]
|
|
||||||
|
|
||||||
if self.getGroupById(group_name, default=None) is None:
|
|
||||||
out = out + 'adding group %s\n' % group_name
|
|
||||||
self.userFolderAddGroup(group_name)
|
|
||||||
|
|
||||||
out = out + 'setting group %s membership to %s\n' % (group_name, group_members)
|
|
||||||
self.setUsersOfGroup(group_members, group_name)
|
|
||||||
|
|
||||||
return out
|
|
||||||
|
|
@ -1,140 +0,0 @@
|
|||||||
# (C) Copyright 2000,2001 The Internet (Aust) Pty Ltd
|
|
||||||
# ACN: 082 081 472 ABN: 83 082 081 472
|
|
||||||
# All Rights Reserved
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
||||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
# SUCH DAMAGE.
|
|
||||||
#
|
|
||||||
# Author: Andrew Milton <akm@theinternet.com.au>
|
|
||||||
# $Id: usAuthSourceMethods.py,v 1.3 2001/12/01 08:40:04 akm Exp $
|
|
||||||
#
|
|
||||||
########################################################################
|
|
||||||
#
|
|
||||||
# This is an example of an Extension Module to provide User Supplied
|
|
||||||
# Authentication Methods.
|
|
||||||
#
|
|
||||||
# It mimics the behaviour of the pgAuthSource Module, and the sql queries
|
|
||||||
# Used here would be added as ZSQLMethods in the usAuthSource Folder.
|
|
||||||
# (you can basically cut and paste them from the bottom of this .py file
|
|
||||||
# into the ZSQL Method Template Area
|
|
||||||
#
|
|
||||||
# It's not complete, but, you do get the idea...
|
|
||||||
#
|
|
||||||
# Each function becomes usFunctionName
|
|
||||||
#
|
|
||||||
# e.g. listOneUser -> usListOneUser
|
|
||||||
#
|
|
||||||
import string
|
|
||||||
from crypt import crypt
|
|
||||||
|
|
||||||
def listOneUser(self,username):
|
|
||||||
users = []
|
|
||||||
result=self.sqlListOneUser(username=username)
|
|
||||||
for n in result:
|
|
||||||
username=sqlattr(n,'username')
|
|
||||||
password=sqlattr(n,'password')
|
|
||||||
roles=string.split(sqlattr(n,'roles'))
|
|
||||||
N={'username':username, 'password':password, 'roles':roles}
|
|
||||||
users.append(N)
|
|
||||||
return users
|
|
||||||
|
|
||||||
def listUsers(self):
|
|
||||||
"""Returns a list of user names or [] if no users exist"""
|
|
||||||
users = []
|
|
||||||
result=self.sqlListUsers()
|
|
||||||
for n in result:
|
|
||||||
username=sqlattr(n,'username')
|
|
||||||
N={'username':username}
|
|
||||||
users.append(N)
|
|
||||||
return users
|
|
||||||
|
|
||||||
def getUsers(self):
|
|
||||||
"""Return a list of user objects or [] if no users exist"""
|
|
||||||
data=[]
|
|
||||||
try: items=self.listusers()
|
|
||||||
except: return data
|
|
||||||
for people in items:
|
|
||||||
roles=string.split(people['roles'],',')
|
|
||||||
user=User(people['username'], roles, '')
|
|
||||||
data.append(user)
|
|
||||||
return data
|
|
||||||
|
|
||||||
def cryptPassword(self, username, password):
|
|
||||||
salt =username[:2]
|
|
||||||
secret = crypt(password, salt)
|
|
||||||
return secret
|
|
||||||
|
|
||||||
def deleteUsers(self, userids):
|
|
||||||
for uid in userids:
|
|
||||||
self.sqlDeleteOneUser(userid=uid)
|
|
||||||
|
|
||||||
|
|
||||||
# Helper Functions...
|
|
||||||
from string import upper, lower
|
|
||||||
import Missing
|
|
||||||
mt=type(Missing.Value)
|
|
||||||
|
|
||||||
def typeconv(val):
|
|
||||||
if type(val)==mt:
|
|
||||||
return ''
|
|
||||||
return val
|
|
||||||
|
|
||||||
def sqlattr(ob, attr):
|
|
||||||
name=attr
|
|
||||||
if hasattr(ob, attr):
|
|
||||||
return typeconv(getattr(ob, attr))
|
|
||||||
attr=upper(attr)
|
|
||||||
if hasattr(ob, attr):
|
|
||||||
return typeconv(getattr(ob, attr))
|
|
||||||
attr=lower(attr)
|
|
||||||
if hasattr(ob, attr):
|
|
||||||
return typeconv(getattr(ob, attr))
|
|
||||||
raise NameError, name
|
|
||||||
|
|
||||||
|
|
||||||
########################################################################
|
|
||||||
# SQL METHODS USED ABOVE
|
|
||||||
# PASTE INTO ZSQL METHODS
|
|
||||||
# take note of what parameters are used in each query
|
|
||||||
########################################################################
|
|
||||||
|
|
||||||
_sqlListUsers="""
|
|
||||||
SELECT * FROM passwd
|
|
||||||
"""
|
|
||||||
|
|
||||||
_sqlListOneUser="""
|
|
||||||
SELECT * FROM passwd
|
|
||||||
where username=<dtml-sqlvar username type=string>
|
|
||||||
"""
|
|
||||||
|
|
||||||
_sqlDeleteOneUser="""
|
|
||||||
DELETE FROM passwd
|
|
||||||
where uid=<dtml-sqlvar userid type=int>
|
|
||||||
"""
|
|
||||||
|
|
||||||
_sqlInsertUser="""
|
|
||||||
INSERT INTO passwd (username, password, roles)
|
|
||||||
VALUES (<dtml-sqlvar username type=string>,
|
|
||||||
<dtml-sqlvar password type=string>,
|
|
||||||
<dtml-sqlvar roles type=string>)
|
|
||||||
"""
|
|
||||||
|
|
||||||
_sqlUpdateUserPassword="""
|
|
||||||
UPDATE passwd set password=<dtml-sqlvar password type=string>
|
|
||||||
WHERE username=<dtml-sqlvar username type=string>
|
|
||||||
"""
|
|
||||||
|
|
||||||
_sqlUpdateUser="""
|
|
||||||
UPDATE passwd set roles=<dtml-sqlvar roles type=string>
|
|
||||||
WHERE username=<dtml-sqlvar username type=string>
|
|
||||||
"""
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
|||||||
*.pyc
|
|
||||||
*.pyo
|
|
||||||
*~
|
|
||||||
.*.swp
|
|
@ -1,32 +0,0 @@
|
|||||||
#
|
|
||||||
# Extensible User Folder
|
|
||||||
#
|
|
||||||
# Null Group Source for exUserFolder
|
|
||||||
#
|
|
||||||
# Author: Brent Hendricks <bmh@users.sourceforge.net>
|
|
||||||
# $Id: GroupSource.py,v 1.1 2002/12/02 23:20:49 bmh Exp $
|
|
||||||
from Globals import DTMLFile
|
|
||||||
|
|
||||||
|
|
||||||
manage_addGroupSourceForm=DTMLFile('manage_addGroupSourceForm', globals(), __name__='manage_addGroupSourceForm')
|
|
||||||
|
|
||||||
|
|
||||||
def manage_addGroupSource(dispatcher, REQUEST):
|
|
||||||
""" Add a Group Source """
|
|
||||||
|
|
||||||
# Get the XUF object we're being added to
|
|
||||||
xuf = dispatcher.Destination()
|
|
||||||
|
|
||||||
groupId = REQUEST.get('groupId', None)
|
|
||||||
if groupId:
|
|
||||||
# Invoke the add method for this plugin
|
|
||||||
xuf.groupSources[groupId].manage_addMethod(xuf, REQUEST)
|
|
||||||
else:
|
|
||||||
raise "BadRequest", "Required parameter 'groupId' omitted"
|
|
||||||
|
|
||||||
dispatcher.manage_main(dispatcher, REQUEST)
|
|
||||||
|
|
||||||
|
|
||||||
class GroupSource:
|
|
||||||
pass
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
|||||||
# $Id: __init__.py,v 1.1 2002/12/02 23:20:49 bmh Exp $
|
|
||||||
import GroupSource
|
|
@ -1,33 +0,0 @@
|
|||||||
<dtml-var manage_page_header>
|
|
||||||
<dtml-if currentGroupSource>
|
|
||||||
<dtml-var "MessageDialog(title='Group Source Exists', message='Error: There is already a group source here. Please delete it first', action='manage_main')">
|
|
||||||
<dtml-elif allDone>
|
|
||||||
<dtml-var expr="manage_addGroupSource(REQUEST)">
|
|
||||||
<dtml-elif groupId>
|
|
||||||
<dtml-call "REQUEST.set('groupForm',doGroupSourceForm(groupId=groupId))">
|
|
||||||
<dtml-var "groupForm(mapping=_)">
|
|
||||||
<dtml-else>
|
|
||||||
<dtml-var "DialogHeader(_.None,_,DialogTitle='Add eXtensible User Folder Group Source')">
|
|
||||||
<form action="&dtml-URL;" method="post">
|
|
||||||
<table cellspacing="2">
|
|
||||||
<tr>
|
|
||||||
<td align="left" valign="top">
|
|
||||||
<b><dtml-babel src="'en'">Group Source</dtml-babel></b>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<select name="groupId">
|
|
||||||
<dtml-in getGroupSources sort="name">
|
|
||||||
<option value="<dtml-var "_['sequence-item'].name">"><dtml-var description></option>
|
|
||||||
</dtml-in>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<td><br><input type="submit" value=" <dtml-babel src="'en'">Add</dtml-babel> "></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</form>
|
|
||||||
<dtml-var DialogFooter>
|
|
||||||
</dtml-if>
|
|
||||||
<dtml-var manage_page_footer>
|
|
@ -1,31 +0,0 @@
|
|||||||
#
|
|
||||||
# Extensible User Folder
|
|
||||||
#
|
|
||||||
# (C) Copyright 2000-2004 The Internet (Aust) Pty Ltd
|
|
||||||
# ACN: 082 081 472 ABN: 83 082 081 472
|
|
||||||
# All Rights Reserved
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
||||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
# SUCH DAMAGE.
|
|
||||||
#
|
|
||||||
# Author: Andrew Milton <akm@theinternet.com.au>
|
|
||||||
# $Id: __init__.py,v 1.1 2004/11/10 14:15:53 akm Exp $
|
|
||||||
|
|
||||||
import nullGroupSource
|
|
||||||
|
|
||||||
# If this fails due to NUG being absent, just skip it
|
|
||||||
try:
|
|
||||||
import zodbGroupSource
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
|||||||
*.pyc
|
|
||||||
*.pyo
|
|
||||||
*~
|
|
||||||
.*.swp
|
|
@ -1,2 +0,0 @@
|
|||||||
# $Id: __init__.py,v 1.1 2004/11/10 14:15:53 akm Exp $
|
|
||||||
import nullGroupSource
|
|
@ -1,21 +0,0 @@
|
|||||||
<dtml-var "DialogHeader(_.None, _, DialogTitle='Add Basic Group Source', dialog_width='')">
|
|
||||||
<FORM ACTION="&dtml-URL;" METHOD="POST">
|
|
||||||
<dtml-in "REQUEST.form.keys()">
|
|
||||||
<dtml-if "getVariableType(REQUEST[_['sequence-item']]) == 'List'">
|
|
||||||
<dtml-let listVar=sequence-item>
|
|
||||||
<dtml-in "REQUEST[listVar]">
|
|
||||||
<input type="HIDDEN" name="<dtml-var listVar>:list" value="<dtml-var sequence-item>">
|
|
||||||
</dtml-in>
|
|
||||||
</dtml-let>
|
|
||||||
<dtml-else>
|
|
||||||
<input type="HIDDEN" name="<dtml-var sequence-item>" value="<dtml-var "REQUEST[_.getitem('sequence-item',0)]">">
|
|
||||||
</dtml-if>
|
|
||||||
|
|
||||||
</dtml-in>
|
|
||||||
|
|
||||||
<input type="HIDDEN" name="allDone" value="1">
|
|
||||||
<b><dtml-babel src="'en'">This Group Source has no configuration Items</dtml-babel></b><br>
|
|
||||||
<br>
|
|
||||||
<input type="SUBMIT" value="<dtml-babel src="'en'">Add</dtml-babel>">
|
|
||||||
</form>
|
|
||||||
<dtml-var DialogFooter>
|
|
@ -1,34 +0,0 @@
|
|||||||
#
|
|
||||||
# Extensible User Folder
|
|
||||||
#
|
|
||||||
# Null Group Source for exUserFolder
|
|
||||||
#
|
|
||||||
# Author: Brent Hendricks <bmh@users.sourceforge.net>
|
|
||||||
# $Id: nullGroupSource.py,v 1.1 2004/11/10 14:15:53 akm Exp $
|
|
||||||
from Globals import HTMLFile, INSTANCE_HOME
|
|
||||||
|
|
||||||
from OFS.Folder import Folder
|
|
||||||
|
|
||||||
from Products.exUserFolder.exUserFolder import exUserFolder
|
|
||||||
from Products.exUserFolder.Plugins import PluginRegister
|
|
||||||
from Products.exUserFolder.nullPlugin import nullPlugin
|
|
||||||
|
|
||||||
def manage_addNullGroupSource(self, REQUEST):
|
|
||||||
""" Add a Group Source """
|
|
||||||
self.currentGroupSource=None
|
|
||||||
return ''
|
|
||||||
|
|
||||||
|
|
||||||
manage_addNullGroupSourceForm=HTMLFile('manage_addNullPluginSourceForm',globals())
|
|
||||||
manage_editNullGroupSourceForm=None
|
|
||||||
|
|
||||||
|
|
||||||
nullGroupReg=PluginRegister('nullGroupSource',
|
|
||||||
'Null Group Source',
|
|
||||||
nullPlugin,
|
|
||||||
manage_addNullGroupSourceForm,
|
|
||||||
manage_addNullGroupSource,
|
|
||||||
manage_editNullGroupSourceForm)
|
|
||||||
|
|
||||||
exUserFolder.groupSources['nullGroupSource']=nullGroupReg
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
|||||||
*.pyc
|
|
||||||
*.pyo
|
|
||||||
*~
|
|
||||||
.*.swp
|
|
@ -1,2 +0,0 @@
|
|||||||
# $Id: __init__.py,v 1.1 2004/11/10 14:15:54 akm Exp $
|
|
||||||
import zodbGroupSource
|
|
@ -1,21 +0,0 @@
|
|||||||
<dtml-var "DialogHeader(_.None,_,DialogTitle='Add ZODB Group Source')">
|
|
||||||
|
|
||||||
<FORM ACTION="&dtml-URL;" METHOD="POST">
|
|
||||||
<dtml-in "REQUEST.form.keys()">
|
|
||||||
<dtml-if "getVariableType(REQUEST[_['sequence-item']]) == 'List'">
|
|
||||||
<dtml-let listVar=sequence-item>
|
|
||||||
<dtml-in "REQUEST[listVar]">
|
|
||||||
<input type="HIDDEN" name="<dtml-var listVar>:list" value="<dtml-var sequence-item>">
|
|
||||||
</dtml-in>
|
|
||||||
</dtml-let>
|
|
||||||
<dtml-else>
|
|
||||||
<input type="HIDDEN" name="<dtml-var sequence-item>" value="<dtml-var "REQUEST[_.getitem('sequence-item',0)]">">
|
|
||||||
</dtml-if>
|
|
||||||
|
|
||||||
</dtml-in>
|
|
||||||
|
|
||||||
<input type="HIDDEN" name="allDone" value="1">
|
|
||||||
<b><dtml-babel src="'en'">This group source requires no user configuration items at this time.</dtml-babel></b><br>
|
|
||||||
<INPUT TYPE="SUBMIT" VALUE=" <dtml-babel src="'en'">NEXT</dtml-babel> ">
|
|
||||||
</FORM>
|
|
||||||
<dtml-var DialogFooter>
|
|
@ -1,7 +0,0 @@
|
|||||||
<dtml-var "DialogHeader(_.None,_,DialogTitle='ZODB Group Source',dialog_width='100%')">
|
|
||||||
<dtml-var manage_tabs>
|
|
||||||
<FORM ACTION="manage_main" METHOD="POST">
|
|
||||||
<b><dtml-babel src="'en'">This group source requires no user configuration items at this time.</dtml-babel></b><br>
|
|
||||||
<INPUT TYPE="SUBMIT" VALUE=" <dtml-babel src="'en'">OK</dtml-babel> ">
|
|
||||||
</FORM>
|
|
||||||
<dtml-var DialogFooter>
|
|
@ -1,177 +0,0 @@
|
|||||||
#
|
|
||||||
# Extensible User Folder
|
|
||||||
#
|
|
||||||
# ZODB Group Source for exUserFolder
|
|
||||||
#
|
|
||||||
# Author: Brent Hendricks <mh@users.sourceforge.net>
|
|
||||||
# $Id: zodbGroupSource.py,v 1.1 2004/11/10 14:15:54 akm Exp $
|
|
||||||
from Globals import HTMLFile, MessageDialog, INSTANCE_HOME,Acquisition, PersistentMapping
|
|
||||||
|
|
||||||
from OFS.Folder import Folder
|
|
||||||
|
|
||||||
from Products.ZSQLMethods.SQL import SQL
|
|
||||||
|
|
||||||
from Products.exUserFolder.exUserFolder import exUserFolder
|
|
||||||
from Products.exUserFolder.Plugins import PluginRegister
|
|
||||||
from Products.NuxUserGroups.UserFolderWithGroups import Group, _marker
|
|
||||||
|
|
||||||
import time
|
|
||||||
import zLOG
|
|
||||||
import sys
|
|
||||||
|
|
||||||
manage_addGroupSourceForm=HTMLFile('manage_addzodbGroupSourceForm', globals())
|
|
||||||
|
|
||||||
def manage_addzodbGroupSource(self, REQUEST):
|
|
||||||
""" Add a ZODB Group Source """
|
|
||||||
|
|
||||||
o = zodbGroupSource()
|
|
||||||
self._setObject('zodbGroupSource', o, None, None, 0)
|
|
||||||
o = getattr(self, 'zodbGroupSource')
|
|
||||||
|
|
||||||
# Allow Prop Source to setup default users...
|
|
||||||
if hasattr(o, 'postInitialisation'):
|
|
||||||
o.postInitialisation(REQUEST)
|
|
||||||
self.currentGroupSource=o
|
|
||||||
|
|
||||||
manage_addzodbGroupSourceForm=HTMLFile('manage_addzodbGroupSourceForm', globals())
|
|
||||||
manage_editzodbGroupSourceForm=HTMLFile('manage_editzodbGroupSourceForm', globals())
|
|
||||||
|
|
||||||
#
|
|
||||||
# Very very simple thing, used as an example of how to write a property source
|
|
||||||
# Not recommended for large scale production sites...
|
|
||||||
#
|
|
||||||
|
|
||||||
class zodbGroupSource(Folder):
|
|
||||||
""" Store Group Data inside ZODB, the simplistic way """
|
|
||||||
|
|
||||||
meta_type='Group Source'
|
|
||||||
title='Simplistic ZODB Groups'
|
|
||||||
icon ='misc_/exUserFolder/exUserFolderPlugin.gif'
|
|
||||||
manage_editForm=manage_editzodbGroupSourceForm
|
|
||||||
manage_tabs=Acquisition.Acquired
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.id='zodbGroupSource'
|
|
||||||
self.groups=PersistentMapping()
|
|
||||||
|
|
||||||
|
|
||||||
def addGroup(self, groupname, title='', users=(), **kw):
|
|
||||||
"""Creates a group"""
|
|
||||||
if self.groups.has_key(groupname):
|
|
||||||
raise ValueError, 'Group "%s" already exists' % groupname
|
|
||||||
a = 'before: groupname %s groups %s' % (groupname, self.groups)
|
|
||||||
group = apply(Group, (groupname,), kw)
|
|
||||||
group.setTitle(title)
|
|
||||||
group._setUsers(users)
|
|
||||||
self.groups[groupname] = group
|
|
||||||
|
|
||||||
|
|
||||||
def getGroup(self, groupname, default=_marker):
|
|
||||||
"""Returns the given group"""
|
|
||||||
try:
|
|
||||||
group = self.groups[groupname]
|
|
||||||
except KeyError:
|
|
||||||
if default is _marker: raise
|
|
||||||
return default
|
|
||||||
return group
|
|
||||||
|
|
||||||
|
|
||||||
def delGroup(self, groupname):
|
|
||||||
"""Deletes the given group"""
|
|
||||||
usernames = self.groups[groupname].getUsers()
|
|
||||||
#self.delUsersFromGroup(usernames, groupname)
|
|
||||||
del self.groups[groupname]
|
|
||||||
|
|
||||||
|
|
||||||
def listGroups(self):
|
|
||||||
"""Returns a list of group names"""
|
|
||||||
return tuple(self.groups.keys())
|
|
||||||
|
|
||||||
|
|
||||||
def getGroupsOfUser(self, username):
|
|
||||||
"Get a user's groups"
|
|
||||||
groupnames = []
|
|
||||||
allnames = self.listGroups()
|
|
||||||
groupnames = filter(lambda g, u=username, self=self: u in self.groups[g].getUsers(), allnames)
|
|
||||||
return tuple(groupnames)
|
|
||||||
|
|
||||||
|
|
||||||
def setGroupsOfUser(self, groupnames, username):
|
|
||||||
"Set a user's groups"
|
|
||||||
oldGroups = self.getGroupsOfUser(username)
|
|
||||||
self.delGroupsFromUser(oldGroups, username)
|
|
||||||
self.addGroupsToUser(groupnames, username)
|
|
||||||
|
|
||||||
|
|
||||||
def addGroupsToUser(self, groupnames, username):
|
|
||||||
"Add groups to a user"
|
|
||||||
for name in groupnames:
|
|
||||||
group = self.groups[name]
|
|
||||||
if not username in group.getUsers():
|
|
||||||
group._addUsers([username])
|
|
||||||
|
|
||||||
|
|
||||||
def delGroupsFromUser(self, groupnames, username):
|
|
||||||
"Delete groups from a user"
|
|
||||||
for name in groupnames:
|
|
||||||
group = self.groups[name]
|
|
||||||
if username in group.getUsers():
|
|
||||||
group._delUsers([username])
|
|
||||||
|
|
||||||
|
|
||||||
def getUsersOfGroup(self, groupname):
|
|
||||||
"Get the users in a group"
|
|
||||||
return self.groups[groupname].getUsers()
|
|
||||||
|
|
||||||
|
|
||||||
def setUsersOfGroup(self, usernames, groupname):
|
|
||||||
"Set the users in a group"
|
|
||||||
# uniquify
|
|
||||||
dict = {}
|
|
||||||
for u in usernames: dict[u] = None
|
|
||||||
usernames = dict.keys()
|
|
||||||
|
|
||||||
self.groups[groupname]._setUsers(usernames)
|
|
||||||
|
|
||||||
|
|
||||||
def addUsersToGroup(self, usernames, groupname):
|
|
||||||
"Add users to a group"
|
|
||||||
# uniquify
|
|
||||||
dict = {}
|
|
||||||
for u in usernames: dict[u] = None
|
|
||||||
usernames = dict.keys()
|
|
||||||
|
|
||||||
self.groups[groupname]._addUsers(usernames)
|
|
||||||
|
|
||||||
|
|
||||||
def delUsersFromGroup(self, usernames, groupname):
|
|
||||||
"Delete users from a group"
|
|
||||||
# uniquify
|
|
||||||
dict = {}
|
|
||||||
for u in usernames: dict[u] = None
|
|
||||||
usernames = dict.keys()
|
|
||||||
|
|
||||||
self.groups[groupname]._delUsers(usernames)
|
|
||||||
|
|
||||||
|
|
||||||
def deleteUsers(self, usernames):
|
|
||||||
"Delete a list of users"
|
|
||||||
for user in usernames:
|
|
||||||
groups = self.getGroupsOfUser(user)
|
|
||||||
self.delGroupsFromUser(groups, user)
|
|
||||||
|
|
||||||
|
|
||||||
def postInitialisation(self, REQUEST):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def manage_beforeDelete(self, item, container):
|
|
||||||
# Notify the exUserFolder that it doesn't have a group source anymore
|
|
||||||
container.currentGroupSource=None
|
|
||||||
|
|
||||||
|
|
||||||
zodbGroupReg=PluginRegister('zodbGroupSource','Simplistic ZODB Group Source',
|
|
||||||
zodbGroupSource, manage_addzodbGroupSourceForm,
|
|
||||||
manage_addzodbGroupSource,
|
|
||||||
manage_editzodbGroupSourceForm)
|
|
||||||
exUserFolder.groupSources['zodbGroupSource']=zodbGroupReg
|
|
@ -1,91 +0,0 @@
|
|||||||
XUF as a whole is covered by the BSD License, however it uses software
|
|
||||||
covered by other compatible licenses (see below)
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
All of the documentation and software included in the exUserFolder
|
|
||||||
Releases is copyrighted by The Internet (Aust) Pty Ltd and contributors
|
|
||||||
ACN: 082 081 472 ABN: 83 082 081 472
|
|
||||||
|
|
||||||
Copyright 2001, 2002 The Internet (Aust) Pty Ltd
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions
|
|
||||||
are met:
|
|
||||||
1. Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in the
|
|
||||||
documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
SUCH DAMAGE.
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
This product includes software developed by Digital Creations for use in
|
|
||||||
the Z Object Publishing Environment (http://www.zope.org/)
|
|
||||||
|
|
||||||
Portions of smbAuthSource Copyright (C) 2001 Michael Teo
|
|
||||||
|
|
||||||
Portions of radiusAuthSource Copyright (C) 1999 Stuart Bishop
|
|
||||||
|
|
||||||
fcrypt is Copyright (C) 2001, 2001 Carey Evans
|
|
||||||
|
|
||||||
This product includes cryptographic software written by Eric Young
|
|
||||||
(eay@mincom.oz.au)
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
Brief discussion of what the license means to you, not meant to be
|
|
||||||
all encompassing, but, to give you the general idea. This editorial does
|
|
||||||
not need to be distributed d8)
|
|
||||||
|
|
||||||
If you want to incorporate this product (or parts of it) into a commercial
|
|
||||||
product that's fine.
|
|
||||||
|
|
||||||
If you want to modify this product that's fine.
|
|
||||||
|
|
||||||
If you want to modify and distribute this product that's fine (even in
|
|
||||||
commercial products).
|
|
||||||
|
|
||||||
If you want to incorporate this into a larger work that's fine (even
|
|
||||||
if that work has a different license).
|
|
||||||
|
|
||||||
None of the previous items place any obligation of notification, compensation,
|
|
||||||
or return of code to us. In fact we don't care if you do these things. Go
|
|
||||||
forth and prosper. Basically as long as you recognise that this doesn't
|
|
||||||
belong to you, you can do what you want with it even charge money for it.
|
|
||||||
|
|
||||||
Note: If you do distribute this as source, then the XUF components are
|
|
||||||
removable and distributable independently of your license as a whole
|
|
||||||
(although that's a lot of trouble to go to when they could just download it
|
|
||||||
from the same place you did).
|
|
||||||
|
|
||||||
What you can't do, is claim it's yours, and this one thing encompasses a lot
|
|
||||||
of things, here's a few.
|
|
||||||
|
|
||||||
If it's not yours you can't;
|
|
||||||
|
|
||||||
Change the license even if you change the code since the copyright
|
|
||||||
of the modified files remains with the original copyright holders.
|
|
||||||
|
|
||||||
Use bits of it inside products that require the license to change, because
|
|
||||||
only the copyright holders have the right to modify the license (not a
|
|
||||||
concern for commercial projects, only some other Free/Open Source licenses).
|
|
||||||
|
|
||||||
Assign the copyright or other IP to any other party of the whole or any
|
|
||||||
part (even if you change the code), because it's not yours to give away or
|
|
||||||
sell to a 3rd party.
|
|
||||||
|
|
||||||
If the fact you can almost do whatever you want with this code isn't
|
|
||||||
liberal enough for you, contact us and we'll see what we can arrange.
|
|
@ -1,27 +0,0 @@
|
|||||||
#
|
|
||||||
# Extensible User Folder
|
|
||||||
#
|
|
||||||
# (C) Copyright 2000,2001 The Internet (Aust) Pty Ltd
|
|
||||||
# ACN: 082 081 472 ABN: 83 082 081 472
|
|
||||||
# All Rights Reserved
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
||||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
# SUCH DAMAGE.
|
|
||||||
#
|
|
||||||
# Author: Andrew Milton <akm@theinternet.com.au>
|
|
||||||
# $Id: LoginRequiredMessages.py,v 1.2 2001/12/01 08:40:03 akm Exp $
|
|
||||||
|
|
||||||
LoginRequiredMessages={
|
|
||||||
'session_expired':'Your Session has Expired',
|
|
||||||
'unauthorized':'Please Login',
|
|
||||||
'login_failed':'Login Failed',
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
#
|
|
||||||
# Extensible User Folder
|
|
||||||
#
|
|
||||||
# (C) Copyright 2000-2005 The Internet (Aust) Pty Ltd
|
|
||||||
# ACN: 082 081 472 ABN: 83 082 081 472
|
|
||||||
# All Rights Reserved
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
||||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
# SUCH DAMAGE.
|
|
||||||
#
|
|
||||||
# Author: Andrew Milton <akm@theinternet.com.au>
|
|
||||||
# $Id:
|
|
||||||
|
|
||||||
import basicMemberSource
|
|
||||||
import nullMemberSource
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
|||||||
*.pyc
|
|
||||||
*.pyo
|
|
||||||
*~
|
|
||||||
.*.swp
|
|
@ -1,22 +0,0 @@
|
|||||||
<dtml-var "DialogHeader(DialogTitle='Change Password', dialog_width='')">
|
|
||||||
<form action="acl_users/manage_changePassword" method="POST">
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td align="right"><b><dtml-babel src="'en'">Old Password</dtml-babel></b></td>
|
|
||||||
<td><input type="password" name="current_password"></td>
|
|
||||||
<tr>
|
|
||||||
<td align="right"><b><dtml-babel src="'en'">Password</dtml-babel></b></td>
|
|
||||||
<td><input type="password" name="password"></td>
|
|
||||||
</tr>
|
|
||||||
<td align="right"><b><dtml-babel src="'en'">Confirm Password</dtml-babel></b></td>
|
|
||||||
<td><input type="password" name="password_confirm"></td>
|
|
||||||
</tr>
|
|
||||||
<dtml-if "forgottenPasswords=='hint'">
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Password Hint</dtml-babel></b></td>
|
|
||||||
<td><input type="text" name="user_hint" value="&dtml.missing-user_hint;"></td>
|
|
||||||
</tr>
|
|
||||||
</dtml-if>
|
|
||||||
</table>
|
|
||||||
<input type="submit" value=" <dtml-babel src="'en'">Change Password</dtml-babel> ">
|
|
||||||
</form>
|
|
||||||
<dtml-var DialogFooter>
|
|
@ -1,31 +0,0 @@
|
|||||||
<dtml-var "DialogHeader(DialogTitle='Signup', dialog_width='')">
|
|
||||||
<form action="acl_users/manage_signupUser" method="POST">
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td align="right"><b><dtml-babel src="'en'">Username</dtml-babel></td>
|
|
||||||
<td><input name="username" type="text" value="&dtml.missing-username;"></td>
|
|
||||||
</tr>
|
|
||||||
<dtml-if "passwordPolicy=='user'">
|
|
||||||
<tr>
|
|
||||||
<td align="right"><b><dtml-babel src="'en'">Password</dtml-babel></b></td>
|
|
||||||
<td><input type="password" name="password" value="&dtml.missing-password;"></td>
|
|
||||||
</tr>
|
|
||||||
<td align="right"><b><dtml-babel src="'en'">Confirm Password</dtml-babel></b></td>
|
|
||||||
<td><input type="password" name="password_confirm"></td>
|
|
||||||
</tr>
|
|
||||||
<dtml-if "forgottenPasswords=='hint'">
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Password Hint</dtml-babel></b></td>
|
|
||||||
<td><input type="text" name="user_hint" value="&dtml.missing-user_hint;"></td>
|
|
||||||
</tr>
|
|
||||||
</dtml-if>
|
|
||||||
</dtml-if>
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Real Name</dtml-babel></b></td>
|
|
||||||
<td><input type="text" name="user_realname" value="&dtml.missing-user_realname;"></td>
|
|
||||||
</tr>
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'"><dtml-babel src="'en'">Email</dtml-babel></dtml-babel></b></td>
|
|
||||||
<td><input type="text" name="user_email" value="&dtml.missing-user_email;"></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<input type="submit" value=" <dtml-babel src="'en'">Signup</dtml-babel> ">
|
|
||||||
</form>
|
|
||||||
<dtml-var DialogFooter>
|
|
@ -1,2 +0,0 @@
|
|||||||
# $Id: __init__.py,v 1.1 2004/11/10 14:15:55 akm Exp $
|
|
||||||
import basicMemberSource
|
|
@ -1,629 +0,0 @@
|
|||||||
#
|
|
||||||
# Extensible User Folder
|
|
||||||
#
|
|
||||||
# Basic Membership Source for exUserFolder
|
|
||||||
#
|
|
||||||
# (C) Copyright 2000,2001 The Internet (Aust) Pty Ltd
|
|
||||||
# ACN: 082 081 472 ABN: 83 082 081 472
|
|
||||||
# All Rights Reserved
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
||||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
# SUCH DAMAGE.
|
|
||||||
#
|
|
||||||
# Author: Andrew Milton <akm@theinternet.com.au>
|
|
||||||
# $Id: basicMemberSource.py,v 1.1 2004/11/10 14:15:55 akm Exp $
|
|
||||||
|
|
||||||
#
|
|
||||||
# Basically membership is a layer between the signup/login form, and
|
|
||||||
# the authentication layer, it uses the prop source of the users to
|
|
||||||
# store additional information about a user i.e. doesn't impact on the
|
|
||||||
# authentication source.
|
|
||||||
#
|
|
||||||
# Some membership features imply some extra properties for the user will
|
|
||||||
# be available; specifically at this time an email property.
|
|
||||||
#
|
|
||||||
# You also need a MailHost setup and ready to go for emailing stuff to users
|
|
||||||
#
|
|
||||||
|
|
||||||
import string,Acquisition
|
|
||||||
from random import choice
|
|
||||||
|
|
||||||
|
|
||||||
from Globals import HTMLFile, INSTANCE_HOME
|
|
||||||
|
|
||||||
from OFS.Folder import Folder
|
|
||||||
from OFS.DTMLMethod import DTMLMethod
|
|
||||||
|
|
||||||
from Products.exUserFolder.exUserFolder import exUserFolder
|
|
||||||
from Products.exUserFolder.Plugins import PluginRegister
|
|
||||||
|
|
||||||
from base64 import encodestring
|
|
||||||
from urllib import quote
|
|
||||||
|
|
||||||
import zLOG
|
|
||||||
|
|
||||||
"""
|
|
||||||
Password Policy enforcement (min/max length, caps etc)
|
|
||||||
Create Password, or User Chooses.
|
|
||||||
Timing out of passwords...
|
|
||||||
Empty Password force change on login...
|
|
||||||
Create Home Directory
|
|
||||||
Copy files from Skelton Directory
|
|
||||||
EMail password hint to user (forgot my password)
|
|
||||||
Reset password and email user (needs plugin?)
|
|
||||||
Redirect on login to fixed or varying per username location.
|
|
||||||
Automatically add users, or manually approve of users.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Stupid little things for making a password
|
|
||||||
# Don't hassle me, it's supposed to be basic.
|
|
||||||
|
|
||||||
nouns=['ace', 'ant', 'arc', 'arm', 'axe',
|
|
||||||
'bar', 'bat', 'bee', 'bib', 'bin',
|
|
||||||
'can', 'cap', 'car', 'cat', 'cob',
|
|
||||||
'day', 'den', 'dog', 'dot', 'dux',
|
|
||||||
'ear', 'eel', 'egg', 'elf', 'elk',
|
|
||||||
'fad', 'fan', 'fat', 'fig', 'fez',
|
|
||||||
'gag', 'gas', 'gin', 'git', 'gum',
|
|
||||||
'hag', 'hat', 'hay', 'hex', 'hub']
|
|
||||||
|
|
||||||
pastConjs = [ 'did', 'has', 'was' ]
|
|
||||||
suffixes = [ 'ing', 'es', 'ed', 'ious', 'ily']
|
|
||||||
|
|
||||||
def manage_addBasicMemberSource(self, REQUEST):
|
|
||||||
""" Add a Membership Source """
|
|
||||||
|
|
||||||
pvfeatures=[]
|
|
||||||
minLength=0
|
|
||||||
passwordPolicy=''
|
|
||||||
createHomedir=0
|
|
||||||
homeRoot=''
|
|
||||||
copyFilesFrom=''
|
|
||||||
postLogin=''
|
|
||||||
postSignup=''
|
|
||||||
forgottenPasswords=''
|
|
||||||
defaultRoles=[]
|
|
||||||
usersCanChangePasswords=0
|
|
||||||
baseURL=''
|
|
||||||
loginPage=''
|
|
||||||
signupPage=''
|
|
||||||
passwordPage=''
|
|
||||||
mailHost=''
|
|
||||||
fixedDest=''
|
|
||||||
|
|
||||||
if REQUEST.has_key('basicmember_pvfeatures'):
|
|
||||||
pvfeatures=REQUEST['basicmember_pvfeatures']
|
|
||||||
|
|
||||||
if REQUEST.has_key('basicmember_roles'):
|
|
||||||
defaultRoles=REQUEST['basicmember_roles']
|
|
||||||
|
|
||||||
if not defaultRoles:
|
|
||||||
defaultRoles=['Member']
|
|
||||||
|
|
||||||
if 'minlength' in pvfeatures:
|
|
||||||
minLength=REQUEST['basicmember_minpasslen']
|
|
||||||
|
|
||||||
if REQUEST.has_key('basicmember_passwordpolicy'):
|
|
||||||
passwordPolicy=REQUEST['basicmember_passwordpolicy']
|
|
||||||
|
|
||||||
if REQUEST.has_key('basicmember_createhomedir'):
|
|
||||||
homeRoot=REQUEST['basicmember_homeroot']
|
|
||||||
createHomedir=1
|
|
||||||
|
|
||||||
if REQUEST.has_key('basicmember_copyfiles'):
|
|
||||||
copyFilesFrom=REQUEST['basicmember_copyfiles']
|
|
||||||
|
|
||||||
if REQUEST.has_key('basicmember_changepasswords'):
|
|
||||||
usersCanChangePasswords=1
|
|
||||||
|
|
||||||
if REQUEST.has_key('basicmember_fixeddest'):
|
|
||||||
fixedDest=''
|
|
||||||
|
|
||||||
forgottenPasswords=REQUEST['basicmember_forgottenpasswords']
|
|
||||||
postLogin=REQUEST['basicmember_postlogin']
|
|
||||||
|
|
||||||
baseURL=REQUEST['basicmember_baseurl']
|
|
||||||
loginPage=REQUEST['basicmember_loginpage']
|
|
||||||
signupPage=REQUEST['basicmember_signuppage']
|
|
||||||
passwordPage=REQUEST['basicmember_passwordpage']
|
|
||||||
siteEmail=REQUEST['basicmember_siteemail']
|
|
||||||
siteName=REQUEST['basicmember_sitename']
|
|
||||||
|
|
||||||
mailHost=REQUEST['basicmember_mailhost']
|
|
||||||
|
|
||||||
# postSignup=REQUEST['basicmember_postsignup']
|
|
||||||
|
|
||||||
#
|
|
||||||
# Yep this is obscene
|
|
||||||
#
|
|
||||||
o = BasicMemberSource(pvfeatures, minLength, passwordPolicy,
|
|
||||||
createHomedir, copyFilesFrom, postLogin,
|
|
||||||
homeRoot, forgottenPasswords, defaultRoles,
|
|
||||||
usersCanChangePasswords, baseURL, loginPage,
|
|
||||||
signupPage, passwordPage, mailHost,
|
|
||||||
siteName, siteEmail, fixedDest)
|
|
||||||
|
|
||||||
self._setObject('basicMemberSource', o, None, None, 0)
|
|
||||||
o = getattr(self, 'basicMemberSource')
|
|
||||||
|
|
||||||
if hasattr(o, 'postInitialisation'):
|
|
||||||
o.postInitialisation(REQUEST)
|
|
||||||
|
|
||||||
self.currentMembershipSource=o
|
|
||||||
return ''
|
|
||||||
|
|
||||||
|
|
||||||
manage_addBasicMemberSourceForm=HTMLFile('manage_addBasicMemberSourceForm',
|
|
||||||
globals())
|
|
||||||
manage_editBasicMemberSourceForm=HTMLFile('manage_editBasicMemberSourceForm',
|
|
||||||
globals())
|
|
||||||
|
|
||||||
#
|
|
||||||
# Crap, I don't know why I called this basic, I'd hate to see a
|
|
||||||
# complicated one.
|
|
||||||
#
|
|
||||||
class BasicMemberSource(Folder):
|
|
||||||
""" Provide High Level User Management """
|
|
||||||
meta_type="Membership Source"
|
|
||||||
title="Basic Membership Source"
|
|
||||||
icon ='misc_/exUserFolder/exUserFolderPlugin.gif'
|
|
||||||
manage_tabs=Acquisition.Acquired
|
|
||||||
manage_editForm=manage_editBasicMemberSourceForm
|
|
||||||
|
|
||||||
# Ugh...
|
|
||||||
def __init__(self, pvFeatures=[], minLength=0, passwordPolicy='',
|
|
||||||
createHomeDir=0, copyFilesFrom='', postLogin='', homeRoot='',
|
|
||||||
forgottenPasswords='', defaultRoles=[], usersCanChangePasswords=0,
|
|
||||||
baseURL='', loginPage='', signupPage='', passwordPage='',
|
|
||||||
mailHost='', siteName='', siteEmail='', fixedDest=''):
|
|
||||||
|
|
||||||
self.id='basicMemberSource'
|
|
||||||
self.pvFeatures=pvFeatures
|
|
||||||
self.minLength=int(minLength)
|
|
||||||
self.passwordPolicy=passwordPolicy
|
|
||||||
self.createHomeDir=createHomeDir
|
|
||||||
self.copyFilesFrom=copyFilesFrom
|
|
||||||
self.postLogin=postLogin
|
|
||||||
self.homeRoot=homeRoot
|
|
||||||
self.forgottenPasswords=forgottenPasswords
|
|
||||||
self.defaultRoles=defaultRoles
|
|
||||||
self.usersCanChangePasswords=usersCanChangePasswords
|
|
||||||
self.baseURL=baseURL
|
|
||||||
self.loginPage=loginPage
|
|
||||||
self.signupPage=signupPage
|
|
||||||
self.passwordPage=passwordPage
|
|
||||||
self.siteName=siteName
|
|
||||||
self.siteEmail=siteEmail
|
|
||||||
self.fixedDest=fixedDest
|
|
||||||
|
|
||||||
_SignupForm=HTMLFile('SignupForm', globals())
|
|
||||||
SignupForm=DTMLMethod()
|
|
||||||
SignupForm.manage_edit(data=_SignupForm, title='Signup Form')
|
|
||||||
self._setObject('SignupForm', SignupForm)
|
|
||||||
|
|
||||||
_PasswordForm=HTMLFile('PasswordForm', globals())
|
|
||||||
PasswordForm=DTMLMethod()
|
|
||||||
PasswordForm.manage_edit(data=_PasswordForm,
|
|
||||||
title='Change Password')
|
|
||||||
self._setObject('PasswordForm', PasswordForm)
|
|
||||||
|
|
||||||
self.mailHost=mailHost
|
|
||||||
|
|
||||||
_newPasswordEmail=HTMLFile('newPasswordEmail', globals())
|
|
||||||
newPasswordEmail=DTMLMethod()
|
|
||||||
newPasswordEmail.manage_edit(data=_newPasswordEmail,
|
|
||||||
title='Send New Password')
|
|
||||||
self._setObject('newPasswordEmail', newPasswordEmail)
|
|
||||||
|
|
||||||
_forgotPasswordEmail=HTMLFile('forgotPasswordEmail', globals())
|
|
||||||
forgotPasswordEmail=DTMLMethod()
|
|
||||||
forgotPasswordEmail.manage_edit(data=_forgotPasswordEmail,
|
|
||||||
title='Send Forgotten Password')
|
|
||||||
self._setObject('forgotPasswordEmail', forgotPasswordEmail)
|
|
||||||
|
|
||||||
_passwordHintEmail=HTMLFile('passwordHintEmail', globals())
|
|
||||||
passwordHintEmail=DTMLMethod()
|
|
||||||
passwordHintEmail.manage_edit(data=_passwordHintEmail,
|
|
||||||
title='Send Forgotten Password Hint')
|
|
||||||
self._setObject('passwordHintEmail', passwordHintEmail)
|
|
||||||
|
|
||||||
def postInitialisation(self, REQUEST):
|
|
||||||
if self.createHomeDir and self.homeRoot:
|
|
||||||
self.findHomeRootObject()
|
|
||||||
else:
|
|
||||||
self.homeRootObj=None
|
|
||||||
|
|
||||||
if self.copyFilesFrom:
|
|
||||||
self.findSkelRootObject()
|
|
||||||
else:
|
|
||||||
self.homeSkelObj=None
|
|
||||||
|
|
||||||
# The nice sendmail tag doesn't allow expressions for
|
|
||||||
# the mailhost
|
|
||||||
self.mailHostObject=getattr(self, self.mailHost)
|
|
||||||
|
|
||||||
def manage_editMembershipSource(self, REQUEST):
|
|
||||||
""" Edit a basic Membership Source """
|
|
||||||
if REQUEST.has_key('pvfeatures'):
|
|
||||||
self.pvFeatures=REQUEST['pvfeatures']
|
|
||||||
else:
|
|
||||||
self.pvFeatures=[]
|
|
||||||
|
|
||||||
if REQUEST.has_key('minpasslength'):
|
|
||||||
self.minLength=REQUEST['minpasslength']
|
|
||||||
|
|
||||||
if REQUEST.has_key('createhomedir'):
|
|
||||||
createHomeDir=1
|
|
||||||
else:
|
|
||||||
createHomeDir=0
|
|
||||||
|
|
||||||
if createHomeDir:
|
|
||||||
self.copyFilesFrom=REQUEST['copyfiles']
|
|
||||||
if self.copyFilesFrom:
|
|
||||||
self.findSkelRootObject()
|
|
||||||
else:
|
|
||||||
self.homeRoot=REQUEST['homeroot']
|
|
||||||
self.findHomeRootObject()
|
|
||||||
|
|
||||||
if REQUEST.has_key('memberroles'):
|
|
||||||
self.defaultRoles=REQUEST['memberroles']
|
|
||||||
if REQUEST.has_key('changepasswords'):
|
|
||||||
self.usersCanChangePasswords=1
|
|
||||||
else:
|
|
||||||
self.usersCanChangePasswords=0
|
|
||||||
|
|
||||||
self.postLogin=REQUEST['postlogin']
|
|
||||||
if REQUEST.has_key('fixeddest'):
|
|
||||||
self.fixedDest=REQUEST['fixeddest']
|
|
||||||
|
|
||||||
self.baseURL=REQUEST['baseurl']
|
|
||||||
self.loginPage=REQUEST['loginpage']
|
|
||||||
self.signupPage=REQUEST['signuppage']
|
|
||||||
self.passwordPage=REQUEST['passwordpage']
|
|
||||||
self.siteName=REQUEST['sitename']
|
|
||||||
self.siteEmail=REQUEST['siteemail']
|
|
||||||
return self.MessageDialog(self,
|
|
||||||
title ='Updated!',
|
|
||||||
message="Membership was Updated",
|
|
||||||
action ='manage_editMembershipSourceForm',
|
|
||||||
REQUEST=REQUEST)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def forgotPassword(self, REQUEST):
|
|
||||||
username=REQUEST['username']
|
|
||||||
curUser=self.getUser(username)
|
|
||||||
if not curUser:
|
|
||||||
return self.MessageDialog(self,
|
|
||||||
title ='No such user',
|
|
||||||
message="No users matching that username were found.",
|
|
||||||
action ='%s/%s'%(self.baseURL, self.loginPage),
|
|
||||||
REQUEST=REQUEST)
|
|
||||||
|
|
||||||
|
|
||||||
userEmail=curUser.getProperty('email')
|
|
||||||
userName=curUser.getProperty('realname')
|
|
||||||
if self.forgottenPasswords == "hint":
|
|
||||||
passwordHint=curUser.getProperty('passwordhint')
|
|
||||||
self.passwordHintEmail(self,
|
|
||||||
REQUEST=REQUEST,
|
|
||||||
username=username,
|
|
||||||
hint=passwordHint,
|
|
||||||
realname=userName,
|
|
||||||
email=userEmail)
|
|
||||||
else:
|
|
||||||
# make a new password, and mail it to the user
|
|
||||||
password = self.generatePassword()
|
|
||||||
curCrypt=self.currentAuthSource.cryptPassword(username,password)
|
|
||||||
|
|
||||||
# Update the user
|
|
||||||
bogusREQUEST={}
|
|
||||||
#bogusREQUEST['username']=username
|
|
||||||
bogusREQUEST['password']=password
|
|
||||||
bogusREQUEST['password_confirm']=password
|
|
||||||
bogusREQUEST['roles']=curUser.roles
|
|
||||||
self.manage_editUser(username, bogusREQUEST)
|
|
||||||
|
|
||||||
self.forgotPasswordEmail(self,
|
|
||||||
REQUEST=REQUEST,
|
|
||||||
username=username,
|
|
||||||
password=password,
|
|
||||||
realname=userName,
|
|
||||||
email=userEmail)
|
|
||||||
return self.MessageDialog(self,
|
|
||||||
title ='Sent!',
|
|
||||||
message="Password details have been emailed to you",
|
|
||||||
action ='%s/%s'%(self.baseURL, self.loginPage),
|
|
||||||
REQUEST=REQUEST)
|
|
||||||
|
|
||||||
|
|
||||||
def changeProperties(self, REQUEST):
|
|
||||||
|
|
||||||
curUser=self.listOneUser(REQUEST['AUTHENTICATED_USER'].getUserName())
|
|
||||||
curUser=curUser[0]
|
|
||||||
if not curUser:
|
|
||||||
return self.MessageDialog(self,
|
|
||||||
title ='Erm!',
|
|
||||||
message="You don't seem to be logged in",
|
|
||||||
action ='%s/%s'%(self.baseURL, self.passwordPage),
|
|
||||||
REQUEST=REQUEST)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
self.currentPropSource.updateUser(curUser['username'],REQUEST)
|
|
||||||
|
|
||||||
return self.MessageDialog(self,
|
|
||||||
title ='Properties updated',
|
|
||||||
message="Your properties have been updated",
|
|
||||||
action =self.baseURL,
|
|
||||||
REQUEST=REQUEST,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def changePassword(self, REQUEST):
|
|
||||||
if not self.usersCanChangePasswords:
|
|
||||||
return ''
|
|
||||||
|
|
||||||
curUser=self.listOneUser(REQUEST['AUTHENTICATED_USER'].getUserName())
|
|
||||||
curUser=curUser[0]
|
|
||||||
if not curUser:
|
|
||||||
return self.MessageDialog(
|
|
||||||
title ='Erm!',
|
|
||||||
message="You don't seem to be logged in",
|
|
||||||
action ='%s/%s'%(self.baseURL, self.passwordPage),
|
|
||||||
REQUEST=REQUEST)
|
|
||||||
|
|
||||||
curCrypt=self.currentAuthSource.cryptPassword(curUser['username'],REQUEST['current_password'])
|
|
||||||
if curCrypt != curUser['password']:
|
|
||||||
return self.MessageDialog(self,
|
|
||||||
title ='Password Mismatch',
|
|
||||||
message="Password is incorrect",
|
|
||||||
action ='%s/%s'%(self.baseURL, self.passwordPage),
|
|
||||||
REQUEST=REQUEST)
|
|
||||||
|
|
||||||
if REQUEST['password'] != REQUEST['password_confirm']:
|
|
||||||
return self.MessageDialog(self,
|
|
||||||
title ='Password Mismatch',
|
|
||||||
message="Passwords do not match",
|
|
||||||
action ='%s/%s'%(self.baseURL, self.passwordPage),
|
|
||||||
REQUEST=REQUEST)
|
|
||||||
|
|
||||||
# OK the old password matches the one the user provided
|
|
||||||
# Both new passwords match...
|
|
||||||
# Time to validate against our normal set of rules...
|
|
||||||
#
|
|
||||||
if not self.validatePassword(REQUEST['password'], curUser['username']):
|
|
||||||
return self.MessageDialog(self,
|
|
||||||
title ='Password problem',
|
|
||||||
message="Your password is invalid, please choose another",
|
|
||||||
action ='%s/%s'%(self.baseURL, self.passwordPage),
|
|
||||||
REQUEST=REQUEST)
|
|
||||||
|
|
||||||
if self.passwordPolicy=='hint':
|
|
||||||
if not hasattr(REQUEST,'user_passwordhint'):
|
|
||||||
return self.MessageDialog(self,
|
|
||||||
title ='Password requires hint',
|
|
||||||
message='You must choose a password hint',
|
|
||||||
action ='%s/%s'%(self.baseURL, self.passwordPage),
|
|
||||||
REQUEST=REQUEST)
|
|
||||||
|
|
||||||
bogusREQUEST={}
|
|
||||||
|
|
||||||
bogusREQUEST['password']=REQUEST['password']
|
|
||||||
bogusREQUEST['password_confirm']=REQUEST['password']
|
|
||||||
bogusREQUEST['roles']=curUser['roles']
|
|
||||||
self.manage_editUser(curUser['username'],bogusREQUEST)
|
|
||||||
# update the cookie so he doesnt have to re-login:
|
|
||||||
if self.cookie_mode:
|
|
||||||
token='%s:%s' %(curUser['username'], REQUEST['password'])
|
|
||||||
token=encodestring(token)
|
|
||||||
token=quote(token)
|
|
||||||
REQUEST.response.setCookie('__ac', token, path='/')
|
|
||||||
REQUEST['__ac']=token
|
|
||||||
|
|
||||||
return self.MessageDialog(self,
|
|
||||||
title ='Password updated',
|
|
||||||
message="Your password has been updated",
|
|
||||||
action =self.baseURL,
|
|
||||||
REQUEST=REQUEST)
|
|
||||||
|
|
||||||
|
|
||||||
def goHome(self, REQUEST, RESPONSE):
|
|
||||||
redirectstring="%s/%s/%s/manage_main"%(self.baseURL, self.homeRoot, REQUEST.AUTHENTICATED_USER.getUserName())
|
|
||||||
RESPONSE.redirect(redirectstring)
|
|
||||||
return ''
|
|
||||||
|
|
||||||
# Tell exUserFolder where we want to go...
|
|
||||||
def getLoginDestination(self, REQUEST):
|
|
||||||
script=''
|
|
||||||
pathinfo=''
|
|
||||||
querystring=''
|
|
||||||
redirectstring=''
|
|
||||||
if self.postLogin=="destination":
|
|
||||||
script=REQUEST['SCRIPT_NAME']
|
|
||||||
pathinfo=REQUEST['PATH_INFO']
|
|
||||||
elif self.postLogin=="varied":
|
|
||||||
script=self.baseURL
|
|
||||||
pathinfo="/acl_users/goHome"
|
|
||||||
|
|
||||||
elif self.postLogin=="fixed":
|
|
||||||
pathinfo="%s"%(self.fixedDest)
|
|
||||||
|
|
||||||
if REQUEST.has_key('QUERY_STRING'):
|
|
||||||
querystring='?'+REQUEST['QUERY_STRING']
|
|
||||||
|
|
||||||
redirectstring=script+pathinfo
|
|
||||||
if querystring:
|
|
||||||
redirectstring=redirectstring+querystring
|
|
||||||
|
|
||||||
return redirectstring
|
|
||||||
|
|
||||||
def validatePassword(self, password, username):
|
|
||||||
if 'minlength' in self.pvFeatures:
|
|
||||||
if len(password) < self.minLength:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
if 'mixedcase' in self.pvFeatures:
|
|
||||||
lower = 0
|
|
||||||
upper = 0
|
|
||||||
for c in password:
|
|
||||||
if c in string.lowercase:
|
|
||||||
lower = 1
|
|
||||||
if c in string.uppercase:
|
|
||||||
upper = 1
|
|
||||||
if not upper and lower:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
if 'specialchar' in self.pvFeatures:
|
|
||||||
special = 0
|
|
||||||
for c in password:
|
|
||||||
if c in string.punctuation:
|
|
||||||
special = 1
|
|
||||||
break
|
|
||||||
elif c in string.digits:
|
|
||||||
special = 1
|
|
||||||
break
|
|
||||||
if not special:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
#
|
|
||||||
# XXX Move this somewhere else
|
|
||||||
#
|
|
||||||
|
|
||||||
if 'notstupid' in self.pvFeatures:
|
|
||||||
email=''
|
|
||||||
# We try some permutations here...
|
|
||||||
curUser=self.getUser(username)
|
|
||||||
if curUser:
|
|
||||||
email = curUser.getProperty('email')
|
|
||||||
elif hasattr(self, 'REQUEST'):
|
|
||||||
if self.REQUEST.has_key('user_email'): # new signup
|
|
||||||
email=self.REQUEST['user_email']
|
|
||||||
elif self.REQUEST.has_key('email'):
|
|
||||||
email=self.REQUEST['email']
|
|
||||||
|
|
||||||
if ((string.find(password, username)>=0) or
|
|
||||||
( email and
|
|
||||||
(string.find(password,
|
|
||||||
string.split(email,'@')[0]) >=0))):
|
|
||||||
return 0
|
|
||||||
return 1
|
|
||||||
|
|
||||||
# These next two look the same (and they are for now), but, the reason I
|
|
||||||
# Don't use one single method, is I think that SkelObj might migrate to
|
|
||||||
# using full paths, not relative paths.
|
|
||||||
|
|
||||||
def findSkelRootObject(self):
|
|
||||||
# Parent should be acl_users
|
|
||||||
parent = getattr(self, 'aq_parent')
|
|
||||||
|
|
||||||
# This should be the root...
|
|
||||||
root = getattr(parent, 'aq_parent')
|
|
||||||
searchPaths = string.split(self.copyFilesFrom, '/')
|
|
||||||
for o in searchPaths:
|
|
||||||
if not getattr(root, o):
|
|
||||||
break
|
|
||||||
root = getattr(root, o)
|
|
||||||
|
|
||||||
self.homeSkelObj=root
|
|
||||||
|
|
||||||
def findHomeRootObject(self):
|
|
||||||
# Parent should be acl_users
|
|
||||||
parent = getattr(self, 'aq_parent')
|
|
||||||
|
|
||||||
# This should be the root...
|
|
||||||
root = getattr(parent, 'aq_parent')
|
|
||||||
|
|
||||||
searchPaths = string.split(self.homeRoot, '/')
|
|
||||||
for o in searchPaths:
|
|
||||||
if o not in root.objectIds():
|
|
||||||
root.manage_addFolder(id=o, title=o, createPublic=0, createUserF=0)
|
|
||||||
root = getattr(root, o)
|
|
||||||
|
|
||||||
self.homeRootObj=root
|
|
||||||
|
|
||||||
def makeHomeDir(self, username):
|
|
||||||
if not self.homeRootObj:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.homeRootObj.manage_addFolder(id=username, title=username, createPublic=0, createUserF=0)
|
|
||||||
home = getattr(self.homeRootObj, username)
|
|
||||||
|
|
||||||
|
|
||||||
# Allow user to be in charge of their own destiny
|
|
||||||
# XXXX WARNING THIS IS A NORMAL FOLDER *SO USERS CAN ADD ANYTHING*
|
|
||||||
# YOU NEED TO CHANGE THE TYPE OF OBJECT ADDED FOR A USER UNLESS
|
|
||||||
# THIS IS WHAT YOU WANT TO HAPPEN
|
|
||||||
home.manage_addLocalRoles(userid=username, roles=['Manager'])
|
|
||||||
|
|
||||||
if self.copyFilesFrom and self.homeSkelObj and self.homeSkelObj.objectIds():
|
|
||||||
cp=self.homeSkelObj.manage_copyObjects(
|
|
||||||
self.homeSkelObj.objectIds())
|
|
||||||
home.manage_pasteObjects(cp)
|
|
||||||
|
|
||||||
# Fix it so the user owns their stuff
|
|
||||||
curUser=self.getUser(username).__of__(self.aq_parent)
|
|
||||||
home.changeOwnership(curUser, recursive=1)
|
|
||||||
|
|
||||||
def generatePassword(self):
|
|
||||||
password = (choice(nouns) + choice(pastConjs) +
|
|
||||||
choice(nouns) + choice(suffixes))
|
|
||||||
return password
|
|
||||||
|
|
||||||
def createUser(self, REQUEST):
|
|
||||||
if self.passwordPolicy == 'user':
|
|
||||||
if not self.validatePassword(REQUEST['password'], REQUEST['username']):
|
|
||||||
return self.MessageDialog(self,
|
|
||||||
title ='Password problem',
|
|
||||||
message='Your password is invalid, please choose another',
|
|
||||||
action ='%s/%s'%(self.baseURL, self.signupPage),
|
|
||||||
REQUEST=REQUEST)
|
|
||||||
|
|
||||||
if self.passwordPolicy=='hint':
|
|
||||||
if not hasattr(REQUEST,'user_passwordhint'):
|
|
||||||
return self.MessageDialog(self,
|
|
||||||
title ='Password requires hint',
|
|
||||||
message='You must choose a password hint',
|
|
||||||
action ='%s/%s'%(self.baseURL, self.signupPage),
|
|
||||||
REQUEST=REQUEST)
|
|
||||||
|
|
||||||
elif self.passwordPolicy == 'system':
|
|
||||||
REQUEST['password']=self.generatePassword()
|
|
||||||
REQUEST['password_confirm']=REQUEST['password']
|
|
||||||
|
|
||||||
# Email the password.
|
|
||||||
self.newPasswordEmail(self, REQUEST)
|
|
||||||
|
|
||||||
zLOG.LOG("exUserFolder.basicMemberSource", zLOG.BLATHER,
|
|
||||||
"Creating user",
|
|
||||||
"Passed all tests -- creating [%s]" % REQUEST['username'])
|
|
||||||
REQUEST['roles']=self.defaultRoles
|
|
||||||
self.manage_addUser(REQUEST) # Create the User...
|
|
||||||
if self.createHomeDir:
|
|
||||||
self.makeHomeDir(REQUEST['username'])
|
|
||||||
|
|
||||||
return self.MessageDialog(self,
|
|
||||||
title ='You have signed up',
|
|
||||||
message='You have been signed up succesfully',
|
|
||||||
action ='%s'%(self.baseURL),
|
|
||||||
REQUEST=REQUEST)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
basicMemberReg=PluginRegister('basicMemberSource',
|
|
||||||
'Basic Membership Source',
|
|
||||||
BasicMemberSource,
|
|
||||||
manage_addBasicMemberSourceForm,
|
|
||||||
manage_addBasicMemberSource,
|
|
||||||
manage_editBasicMemberSourceForm)
|
|
||||||
exUserFolder.membershipSources['basicMemberSource']=basicMemberReg
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
|||||||
<dtml-sendmail mailhost=mailHostObject>
|
|
||||||
To: <dtml-var realname> <<dtml-var email>>
|
|
||||||
From: <dtml-var siteName> <<dtml-var siteEmail>>
|
|
||||||
Subject: You forgot your password for <dtml-var siteName>
|
|
||||||
|
|
||||||
Dear <dtml-var realname>,
|
|
||||||
|
|
||||||
Your username is <dtml-var username> and your password is now
|
|
||||||
<dtml-var password>.
|
|
||||||
|
|
||||||
You should have tested this first, and now that you've tested it, you'll
|
|
||||||
see you need to customise this method.
|
|
||||||
|
|
||||||
</dtml-sendmail>
|
|
||||||
|
|
@ -1,143 +0,0 @@
|
|||||||
<dtml-var "DialogHeader(_.None, _, DialogTitle='Add Basic Membership Source', dialog_width='')">
|
|
||||||
<b><dtml-babel src="'en'">Membership requires a valid property source, you cannot use this with NULL Property Source</dtml-babel></b>
|
|
||||||
<FORM ACTION="&dtml-URL;" METHOD="POST">
|
|
||||||
<dtml-in "REQUEST.form.keys()">
|
|
||||||
<dtml-if "getVariableType(REQUEST[_['sequence-item']]) == 'List'">
|
|
||||||
<dtml-let listVar=sequence-item>
|
|
||||||
<dtml-in "REQUEST[listVar]">
|
|
||||||
<input type="HIDDEN" name="<dtml-var listVar>:list" value="<dtml-var sequence-item>">
|
|
||||||
</dtml-in>
|
|
||||||
</dtml-let>
|
|
||||||
<dtml-else>
|
|
||||||
<input type="HIDDEN" name="<dtml-var sequence-item>" value="<dtml-var "REQUEST[_.getitem('sequence-item',0)]">">
|
|
||||||
</dtml-if>
|
|
||||||
|
|
||||||
</dtml-in>
|
|
||||||
|
|
||||||
<input type="HIDDEN" name="doGroup" value="1">
|
|
||||||
<table cellspacing="2">
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Site Name (used in emails)</dtml-babel></b></td>
|
|
||||||
<td><input type="text" name="basicmember_sitename">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Site Email (used for emails)</dtml-babel></b></td>
|
|
||||||
<td><input type="text" name="basicmember_siteemail">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Mail Host</dtml-babel></b></td>
|
|
||||||
<td>
|
|
||||||
<select name="basicmember_mailhost">
|
|
||||||
<dtml-in "MailHostIDs()">
|
|
||||||
<option value="<dtml-var sequence-item>">
|
|
||||||
<dtml-var sequence-key></option>
|
|
||||||
</dtml-in>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Site Base</dtml-babel></b></td>
|
|
||||||
<td><input type="text" name="basicmember_baseurl"
|
|
||||||
value="<dtml-var "absolute_url()">">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Relative Path (from base) of Login Page</dtml-babel></b></td>
|
|
||||||
<td><input type="text" name="basicmember_loginpage" value="LoginForm">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Relative Path (from base) of Signup Page</dtml-babel></b></td>
|
|
||||||
<td><input type="text" name="basicmember_signuppage" value="SignupForm">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Relative Path (from base) of Change Password Page</dtml-babel></b></td>
|
|
||||||
<td><input type="text" name="basicmember_passwordpage" value="ChangePasswordForm">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Password Validation Features</dtml-babel></b></td>
|
|
||||||
<td>
|
|
||||||
<select name="basicmember_pvfeatures:list" multiple>
|
|
||||||
<option value="minlength">Minimum Length</option>
|
|
||||||
<option value="mixedcase">Must have Mixed Case</option>
|
|
||||||
<option value="specichar">Must have Special Chars</option>
|
|
||||||
<option value="notstupid">Not Stupid (username/email/part of name)</option>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Minimum Length (0 if not required)</dtml-babel></b></td>
|
|
||||||
<td>
|
|
||||||
<input type="text" name="basicmember_minpasslen:int" value="0">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Password Policy</dtml-babel></b></td>
|
|
||||||
<td>
|
|
||||||
<select name="basicmember_passwordpolicy">
|
|
||||||
<option value="user">User Chooses</option>
|
|
||||||
<option value="system">System Chooses and emails User</option>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Forgotten Passwords</dtml-babel></b></td>
|
|
||||||
<td>
|
|
||||||
<select name="basicmember_forgottenpasswords">
|
|
||||||
<option value="hint"><dtml-babel src="'en'">Email a Hint</dtml-babel></option>
|
|
||||||
<option value="reset"><dtml-babel src="'en'">Reset and Email New password</dtml-babel></option>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Allow users to change passwords</dtml-babel></b></td>
|
|
||||||
<td>
|
|
||||||
<input type="checkbox" name="basicmember_changepasswords" checked><dtml-babel src="'en'">Yes</dtml-babel>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Create 'Home Directory'</dtml-babel></b></td>
|
|
||||||
<td>
|
|
||||||
<input type="checkbox" name="basicmember_createhomedir"><dtml-babel src="'en'">Yes</dtml-babel>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Relative Path to 'Home Directory' Root</dtml-babel></b></td>
|
|
||||||
<td>
|
|
||||||
<input type="text" name="basicmember_homeroot" value="Members">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Copy initial 'Home Directory' files from...(empty=No Copy)</dtml-babel></b></td>
|
|
||||||
<td>
|
|
||||||
<input type="text" name="basicmember_copyfiles", value="<dtml-var "_.string.join(getPhysicalPath()[1:], '/')">">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">After login....</dtml-babel></b></td>
|
|
||||||
<td>
|
|
||||||
<select name="basicmember_postlogin">
|
|
||||||
<option value="destination"><dtml-babel src="'en'">Go to intended destination</dtml-babel></option>
|
|
||||||
<option value="fixed"><dtml-babel src="'en'">Go to fixed destination</dtml-babel></option>
|
|
||||||
<option value="varied"><dtml-babel src="'en'">Go to Home Directory</dtml-babel></option>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Fixed Destination</dtml-babel></b></td>
|
|
||||||
<td>
|
|
||||||
<input type="text" name="basicmember_fixeddest">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" align="right"><b><dtml-babel src="'en'">Default Roles</dtml-babel></b></td>
|
|
||||||
<td align="left" valign="top">
|
|
||||||
<select name="basicmember_roles:list" size="5" multiple>
|
|
||||||
<dtml-in valid_roles>
|
|
||||||
<dtml-if expr="_vars['sequence-item'] != 'Anonymous'">
|
|
||||||
<dtml-if expr="_vars['sequence-item'] != 'Authenticated'">
|
|
||||||
<dtml-if expr="_vars['sequence-item'] != 'Shared'">
|
|
||||||
<option value="<dtml-var sequence-item html_quote>"><dtml-var sequence-item>
|
|
||||||
</dtml-if>
|
|
||||||
</dtml-if>
|
|
||||||
</dtml-if>
|
|
||||||
</dtml-in valid_roles>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<input type="SUBMIT" value="<dtml-babel src="'en'">Add</dtml-babel>">
|
|
||||||
</form>
|
|
||||||
<dtml-var DialogFooter>
|
|
@ -1,116 +0,0 @@
|
|||||||
<dtml-var "DialogHeader(_.None, _, DialogTitle='Edit Basic Membership Source', dialog_width='')">
|
|
||||||
<dtml-var manage_tabs>
|
|
||||||
<FORM ACTION="manage_editMembershipSource" METHOD="POST">
|
|
||||||
<dtml-with currentMembershipSource>
|
|
||||||
<table cellspacing="2">
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Site Name (used in emails)</dtml-babel></b></td>
|
|
||||||
<td><input type="text" name="sitename" value="&dtml.missing-siteName;">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Site Email (used for emails)</dtml-babel></b></td>
|
|
||||||
<td><input type="text" name="siteemail" value="&dtml.missing-siteEmail;">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Mail Host</dtml-babel></b></td>
|
|
||||||
<td>
|
|
||||||
<select name="mailhost">
|
|
||||||
<dtml-in "MailHostIDs()">
|
|
||||||
<option value="<dtml-var sequence-item>"<dtml-if "mailHost==_.getitem('sequence-item',0)"> selected</dtml-if>><dtml-var sequence-key></option>
|
|
||||||
</dtml-in>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Site Base</dtml-babel></b></td>
|
|
||||||
<td><input type="text" name="baseurl"
|
|
||||||
value="&dtml.missing-baseURL;">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Relative Path (from base) of Login Page</dtml-babel></b></td>
|
|
||||||
<td><input type="text" name="loginpage" value="&dtml.missing-loginPage;">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr><td align="right"><b><dtml-babel>Relative Path (from base) of Signup Page</dtml-babel></b></td>
|
|
||||||
<td><input type="text" name="signuppage" value="&dtml.missing-signupPage;">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Relative Path (from base) of Change Password Page</dtml-babel></b></td>
|
|
||||||
<td><input type="text" name="passwordpage" value="&dtml.missing-passwordPage;">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Password Validation Features</dtml-babel></b></td>
|
|
||||||
<td>
|
|
||||||
<select name="pvfeatures:list" multiple>
|
|
||||||
<option value="minlength" <dtml-if "'minlength' in pvFeatures"> selected</dtml-if>><dtml-babel src="'en'">Minimum Length</dtml-babel></option>
|
|
||||||
<option value="mixedcase" <dtml-if "'mixedcase' in pvFeatures"> selected</dtml-if>><dtml-babel src="'en'">Must have Mixed Case</dtml-babel></option>
|
|
||||||
<option value="specichar" <dtml-if "'specichar' in pvFeatures"> selected</dtml-if>><dtml-babel src="'en'">Must have Special Chars</dtml-babel></option>
|
|
||||||
<option value="notstupid" <dtml-if "'notstupid' in pvFeatures"> selected</dtml-if>><dtml-babel src="'en'">Not Stupid (username/email/part of name)</dtml-babel></option>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Minimum Length (if required)</dtml-babel></b></td>
|
|
||||||
<td>
|
|
||||||
<input type="text" name="minpasslen:int" value="&dtml.missing-minLength;">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Allow users to change passwords</dtml-babel></b></td>
|
|
||||||
<td>
|
|
||||||
<input type="checkbox" name="changepasswords"<dtml-if usersCanChangePasswords> checked</dtml-if>><dtml-babel src="'en'">Yes</dtml-babel>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Create 'Home Directory'</dtml-babel></b></td>
|
|
||||||
<td>
|
|
||||||
<input type="checkbox" name="createhomedir"<dtml-if createHomeDir> checked</dtml-if>><dtml-babel src="'en'">Yes</dtml-babel>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Path to 'Home Directory' Root</dtml-babel></b></td>
|
|
||||||
<td>
|
|
||||||
<input type="text" name="homeroot" value="&dtml.missing-homeRoot;">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Copy initial 'Home Directory' files from...(empty=No Copy)</dtml-babel></b></td>
|
|
||||||
<td>
|
|
||||||
<input type="text" name="copyfiles" value="&dtml.missing-copyFilesFrom;">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">After login....</dtml-babel></b></td>
|
|
||||||
<td>
|
|
||||||
<select name="postlogin">
|
|
||||||
<option value="destination"<dtml-if "postLogin=='destination'"> selected</dtml-if>><dtml-babel src="'en'">Go to intended destination</dtml-babel></option>
|
|
||||||
<option value="fixed"<dtml-if "postLogin=='fixed'"> selected</dtml-if>><dtml-babel src="'en'">Go to fixed destination</dtml-babel></option>
|
|
||||||
<option value="varied"<dtml-if "postLogin=='varied'"> selected</dtml-if>><dtml-babel src="'en'">Go to Home Directory</dtml-babel></option>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr><td align="right"><b><dtml-babel src="'en'">Fixed Destination</dtml-babel></b></td>
|
|
||||||
<td>
|
|
||||||
<input type="text" name="fixeddest" value="&dtml.missing-fixedDest;">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" align="right"><b><dtml-babel src="'en'">Default Roles</dtml-babel></b></td>
|
|
||||||
<td align="left" valign="top">
|
|
||||||
<select name="memberroles:list" size="5" multiple>
|
|
||||||
<dtml-in valid_roles>
|
|
||||||
<dtml-if expr="_vars['sequence-item'] != 'Anonymous'">
|
|
||||||
<dtml-if expr="_vars['sequence-item'] != 'Authenticated'">
|
|
||||||
<dtml-if expr="_vars['sequence-item'] != 'Shared'">
|
|
||||||
<option value="<dtml-var sequence-item html_quote>"<dtml-if "_['sequence-item'] in defaultRoles"> selected</dtml-if>><dtml-var sequence-item>
|
|
||||||
</dtml-if>
|
|
||||||
</dtml-if>
|
|
||||||
</dtml-if>
|
|
||||||
</dtml-in valid_roles>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<input type="SUBMIT" value=" <dtml-babel src="'en'">Update</dtml-babel> ">
|
|
||||||
</dtml-with>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<dtml-var DialogFooter>
|
|
@ -1,16 +0,0 @@
|
|||||||
<dtml-sendmail mailhost=mailHostObject>
|
|
||||||
To: <dtml-var user_realname> <<dtml-var user_email>>
|
|
||||||
From: <dtml-var siteName> <<dtml-var siteEmail>>
|
|
||||||
Subject: Welcome to <dtml-var siteName>
|
|
||||||
|
|
||||||
Dear <dtml-var user_realname>,
|
|
||||||
|
|
||||||
Welcome to <dtml-var siteName>.
|
|
||||||
|
|
||||||
Your username is <dtml-var username> and your password is <dtml-var password>.
|
|
||||||
|
|
||||||
You should have tested this first, and now that you've tested it, you'll
|
|
||||||
see you need to customise this method.
|
|
||||||
|
|
||||||
</dtml-sendmail>
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
|||||||
<dtml-sendmail mailhost=mailHostObject>
|
|
||||||
To: <dtml-var realname> <<dtml-var email>>
|
|
||||||
From: <dtml-var siteName> <<dtml-var siteEmail>>
|
|
||||||
Subject: Hint for <dtml-var siteName>
|
|
||||||
|
|
||||||
Dear <dtml-var realname>,
|
|
||||||
|
|
||||||
Your username is <dtml-var username> and your password hint was;
|
|
||||||
<dtml-var hint>.
|
|
||||||
|
|
||||||
You should have tested this first, and now that you've tested it, you'll
|
|
||||||
see you need to customise this method.
|
|
||||||
|
|
||||||
</dtml-sendmail>
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
|||||||
*.pyc
|
|
||||||
*.pyo
|
|
||||||
*~
|
|
||||||
.*.swp
|
|
@ -1,2 +0,0 @@
|
|||||||
# $Id: __init__.py,v 1.1 2004/11/10 14:15:55 akm Exp $
|
|
||||||
import nullMemberSource
|
|
@ -1,21 +0,0 @@
|
|||||||
<dtml-var "DialogHeader(_.None, _, DialogTitle='Add Basic Membership Source', dialog_width='')">
|
|
||||||
<FORM ACTION="&dtml-URL;" METHOD="POST">
|
|
||||||
<dtml-in "REQUEST.form.keys()">
|
|
||||||
<dtml-if "getVariableType(REQUEST[_['sequence-item']]) == 'List'">
|
|
||||||
<dtml-let listVar=sequence-item>
|
|
||||||
<dtml-in "REQUEST[listVar]">
|
|
||||||
<input type="HIDDEN" name="<dtml-var listVar>:list" value="<dtml-var sequence-item>">
|
|
||||||
</dtml-in>
|
|
||||||
</dtml-let>
|
|
||||||
<dtml-else>
|
|
||||||
<input type="HIDDEN" name="<dtml-var sequence-item>" value="<dtml-var "REQUEST[_.getitem('sequence-item',0)]">">
|
|
||||||
</dtml-if>
|
|
||||||
|
|
||||||
</dtml-in>
|
|
||||||
|
|
||||||
<input type="HIDDEN" name="doGroup" value="1">
|
|
||||||
<b><dtml-babel src="'en'">This Membership Source has no configuration Items</dtml-babel></b><br>
|
|
||||||
<br>
|
|
||||||
<input type="SUBMIT" value="<dtml-babel src="'en'">Add</dtml-babel>">
|
|
||||||
</form>
|
|
||||||
<dtml-var DialogFooter>
|
|
@ -1,49 +0,0 @@
|
|||||||
#
|
|
||||||
# Extensible User Folder
|
|
||||||
#
|
|
||||||
# Null Membership Source for exUserFolder
|
|
||||||
#
|
|
||||||
# (C) Copyright 2000,2001 The Internet (Aust) Pty Ltd
|
|
||||||
# ACN: 082 081 472 ABN: 83 082 081 472
|
|
||||||
# All Rights Reserved
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
||||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
# SUCH DAMAGE.
|
|
||||||
#
|
|
||||||
# Author: Andrew Milton <akm@theinternet.com.au>
|
|
||||||
# $Id: nullMemberSource.py,v 1.1 2004/11/10 14:15:55 akm Exp $
|
|
||||||
from Globals import HTMLFile, INSTANCE_HOME
|
|
||||||
|
|
||||||
from OFS.Folder import Folder
|
|
||||||
|
|
||||||
from Products.exUserFolder.exUserFolder import exUserFolder
|
|
||||||
from Products.exUserFolder.Plugins import PluginRegister
|
|
||||||
from Products.exUserFolder.nullPlugin import nullPlugin
|
|
||||||
|
|
||||||
def manage_addNullMemberSource(self, REQUEST):
|
|
||||||
""" Add a Membership Source """
|
|
||||||
self.currentMembershipSource=None
|
|
||||||
return ''
|
|
||||||
|
|
||||||
|
|
||||||
manage_addNullMemberSourceForm=HTMLFile('manage_addNullPluginSourceForm',globals())
|
|
||||||
manage_editNullMemberSourceForm=None
|
|
||||||
|
|
||||||
|
|
||||||
nullMemberReg=PluginRegister('nullMemberSource',
|
|
||||||
'Null Membership Source',
|
|
||||||
nullPlugin,
|
|
||||||
manage_addNullMemberSourceForm,
|
|
||||||
manage_addNullMemberSource,
|
|
||||||
manage_editNullMemberSourceForm)
|
|
||||||
exUserFolder.membershipSources['nullMemberSource']=nullMemberReg
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
|||||||
#
|
|
||||||
#
|
|
||||||
# (C) Copyright 2001 The Internet (Aust) Pty Ltd
|
|
||||||
# ACN: 082 081 472 ABN: 83 082 081 472
|
|
||||||
# All Rights Reserved
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
||||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
# SUCH DAMAGE.
|
|
||||||
#
|
|
||||||
# Author: Andrew Milton <akm@theinternet.com.au>
|
|
||||||
# $Id: Plugins.py,v 1.5 2004/11/10 14:15:33 akm Exp $
|
|
||||||
|
|
||||||
import App, Globals, OFS
|
|
||||||
import string
|
|
||||||
import time
|
|
||||||
|
|
||||||
from Globals import ImageFile, HTMLFile, HTML, MessageDialog, package_home
|
|
||||||
from OFS.Folder import Folder
|
|
||||||
|
|
||||||
class PluginRegister:
|
|
||||||
def __init__(self, name, description, pluginClass,
|
|
||||||
pluginStartForm, pluginStartMethod,
|
|
||||||
pluginEditForm=None, pluginEditMethod=None):
|
|
||||||
self.name=name #No Spaces please...
|
|
||||||
self.description=description
|
|
||||||
self.plugin=pluginClass
|
|
||||||
self.manage_addForm=pluginStartForm
|
|
||||||
self.manage_addMethod=pluginStartMethod
|
|
||||||
self.manage_editForm=pluginEditForm
|
|
||||||
self.manage_editMethod=pluginEditMethod
|
|
||||||
|
|
||||||
class CryptoPluginRegister:
|
|
||||||
def __init__(self, name, crypto, description, pluginMethod):
|
|
||||||
self.name = name #No Spaces please...
|
|
||||||
self.cryptoMethod = crypto
|
|
||||||
self.description = description
|
|
||||||
self.plugin = pluginMethod
|
|
@ -1,28 +0,0 @@
|
|||||||
#
|
|
||||||
# Extensible User Folder
|
|
||||||
#
|
|
||||||
# (C) Copyright 2000-2004 The Internet (Aust) Pty Ltd
|
|
||||||
# ACN: 082 081 472 ABN: 83 082 081 472
|
|
||||||
# All Rights Reserved
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
||||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
# SUCH DAMAGE.
|
|
||||||
#
|
|
||||||
# Author: Andrew Milton <akm@theinternet.com.au>
|
|
||||||
# $Id: __init__.py,v 1.1 2004/11/10 14:15:55 akm Exp $
|
|
||||||
|
|
||||||
|
|
||||||
import nullPropSource
|
|
||||||
# aucune autre prop source pour ScoDoc
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
|||||||
*.pyc
|
|
||||||
*.pyo
|
|
||||||
*~
|
|
||||||
.*.swp
|
|
@ -1,2 +0,0 @@
|
|||||||
# $Id: __init__.py,v 1.1 2004/11/10 14:15:56 akm Exp $
|
|
||||||
import nullPropSource
|
|
@ -1,21 +0,0 @@
|
|||||||
<dtml-var "DialogHeader(_.None, _, DialogTitle='Add Basic Property Source', dialog_width='')">
|
|
||||||
<FORM ACTION="&dtml-URL;" METHOD="POST">
|
|
||||||
<dtml-in "REQUEST.form.keys()">
|
|
||||||
<dtml-if "getVariableType(REQUEST[_['sequence-item']]) == 'List'">
|
|
||||||
<dtml-let listVar=sequence-item>
|
|
||||||
<dtml-in "REQUEST[listVar]">
|
|
||||||
<input type="HIDDEN" name="<dtml-var listVar>:list" value="<dtml-var sequence-item>">
|
|
||||||
</dtml-in>
|
|
||||||
</dtml-let>
|
|
||||||
<dtml-else>
|
|
||||||
<input type="HIDDEN" name="<dtml-var sequence-item>" value="<dtml-var "REQUEST[_.getitem('sequence-item',0)]">">
|
|
||||||
</dtml-if>
|
|
||||||
|
|
||||||
</dtml-in>
|
|
||||||
|
|
||||||
<input type="HIDDEN" name="doMember" value="1">
|
|
||||||
<b><dtml-babel src="'en'">This Property Source has no configuration Items</dtml-babel></b><br>
|
|
||||||
<br>
|
|
||||||
<input type="SUBMIT" value="<dtml-babel src="'en'">Add</dtml-babel>">
|
|
||||||
</form>
|
|
||||||
<dtml-var DialogFooter>
|
|
@ -1,50 +0,0 @@
|
|||||||
#
|
|
||||||
# Extensible User Folder
|
|
||||||
#
|
|
||||||
# Null Membership Source for exUserFolder
|
|
||||||
#
|
|
||||||
# (C) Copyright 2000,2001 The Internet (Aust) Pty Ltd
|
|
||||||
# ACN: 082 081 472 ABN: 83 082 081 472
|
|
||||||
# All Rights Reserved
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
||||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
# SUCH DAMAGE.
|
|
||||||
#
|
|
||||||
# Author: Andrew Milton <akm@theinternet.com.au>
|
|
||||||
# $Id: nullPropSource.py,v 1.1 2004/11/10 14:15:56 akm Exp $
|
|
||||||
from Globals import HTMLFile, INSTANCE_HOME
|
|
||||||
|
|
||||||
from OFS.Folder import Folder
|
|
||||||
|
|
||||||
from Products.exUserFolder.exUserFolder import exUserFolder
|
|
||||||
from Products.exUserFolder.Plugins import PluginRegister
|
|
||||||
from Products.exUserFolder.nullPlugin import nullPlugin
|
|
||||||
|
|
||||||
def manage_addNullPropSource(self, REQUEST):
|
|
||||||
""" Add a Property Source """
|
|
||||||
self.currentPropSource=None
|
|
||||||
return ''
|
|
||||||
|
|
||||||
|
|
||||||
manage_addNullPropSourceForm=HTMLFile('manage_addNullPluginSourceForm',globals())
|
|
||||||
manage_editNullPropSourceForm=None
|
|
||||||
|
|
||||||
|
|
||||||
nullPropReg=PluginRegister('nullPropSource',
|
|
||||||
'Null Property Source',
|
|
||||||
nullPlugin,
|
|
||||||
manage_addNullPropSourceForm,
|
|
||||||
manage_addNullPropSource,
|
|
||||||
manage_editNullPropSourceForm)
|
|
||||||
|
|
||||||
exUserFolder.propSources['nullPropSource']=nullPropReg
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
|||||||
*.pyc
|
|
||||||
*.pyo
|
|
||||||
*~
|
|
||||||
.*.swp
|
|
@ -1,122 +0,0 @@
|
|||||||
#
|
|
||||||
# Extensible User Folder
|
|
||||||
#
|
|
||||||
# (C) Copyright 2000,2001 The Internet (Aust) Pty Ltd
|
|
||||||
# ACN: 082 081 472 ABN: 83 082 081 472
|
|
||||||
# All Rights Reserved
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
||||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
# SUCH DAMAGE.
|
|
||||||
#
|
|
||||||
# Author: Andrew Milton <akm@theinternet.com.au>
|
|
||||||
# $Id: PropertyEditor.py,v 1.3 2002/01/29 17:42:02 alex_zxc Exp $
|
|
||||||
|
|
||||||
from Globals import DTMLFile, MessageDialog, INSTANCE_HOME
|
|
||||||
from string import join,strip,split,lower,upper,find
|
|
||||||
from urllib import quote, unquote
|
|
||||||
|
|
||||||
|
|
||||||
def editStringProperty( name, value):
|
|
||||||
""" """
|
|
||||||
return('<input type="TEXT" name="%s:string" value="%s">\n'%(name, value))
|
|
||||||
|
|
||||||
def viewStringProperty( name, value):
|
|
||||||
""" """
|
|
||||||
return('<input type="HIDDEN" name="propValue:string" value="%s"><br>%s<br>\n'%(value, value))
|
|
||||||
|
|
||||||
|
|
||||||
def editIntegerProperty( name, value):
|
|
||||||
""" """
|
|
||||||
return('<input type="TEXT" name="%s:int" value="%d">\n'%(name, value or 0))
|
|
||||||
|
|
||||||
def viewIntegerProperty( name, value):
|
|
||||||
""" """
|
|
||||||
return('<input type="HIDDEN" name="propValue:int" value="%d"><br>%d<br>\n'%(value or 0 , value or 0))
|
|
||||||
|
|
||||||
|
|
||||||
def editLongProperty( name, value):
|
|
||||||
""" """
|
|
||||||
return('<input type="TEXT" name="%s:int" value="%d">\n'%(name, value or 0))
|
|
||||||
|
|
||||||
def viewLongProperty( name, value):
|
|
||||||
""" """
|
|
||||||
return('<input type="HIDDEN" name="propValue:long" value="%d"><br>%d<br>\n'%(value or 0, value or 0))
|
|
||||||
|
|
||||||
|
|
||||||
def editFloatProperty( name, value):
|
|
||||||
""" """
|
|
||||||
return('<input type="TEXT" name="%s:float" value="%d">\n'%(name, value))
|
|
||||||
|
|
||||||
def viewFloatProperty( name, value):
|
|
||||||
""" """
|
|
||||||
return('<input type="HIDDEN" name="propValue:float" value="%f"><br>%f<br>\n'%(value, value))
|
|
||||||
|
|
||||||
|
|
||||||
def editListProperty( name, value):
|
|
||||||
a=''
|
|
||||||
if value:
|
|
||||||
a = a + 'Select Items to keep<br>\n'
|
|
||||||
a = a + '<select name="%s:list" multiple>\n'%(name)
|
|
||||||
for i in value:
|
|
||||||
a = a + (
|
|
||||||
'<option value="%s" SELECTED>%s\n'%(i, i))
|
|
||||||
a = a + '</select>\n<br>'
|
|
||||||
a = a + 'Add an item\n<br>'
|
|
||||||
a = a + '<input type="TEXT" name="%s:list">'%(name)
|
|
||||||
return(a)
|
|
||||||
|
|
||||||
def viewListProperty( name, value):
|
|
||||||
a=''
|
|
||||||
if value:
|
|
||||||
for i in value:
|
|
||||||
a = a + (
|
|
||||||
'<input type="HIDDEN" name="propValue:list" value="%s">\n'%(i))
|
|
||||||
a = a + '%s\n<br>'%(i)
|
|
||||||
return(a)
|
|
||||||
|
|
||||||
|
|
||||||
def editDictProperty( name, value):
|
|
||||||
""" """
|
|
||||||
a=''
|
|
||||||
if value and value.keys():
|
|
||||||
for i in value.keys():
|
|
||||||
a = a + '%s : <input type="TEXT" name="%s.%s" value="%s">\n<br>'%(i, name, i, value[i])
|
|
||||||
return a
|
|
||||||
|
|
||||||
|
|
||||||
def viewDictProperty( name, value):
|
|
||||||
""" """
|
|
||||||
a=''
|
|
||||||
if value and value.keys():
|
|
||||||
for i in value.keys():
|
|
||||||
a = a + '%s : <input type="HIDDEN" name="propValue.%s" value="%s">\n<br>'%(i, name, i, value[i])
|
|
||||||
a = a + '%s\n<br>'%(value[i])
|
|
||||||
return a
|
|
||||||
|
|
||||||
EditMethods={'String':editStringProperty,
|
|
||||||
'Integer':editIntegerProperty,
|
|
||||||
'Long':editLongProperty,
|
|
||||||
'Float':editFloatProperty,
|
|
||||||
'List':editListProperty,
|
|
||||||
'Dict':editDictProperty}
|
|
||||||
|
|
||||||
|
|
||||||
ViewMethods={'String':viewStringProperty,
|
|
||||||
'Integer':viewIntegerProperty,
|
|
||||||
'Long':viewLongProperty,
|
|
||||||
'Float':viewFloatProperty,
|
|
||||||
'List':viewListProperty,
|
|
||||||
'Dict':viewDictProperty}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
|||||||
#
|
|
||||||
# Extensible User Folder
|
|
||||||
#
|
|
||||||
# (C) Copyright 2000,2001 The Internet (Aust) Pty Ltd
|
|
||||||
# ACN: 082 081 472 ABN: 83 082 081 472
|
|
||||||
# All Rights Reserved
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
||||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
# SUCH DAMAGE.
|
|
||||||
#
|
|
||||||
# Author: Andrew Milton <akm@theinternet.com.au>
|
|
||||||
# $Id: __init__.py,v 1.2 2001/12/01 08:40:04 akm Exp $
|
|
||||||
import PropertyEditor
|
|
@ -1,14 +0,0 @@
|
|||||||
If you are upgrading an existing site from < 0.50
|
|
||||||
|
|
||||||
I have restructured the source tree. This will make this version
|
|
||||||
incompatible with previous versions, as the classes have moved. This
|
|
||||||
breaks upgrading existing installs unless you keep the old classes
|
|
||||||
around. If you only use external Auth/Prop/Group sources, you will
|
|
||||||
probably be unaffected.
|
|
||||||
|
|
||||||
This means for those of you using SQL or LDAP or any non-ZODB sources,
|
|
||||||
you can remove and then re-add your XUF acl_users to get going again.
|
|
||||||
|
|
||||||
If you are using a ZODB source, then you need to keep the old classes
|
|
||||||
and the old paths around (e.g. symlink zodbAuthSource to
|
|
||||||
AuthSources/zodbAuthSource).
|
|
@ -1,243 +0,0 @@
|
|||||||
#
|
|
||||||
# Extensible User Folder
|
|
||||||
#
|
|
||||||
# (C) Copyright 2000,2001 The Internet (Aust) Pty Ltd
|
|
||||||
# ACN: 082 081 472 ABN: 83 082 081 472
|
|
||||||
# All Rights Reserved
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
||||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
# SUCH DAMAGE.
|
|
||||||
#
|
|
||||||
# Author: Andrew Milton <akm@theinternet.com.au>
|
|
||||||
# $Id: User.py,v 1.10 2004/12/14 05:30:29 akm Exp $
|
|
||||||
|
|
||||||
##############################################################################
|
|
||||||
#
|
|
||||||
# Zope Public License (ZPL) Version 0.9.4
|
|
||||||
# ---------------------------------------
|
|
||||||
#
|
|
||||||
# Copyright (c) Digital Creations. All rights reserved.
|
|
||||||
#
|
|
||||||
# Redistribution and use in source and binary forms, with or
|
|
||||||
# without modification, are permitted provided that the following
|
|
||||||
# conditions are met:
|
|
||||||
#
|
|
||||||
# 1. Redistributions in source code must retain the above
|
|
||||||
# copyright notice, this list of conditions, and the following
|
|
||||||
# disclaimer.
|
|
||||||
#
|
|
||||||
# 6. Redistributions of any form whatsoever must retain the
|
|
||||||
# following acknowledgment:
|
|
||||||
#
|
|
||||||
# "This product includes software developed by Digital
|
|
||||||
# Creations for use in the Z Object Publishing Environment
|
|
||||||
# (http://www.zope.org/)."
|
|
||||||
#
|
|
||||||
# Disclaimer
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND
|
|
||||||
# ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
|
||||||
# SHALL DIGITAL CREATIONS OR ITS CONTRIBUTORS BE LIABLE FOR ANY
|
|
||||||
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
||||||
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
||||||
# THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
#
|
|
||||||
##############################################################################
|
|
||||||
from AccessControl.User import BasicUser
|
|
||||||
from string import join,strip,split,lower,upper,find
|
|
||||||
|
|
||||||
class XUFUser(BasicUser):
|
|
||||||
|
|
||||||
icon='misc_/exUserFolder/exUser.gif'
|
|
||||||
|
|
||||||
# cacheable is a dict that must contain at least name, password,
|
|
||||||
# roles, and domains -- unless you're working with your own User class,
|
|
||||||
# in which case you need to override __init__ and define it yourself.
|
|
||||||
def __init__(self, cacheable, propSource, cryptPassword, authSource,
|
|
||||||
groupSource=None):
|
|
||||||
self.name =cacheable['name']
|
|
||||||
self.__ =cacheable['password']
|
|
||||||
if cacheable['roles']:
|
|
||||||
self.roles = filter(None, cacheable['roles'])
|
|
||||||
else:
|
|
||||||
self.roles = []
|
|
||||||
# domains may be passed as a string or a list
|
|
||||||
if type(cacheable['domains']) == type(''):
|
|
||||||
self.domains=filter(None, map(strip,
|
|
||||||
split(cacheable['domains'], ',')))
|
|
||||||
else:
|
|
||||||
self.domains=cacheable['domains']
|
|
||||||
self._authSource=authSource
|
|
||||||
self._propSource=propSource
|
|
||||||
self._groupSource=groupSource
|
|
||||||
self.cryptPassword=cryptPassword
|
|
||||||
|
|
||||||
def getUserName(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
def _getPassword(self):
|
|
||||||
return self.__
|
|
||||||
|
|
||||||
def getRoles(self):
|
|
||||||
return tuple(self.roles) + ('Authenticated',)
|
|
||||||
|
|
||||||
def getDomains(self):
|
|
||||||
return self.domains
|
|
||||||
|
|
||||||
# Ultra generic way of getting, checking and setting properties
|
|
||||||
def getProperty(self, property, default=None):
|
|
||||||
if self._propSource:
|
|
||||||
return self._propSource.getUserProperty(property, self.name, default)
|
|
||||||
|
|
||||||
def hasProperty(self, property):
|
|
||||||
if self._propSource:
|
|
||||||
return self._propSource.hasProperty(property)
|
|
||||||
|
|
||||||
def setProperty(self, property, value):
|
|
||||||
if property[0]=='_':
|
|
||||||
return
|
|
||||||
if self._propSource:
|
|
||||||
return self._propSource.setUserProperty(property, self.name, value)
|
|
||||||
|
|
||||||
def setTempProperty(self, property, value):
|
|
||||||
if property[0]=='_':
|
|
||||||
return
|
|
||||||
if self._propSource:
|
|
||||||
return self._propSource.setTempProperty(property, value)
|
|
||||||
|
|
||||||
def flushTempProperties(self):
|
|
||||||
if self._propSource:
|
|
||||||
return self._propSource.flushTempProperties()
|
|
||||||
|
|
||||||
def delProperty(self, property):
|
|
||||||
if property[0]=='_':
|
|
||||||
return
|
|
||||||
if self._propSource:
|
|
||||||
return self._propSource.delUserProperty(property, self.name)
|
|
||||||
|
|
||||||
def listProperties(self):
|
|
||||||
if self._propSource:
|
|
||||||
return self._propSource.listUserProperties(self.name)
|
|
||||||
|
|
||||||
# Try to allow User['property'] -- won't work for password d;)
|
|
||||||
def __getitem__(self, key):
|
|
||||||
# Don't return 'private' keys
|
|
||||||
if key[0] != '_':
|
|
||||||
if hasattr(self, key):
|
|
||||||
return getattr(self, key)
|
|
||||||
if self._propSource and self._propSource.hasProperty(key):
|
|
||||||
|
|
||||||
return self._propSource.getUserProperty(key, self.name)
|
|
||||||
raise KeyError, key
|
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
|
||||||
if key[0]=='_':
|
|
||||||
return
|
|
||||||
if self._propSource:
|
|
||||||
self._propSource.setUserProperty(key, self.name, value)
|
|
||||||
|
|
||||||
# List one user is supplied by the Auth Source...
|
|
||||||
|
|
||||||
def authenticate(self, listOneUser, password, request, remoteAuth=None):
|
|
||||||
result=listOneUser(username=self.name)
|
|
||||||
for people in result:
|
|
||||||
if remoteAuth:
|
|
||||||
return remoteAuth(self.name, password)
|
|
||||||
else:
|
|
||||||
secret=self.cryptPassword(self.name, password)
|
|
||||||
return secret==people['password']
|
|
||||||
return None
|
|
||||||
|
|
||||||
# You can set logout times or whatever here if you want to, the
|
|
||||||
# property source is still active.
|
|
||||||
def notifyCacheRemoval(self):
|
|
||||||
if self._propSource:
|
|
||||||
self._propSource.flushTempProperties()
|
|
||||||
|
|
||||||
# You must override this and __init__ if you are subclassing
|
|
||||||
# the user object, or your user object may not be reconstructed
|
|
||||||
# properly! All values in this dict must be non-Persistent objects
|
|
||||||
# or types, and may not hold any references to Persistent objects,
|
|
||||||
# or the cache will break.
|
|
||||||
def _getCacheableDict(self):
|
|
||||||
return {'name': self.name,
|
|
||||||
'password': self.__,
|
|
||||||
'roles': self.roles,
|
|
||||||
'domains': self.domains}
|
|
||||||
|
|
||||||
def getGroups(self):
|
|
||||||
if self._groupSource:
|
|
||||||
return self._groupSource.getGroupsOfUser(self.name)
|
|
||||||
else:
|
|
||||||
return ()
|
|
||||||
|
|
||||||
|
|
||||||
def _setGroups(self, groupnames):
|
|
||||||
if self._groupSource:
|
|
||||||
return self._groupSource.setGroupsOfUser(groupnames, self.name)
|
|
||||||
|
|
||||||
|
|
||||||
def _addGroups(self, groupnames):
|
|
||||||
if self._groupSource:
|
|
||||||
return self._groupSource.addGroupsToUser(groupnames, self.name)
|
|
||||||
|
|
||||||
def _delGroups(self, groupnames):
|
|
||||||
if self._groupSource:
|
|
||||||
return self._groupSource.delGroupsFromUser(groupnames, self.name)
|
|
||||||
|
|
||||||
def getId(self):
|
|
||||||
if self._propSource and self._propSource.getUserProperty('userid', self.name):
|
|
||||||
return self._propSource.getUserProperty('userid', self.name)
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# An Anonymous User for session tracking...
|
|
||||||
# Can set and get properties just like a normal user.
|
|
||||||
#
|
|
||||||
# These objects live in the cache, so, we have a __del__ method to
|
|
||||||
# clean ourselves up.
|
|
||||||
#
|
|
||||||
|
|
||||||
class XUFAnonUser(XUFUser):
|
|
||||||
def __init__(self, name, roles, propSource):
|
|
||||||
self.name =name
|
|
||||||
self.__ =''
|
|
||||||
self.roles =filter(None, roles)
|
|
||||||
self._propSource=propSource
|
|
||||||
|
|
||||||
def getRoles(self):
|
|
||||||
return tuple(self.roles) + ('Anonymous',)
|
|
||||||
|
|
||||||
def authenticate(self, listOneUser, password, request, remoteAuth=None):
|
|
||||||
return 1
|
|
||||||
|
|
||||||
def notifyCacheRemoval(self):
|
|
||||||
if self._propSource:
|
|
||||||
self._propSource.deleteUsers([self.name,])
|
|
||||||
|
|
||||||
# We now set up a dummy classes so that people can extend the User objects
|
|
||||||
# or override stuff with much less pain --akm
|
|
||||||
|
|
||||||
class User(XUFUser):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class AnonUser(XUFAnonUser):
|
|
||||||
pass
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
|||||||
*.pyc
|
|
||||||
*.pyo
|
|
||||||
*~
|
|
||||||
.*.swp
|
|