removed Zope products
@ -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
|
|
@ -1,409 +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: UserCache.py,v 1.16 2003/07/10 21:11:26 akm Exp $
|
|
||||||
|
|
||||||
# Module Level Caches for Various User Operations
|
|
||||||
|
|
||||||
from time import time
|
|
||||||
from BTrees.OOBTree import OOBTree
|
|
||||||
import threading
|
|
||||||
from Acquisition import aq_inner
|
|
||||||
from Products.exUserFolder.User import User
|
|
||||||
|
|
||||||
class UserCacheItem:
|
|
||||||
lastAccessed=0
|
|
||||||
def __init__(self, username, password, cacheable):
|
|
||||||
self.username=username
|
|
||||||
self.password=password
|
|
||||||
self.cacheable=cacheable
|
|
||||||
self.lastAccessed=time()
|
|
||||||
|
|
||||||
def touch(self):
|
|
||||||
self.lastAccessed=time()
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return self.username
|
|
||||||
|
|
||||||
class NegativeUserCacheItem(UserCacheItem):
|
|
||||||
def __init__(self, username):
|
|
||||||
self.username=username
|
|
||||||
self.lastAccessed=time()
|
|
||||||
|
|
||||||
class AdvancedCookieCacheItem(UserCacheItem):
|
|
||||||
lastAccessed=0
|
|
||||||
def __init__(self, username, password):
|
|
||||||
self.username=username
|
|
||||||
self.password=password
|
|
||||||
self.lastAccessed=time()
|
|
||||||
|
|
||||||
class SessionExpiredException(Exception):
|
|
||||||
'User Session Expired'
|
|
||||||
|
|
||||||
|
|
||||||
class UserCache:
|
|
||||||
def __init__(self, sessionLength):
|
|
||||||
self.sessionLength=sessionLength
|
|
||||||
self.cache=OOBTree()
|
|
||||||
self.hits=0
|
|
||||||
self.fail=0
|
|
||||||
self.nouser=0
|
|
||||||
self.attempts=0
|
|
||||||
self.timeouts=0
|
|
||||||
self.cacheStarted=time()
|
|
||||||
self.lock=threading.Lock()
|
|
||||||
|
|
||||||
def addToCache(self, username, password, User):
|
|
||||||
self.lock.acquire()
|
|
||||||
try:
|
|
||||||
if not self.sessionLength:
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
u = self.cache.items(username)
|
|
||||||
if u:
|
|
||||||
for x in self.cache[username]:
|
|
||||||
self.cache.remove(x)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
u = UserCacheItem(username, password, User._getCacheableDict())
|
|
||||||
self.cache[username]=u
|
|
||||||
finally:
|
|
||||||
self.lock.release()
|
|
||||||
|
|
||||||
def getUser(self, caller, username, password, checkpassword=1):
|
|
||||||
self.lock.acquire()
|
|
||||||
try:
|
|
||||||
if not self.sessionLength:
|
|
||||||
return None
|
|
||||||
|
|
||||||
self.attempts=self.attempts+1
|
|
||||||
|
|
||||||
u = None
|
|
||||||
try:
|
|
||||||
u = self.cache[username]
|
|
||||||
except KeyError:
|
|
||||||
self.nouser=self.nouser+1
|
|
||||||
return None
|
|
||||||
|
|
||||||
now = time()
|
|
||||||
if u:
|
|
||||||
if checkpassword and (u.password != password):
|
|
||||||
self.fail=self.fail+1
|
|
||||||
del self.cache[u.username]
|
|
||||||
elif self.sessionLength and (
|
|
||||||
(now - u.lastAccessed) > self.sessionLength):
|
|
||||||
del self.cache[u.username]
|
|
||||||
self.timeouts=self.timeouts+1
|
|
||||||
user_object=User(u.cacheable,
|
|
||||||
caller.currentPropSource,
|
|
||||||
caller.cryptPassword,
|
|
||||||
caller.currentAuthSource,
|
|
||||||
caller.currentGroupSource)
|
|
||||||
user_object.notifyCacheRemoval()
|
|
||||||
del u
|
|
||||||
raise SessionExpiredException
|
|
||||||
else:
|
|
||||||
u.touch()
|
|
||||||
self.hits=self.hits+1
|
|
||||||
return User(u.cacheable,
|
|
||||||
caller.currentPropSource,
|
|
||||||
caller.cryptPassword,
|
|
||||||
caller.currentAuthSource,
|
|
||||||
caller.currentGroupSource)
|
|
||||||
|
|
||||||
self.nouser=self.nouser+1
|
|
||||||
return None
|
|
||||||
finally:
|
|
||||||
self.lock.release()
|
|
||||||
|
|
||||||
def removeUser(self, username):
|
|
||||||
self.lock.acquire()
|
|
||||||
try:
|
|
||||||
if not self.sessionLength:
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
if self.cache[username]:
|
|
||||||
del self.cache[username]
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
finally:
|
|
||||||
self.lock.release()
|
|
||||||
|
|
||||||
def getCacheStats(self):
|
|
||||||
self.lock.acquire()
|
|
||||||
try:
|
|
||||||
return (
|
|
||||||
{'attempts':self.attempts,
|
|
||||||
'hits':self.hits,
|
|
||||||
'fail':self.fail,
|
|
||||||
'misses':self.nouser,
|
|
||||||
'cachesize':len(self.cache),
|
|
||||||
'time':self.cacheStarted,
|
|
||||||
'timeouts':self.timeouts,
|
|
||||||
'length':self.sessionLength})
|
|
||||||
finally:
|
|
||||||
self.lock.release()
|
|
||||||
|
|
||||||
def getCurrentUsers(self, caller):
|
|
||||||
self.lock.acquire()
|
|
||||||
try:
|
|
||||||
x=[]
|
|
||||||
now = time()
|
|
||||||
for z in self.cache.keys():
|
|
||||||
u = self.cache[z]
|
|
||||||
if self.sessionLength and (
|
|
||||||
(now - u.lastAccessed) > self.sessionLength):
|
|
||||||
del self.cache[u.username]
|
|
||||||
self.timeouts=self.timeouts+1
|
|
||||||
user_object=User(u.cacheable,
|
|
||||||
caller.currentPropSource,
|
|
||||||
caller.cryptPassword,
|
|
||||||
caller.currentAuthSource,
|
|
||||||
caller.currentGroupSource)
|
|
||||||
user_object.notifyCacheRemoval()
|
|
||||||
del u
|
|
||||||
else:
|
|
||||||
x.append({'username':u.username,
|
|
||||||
'lastAccessed':u.lastAccessed})
|
|
||||||
return x
|
|
||||||
finally:
|
|
||||||
self.lock.release()
|
|
||||||
|
|
||||||
class NegativeUserCache:
|
|
||||||
def __init__(self, sessionLength):
|
|
||||||
self.sessionLength=sessionLength
|
|
||||||
self.cache=OOBTree()
|
|
||||||
self.hits=0
|
|
||||||
self.cacheStarted=time()
|
|
||||||
self.lock=threading.Lock()
|
|
||||||
|
|
||||||
def addToCache(self, username):
|
|
||||||
self.lock.acquire()
|
|
||||||
try:
|
|
||||||
if not self.sessionLength:
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
u = self.cache.items(username)
|
|
||||||
if u:
|
|
||||||
for x in self.cache[username]:
|
|
||||||
self.cache.remove(x)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
u = NegativeUserCacheItem(username)
|
|
||||||
self.cache[username]=u
|
|
||||||
finally:
|
|
||||||
self.lock.release()
|
|
||||||
|
|
||||||
def getUser(self, username):
|
|
||||||
self.lock.acquire()
|
|
||||||
try:
|
|
||||||
if not self.sessionLength:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
u = None
|
|
||||||
try:
|
|
||||||
u = self.cache[username]
|
|
||||||
except KeyError:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
now = time()
|
|
||||||
if u:
|
|
||||||
if self.sessionLength and (
|
|
||||||
(now - u.lastAccessed) > self.sessionLength):
|
|
||||||
del self.cache[u.username]
|
|
||||||
else:
|
|
||||||
# We don't touch negative user caches
|
|
||||||
# u.touch()
|
|
||||||
self.hits=self.hits+1
|
|
||||||
return 1
|
|
||||||
return 0
|
|
||||||
finally:
|
|
||||||
self.lock.release()
|
|
||||||
|
|
||||||
def removeUser(self, username):
|
|
||||||
self.lock.acquire()
|
|
||||||
try:
|
|
||||||
if not self.sessionLength:
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
del self.cache[username]
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
finally:
|
|
||||||
self.lock.release()
|
|
||||||
|
|
||||||
class CookieCache:
|
|
||||||
def __init__(self, sessionLength):
|
|
||||||
self.sessionLength=sessionLength
|
|
||||||
self.cache=OOBTree()
|
|
||||||
self.hits=0
|
|
||||||
self.cacheStarted=time()
|
|
||||||
self.lock=threading.Lock()
|
|
||||||
|
|
||||||
def addToCache(self, username, password, key):
|
|
||||||
self.lock.acquire()
|
|
||||||
try:
|
|
||||||
if not self.sessionLength:
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
u = self.cache.items(key)
|
|
||||||
if u:
|
|
||||||
for x in self.cache[key]:
|
|
||||||
self.cache.remove(x)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
u = AdvancedCookieCacheItem(username, password)
|
|
||||||
self.cache[key]=u
|
|
||||||
finally:
|
|
||||||
self.lock.release()
|
|
||||||
|
|
||||||
def getUser(self, key):
|
|
||||||
self.lock.acquire()
|
|
||||||
try:
|
|
||||||
if not self.sessionLength:
|
|
||||||
return None
|
|
||||||
|
|
||||||
u = None
|
|
||||||
try:
|
|
||||||
u = self.cache[key]
|
|
||||||
except KeyError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
now = time()
|
|
||||||
if u:
|
|
||||||
if self.sessionLength and (
|
|
||||||
(now - u.lastAccessed) > self.sessionLength):
|
|
||||||
del self.cache[key]
|
|
||||||
else:
|
|
||||||
# We don't touch negative user caches
|
|
||||||
# u.touch()
|
|
||||||
self.hits=self.hits+1
|
|
||||||
return u.username, u.password
|
|
||||||
return None
|
|
||||||
finally:
|
|
||||||
self.lock.release()
|
|
||||||
|
|
||||||
def removeUser(self, key):
|
|
||||||
self.lock.acquire()
|
|
||||||
try:
|
|
||||||
if not self.sessionLength:
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
del self.cache[key]
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
finally:
|
|
||||||
self.lock.release()
|
|
||||||
|
|
||||||
class GlobalUserCache:
|
|
||||||
caches={}
|
|
||||||
def __init__(self):
|
|
||||||
self.lock = threading.Lock()
|
|
||||||
|
|
||||||
def createCache(self, who, sessionLength):
|
|
||||||
self.lock.acquire()
|
|
||||||
try:
|
|
||||||
self.caches[who]=UserCache(sessionLength)
|
|
||||||
return self.caches[who]
|
|
||||||
finally:
|
|
||||||
self.lock.release()
|
|
||||||
|
|
||||||
def getCache(self, who):
|
|
||||||
self.lock.acquire()
|
|
||||||
try:
|
|
||||||
if self.caches.has_key(who):
|
|
||||||
return self.caches[who]
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
finally:
|
|
||||||
self.lock.release()
|
|
||||||
|
|
||||||
def deleteCache(self, who):
|
|
||||||
self.lock.acquire()
|
|
||||||
try:
|
|
||||||
del self.caches[who]
|
|
||||||
finally:
|
|
||||||
self.lock.release()
|
|
||||||
|
|
||||||
class GlobalNegativeUserCache:
|
|
||||||
caches={}
|
|
||||||
def __init__(self):
|
|
||||||
self.lock = threading.Lock()
|
|
||||||
|
|
||||||
def createCache(self, who, sessionLength):
|
|
||||||
self.lock.acquire()
|
|
||||||
try:
|
|
||||||
self.caches[who]=NegativeUserCache(sessionLength)
|
|
||||||
return self.caches[who]
|
|
||||||
finally:
|
|
||||||
self.lock.release()
|
|
||||||
|
|
||||||
def getCache(self, who):
|
|
||||||
self.lock.acquire()
|
|
||||||
try:
|
|
||||||
if self.caches.has_key(who):
|
|
||||||
return self.caches[who]
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
finally:
|
|
||||||
self.lock.release()
|
|
||||||
|
|
||||||
def deleteCache(self, who):
|
|
||||||
self.lock.acquire()
|
|
||||||
try:
|
|
||||||
del self.caches[who]
|
|
||||||
finally:
|
|
||||||
self.lock.release()
|
|
||||||
|
|
||||||
class GlobalAdvancedCookieCache:
|
|
||||||
caches={}
|
|
||||||
def __init__(self):
|
|
||||||
self.lock = threading.Lock()
|
|
||||||
|
|
||||||
def createCache(self, who, sessionLength):
|
|
||||||
self.lock.acquire()
|
|
||||||
try:
|
|
||||||
self.caches[who]=CookieCache(sessionLength)
|
|
||||||
return self.caches[who]
|
|
||||||
finally:
|
|
||||||
self.lock.release()
|
|
||||||
|
|
||||||
def getCache(self, who):
|
|
||||||
self.lock.acquire()
|
|
||||||
try:
|
|
||||||
if self.caches.has_key(who):
|
|
||||||
return self.caches[who]
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
finally:
|
|
||||||
self.lock.release()
|
|
||||||
|
|
||||||
def deleteCache(self, who):
|
|
||||||
self.lock.acquire()
|
|
||||||
try:
|
|
||||||
del self.caches[who]
|
|
||||||
finally:
|
|
||||||
self.lock.release()
|
|
||||||
|
|
@ -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 UserCache
|
|
@ -1,85 +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: __init__.py,v 1.18 2004/11/10 14:15:33 akm Exp $
|
|
||||||
|
|
||||||
import exUserFolder
|
|
||||||
|
|
||||||
import CryptoSources
|
|
||||||
import AuthSources
|
|
||||||
import PropSources
|
|
||||||
import MembershipSources
|
|
||||||
import GroupSources
|
|
||||||
|
|
||||||
from GroupSource import GroupSource
|
|
||||||
|
|
||||||
from App.ImageFile import ImageFile
|
|
||||||
import OFS
|
|
||||||
|
|
||||||
#
|
|
||||||
# Install a dummy ZBabel setup if we don't have ZBabel installed.
|
|
||||||
#
|
|
||||||
import dummyZBabelTag
|
|
||||||
|
|
||||||
# Methods we need access to from any ObjectManager context
|
|
||||||
legacy_methods = (
|
|
||||||
('manage_addexUserFolderForm', exUserFolder.manage_addexUserFolderForm),
|
|
||||||
('manage_addexUserFolder', exUserFolder.manage_addexUserFolder),
|
|
||||||
('getAuthSources', exUserFolder.getAuthSources),
|
|
||||||
#('getPropSources', exUserFolder.getPropSources),
|
|
||||||
('getCryptoSources', exUserFolder.getCryptoSources),
|
|
||||||
('getMembershipSources', exUserFolder.getMembershipSources),
|
|
||||||
('getGroupSources', exUserFolder.getGroupSources),
|
|
||||||
('doAuthSourceForm', exUserFolder.doAuthSourceForm),
|
|
||||||
#('doPropSourceForm', exUserFolder.doPropSourceForm),
|
|
||||||
('doMembershipSourceForm', exUserFolder.doMembershipSourceForm),
|
|
||||||
# ('doGroupSourceForm', exUserFolder.doGroupSourceForm),
|
|
||||||
('getVariableType', exUserFolder.getVariableType),
|
|
||||||
('DialogHeader', exUserFolder.exUserFolder.DialogHeader),
|
|
||||||
('DialogFooter', exUserFolder.exUserFolder.DialogFooter),
|
|
||||||
#('MailHostIDs', exUserFolder.MailHostIDs),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Image files to place in the misc_ object so they are accesible from misc_/exUserFolder
|
|
||||||
misc_={'exUserFolder.gif': ImageFile('exUserFolder.gif', globals()),
|
|
||||||
'exUserFolderPlugin.gif': ImageFile('exUserFolderPlugin.gif', globals()),
|
|
||||||
'exUser.gif': ImageFile('exUser.gif', globals()),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def initialize(context):
|
|
||||||
"""
|
|
||||||
Register base classes
|
|
||||||
"""
|
|
||||||
context.registerClass(exUserFolder.exUserFolder,
|
|
||||||
meta_type="ex User Folder",
|
|
||||||
permission="Add exUser Folder",
|
|
||||||
constructors=(exUserFolder.manage_addexUserFolderForm,
|
|
||||||
exUserFolder.manage_addexUserFolder,),
|
|
||||||
legacy=legacy_methods,
|
|
||||||
icon="exUserFolder.gif")
|
|
||||||
|
|
||||||
context.registerClass(GroupSource.GroupSource,
|
|
||||||
meta_type="ex User Folder Group Source",
|
|
||||||
permission="Add exUser Folder",
|
|
||||||
constructors=(GroupSource.manage_addGroupSourceForm,
|
|
||||||
GroupSource.manage_addGroupSource,),
|
|
||||||
icon="exUserFolderPlugin.gif")
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
|||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</td></tr></table>
|
|
@ -1,7 +0,0 @@
|
|||||||
<table bgcolor="#808080" width="&dtml.missing-dialog_width;"><tr><td align="center" valign="middle">
|
|
||||||
<table width="100%" border="1" bgcolor="White" cellpadding="0" cellspacing="0">
|
|
||||||
<tr>
|
|
||||||
<td align="center"><table width="100%" bgcolor="#dddddd" border="0" cellspacing="0"><tr><td align="center"><font color="Black" size=+1 face="Helvetica"><b><dtml-babel src="'en'" literal="1"><dtml-var DialogTitle></dtml-babel></b></font></td></tr></table></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
@ -1,41 +0,0 @@
|
|||||||
<head><body>
|
|
||||||
<FORM ACTION="<dtml-var action>" METHOD="POST" <dtml-if target>TARGET="<dtml-var target>"</dtml-if>>
|
|
||||||
<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>
|
|
||||||
<dtml-var "DialogHeader(DialogTitle=title, dialog_width='')">
|
|
||||||
<TABLE BORDER="0" WIDTH="100%" CELLPADDING="10">
|
|
||||||
<TR>
|
|
||||||
<TD VALIGN="TOP">
|
|
||||||
<BR>
|
|
||||||
<CENTER><B><FONT SIZE="+6" COLOR="#77003B">!</FONT></B></CENTER>
|
|
||||||
</TD>
|
|
||||||
<TD VALIGN="TOP">
|
|
||||||
<BR><BR>
|
|
||||||
<CENTER>
|
|
||||||
<dtml-babel src="'en'" literal="1"><dtml-var message></dtml-babel>
|
|
||||||
</CENTER>
|
|
||||||
</TD>
|
|
||||||
</TR>
|
|
||||||
<TR>
|
|
||||||
<TD VALIGN="TOP">
|
|
||||||
</TD>
|
|
||||||
<TD VALIGN="TOP">
|
|
||||||
<CENTER>
|
|
||||||
<INPUT TYPE="SUBMIT" VALUE=" <dtml-babel src="'en'">Ok</dtml-babel> ">
|
|
||||||
</CENTER>
|
|
||||||
</TD>
|
|
||||||
</TR>
|
|
||||||
</TABLE>
|
|
||||||
<dtml-var DialogFooter>
|
|
||||||
</FORM>
|
|
||||||
|
|
||||||
</head></body>
|
|
@ -1,176 +0,0 @@
|
|||||||
<dtml-with "_(manage_options=filtered_manage_options())">
|
|
||||||
<dtml-if manage_options>
|
|
||||||
<dtml-call "REQUEST.set('n_', _.len(manage_options)-1)">
|
|
||||||
<dtml-call "REQUEST.set('a_', 0)">
|
|
||||||
<dtml-in manage_options mapping>
|
|
||||||
<dtml-if expr="URL[-(_.len(action)):]==action or
|
|
||||||
URL[-17:]=='/manage_workspace' and _['sequence-start']">
|
|
||||||
<dtml-call "REQUEST.set('a_', _['sequence-index'])">
|
|
||||||
</dtml-if>
|
|
||||||
<dtml-if "_.has_key('management_view') and management_view==label">
|
|
||||||
<dtml-call "REQUEST.set('a_', _['sequence-index'])">
|
|
||||||
</dtml-if>
|
|
||||||
</dtml-in>
|
|
||||||
|
|
||||||
|
|
||||||
<table cellpadding="0" cellspacing="0" width="100%" border="0">
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td bgcolor="#000000" rowspan="5" width="10%" valign="bottom"
|
|
||||||
align="left"> <img src="&dtml-BASEPATH1;/p_/sp"
|
|
||||||
width="2" height="1" alt="" />
|
|
||||||
</td>
|
|
||||||
<td bgcolor="#000000" colspan="<dtml-var "4 * (n_ + 1)">"><img
|
|
||||||
src="&dtml-BASEPATH1;/p_/sp" width="1" height="5" alt="" /></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<dtml-in manage_options>
|
|
||||||
<dtml-if "_['sequence-index']==a_">
|
|
||||||
<td bgcolor="#ffffff" rowspan="2" valign="top"
|
|
||||||
align="left"><img src="&dtml-BASEPATH1;/p_/ltab" width="5"
|
|
||||||
height="5" alt="" /></td>
|
|
||||||
<td bgcolor="#ffffff"><img src="&dtml-BASEPATH1;/p_/sp"
|
|
||||||
width="1" height="2" alt="" /></td>
|
|
||||||
<td bgcolor="#ffffff" rowspan="2" valign="top"
|
|
||||||
align="right"><img src="&dtml-BASEPATH1;/p_/rtab" width="5"
|
|
||||||
height="5" alt="" /></td>
|
|
||||||
<td bgcolor="#000000" rowspan="4"><img src="&dtml-BASEPATH1;/p_/sp"
|
|
||||||
width="2" height="1" alt="" /></td>
|
|
||||||
<dtml-else>
|
|
||||||
<td bgcolor="#efefef" rowspan="2" valign="top"
|
|
||||||
align="left"><img src="&dtml-BASEPATH1;/p_/ltab" width="5"
|
|
||||||
height="5" alt="" /></td>
|
|
||||||
<td bgcolor="#efefef"><img src="&dtml-BASEPATH1;/p_/sp"
|
|
||||||
width="1" height="2" alt="" /></td>
|
|
||||||
<td bgcolor="#efefef" rowspan="2" valign="top"
|
|
||||||
align="right"><img src="&dtml-BASEPATH1;/p_/rtab" width="5"
|
|
||||||
height="5" alt="" /></td>
|
|
||||||
<td bgcolor="#000000" rowspan="4"><img src="&dtml-BASEPATH1;/p_/sp"
|
|
||||||
width="2" height="1" alt="" /></td>
|
|
||||||
</dtml-if>
|
|
||||||
</dtml-in>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<dtml-in manage_options mapping>
|
|
||||||
<dtml-if "_['sequence-index']==a_">
|
|
||||||
<td bgcolor="#ffffff" valign="bottom" class="tab-small"
|
|
||||||
align="center"><font face="Verdana, Arial, Helvetica"
|
|
||||||
size="1" color="#000000"> <a <dtml-if
|
|
||||||
action>href="&dtml-action;"<dtml-else>href="&dtml-URL1;"</dtml-if
|
|
||||||
><dtml-if target> target="&dtml-target;"</dtml-if
|
|
||||||
>><span style="color: #000000;"><strong><dtml-babel src="'en'" literal="1">
|
|
||||||
<dtml-var label></dtml-babel></strong></span></a> </font></td>
|
|
||||||
<dtml-else>
|
|
||||||
<td bgcolor="#efefef" valign="bottom" class="tab-small"
|
|
||||||
align="center"><font face="Verdana, Arial, Helvetica"
|
|
||||||
size="1" color="#000000"> <a <dtml-if
|
|
||||||
action>href="&dtml-action;"<dtml-else>href="&dtml-URL1;"</dtml-if
|
|
||||||
><dtml-if target> target="&dtml-target;"</dtml-if
|
|
||||||
>><span style="color: #000000;"><strong>
|
|
||||||
<dtml-babel src="'en'" literal="1"><dtml-var label></dtml-babel></strong></span></a> </font></td>
|
|
||||||
</dtml-if>
|
|
||||||
</dtml-in>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<dtml-in manage_options>
|
|
||||||
<dtml-if "_['sequence-index']==a_">
|
|
||||||
<td colspan="3" bgcolor="#ffffff"><img src="&dtml-BASEPATH1;/p_/sp"
|
|
||||||
width="2" height="1" alt="" /></td>
|
|
||||||
<dtml-else>
|
|
||||||
<td colspan="3" bgcolor="#efefef"><img src="&dtml-BASEPATH1;/p_/sp"
|
|
||||||
width="2" height="1" alt="" /></td>
|
|
||||||
</dtml-if>
|
|
||||||
</dtml-in>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<dtml-in manage_options>
|
|
||||||
<dtml-if "_['sequence-index']==a_">
|
|
||||||
<td colspan="3" bgcolor="#ffffff"><img src="&dtml-BASEPATH1;/p_/sp"
|
|
||||||
width="2" height="1" alt="" /></td>
|
|
||||||
<dtml-else>
|
|
||||||
<td colspan="3" bgcolor="#c0c0c0"><img src="&dtml-BASEPATH1;/p_/sp"
|
|
||||||
width="2" height="1" alt="" /></td>
|
|
||||||
</dtml-if>
|
|
||||||
</dtml-in>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</dtml-if>
|
|
||||||
|
|
||||||
<dtml-unless MANAGE_TABS_NO_BANNER>
|
|
||||||
<br />
|
|
||||||
<table width="100%" cellspacing="0" cellpadding="2" border="0">
|
|
||||||
<tr class="location-bar">
|
|
||||||
<td align="left" valign="top">
|
|
||||||
<div class="std-text">
|
|
||||||
<dtml-if icon>
|
|
||||||
<img src="&dtml-BASEPATH1;/&dtml-icon;"
|
|
||||||
alt="&dtml-meta_type;" border="0" />
|
|
||||||
</dtml-if>
|
|
||||||
<strong>
|
|
||||||
<dtml-if meta_type>
|
|
||||||
<dtml-if class_manage_path>
|
|
||||||
<a href="&dtml-BASEPATH1;&dtml-class_manage_path;"
|
|
||||||
title="Manage the ZClass of this object">&dtml-meta_type;</a>
|
|
||||||
<dtml-else>
|
|
||||||
&dtml-meta_type;
|
|
||||||
</dtml-if>
|
|
||||||
<dtml-else>
|
|
||||||
Object
|
|
||||||
</dtml-if>
|
|
||||||
at <dtml-var expr="tabs_path_default(REQUEST)">
|
|
||||||
</strong>
|
|
||||||
<dtml-if locked_in_version>
|
|
||||||
<dtml-if modified_in_version>
|
|
||||||
<img src="&dtml-BASEPATH1;/p_/locked"
|
|
||||||
alt="This item has been modified in this version" />
|
|
||||||
<dtml-else>
|
|
||||||
<img src="&dtml-BASEPATH1;/p_/lockedo"
|
|
||||||
alt="This item has been modified in another version" />
|
|
||||||
(<em><dtml-var locked_in_version html_quote></em>)
|
|
||||||
</dtml-if>
|
|
||||||
</dtml-if>
|
|
||||||
<dtml-if wl_isLocked>
|
|
||||||
<img src="&dtml-BASEPATH1;/p_/davlocked"
|
|
||||||
alt="This item has been locked by WebDAV"
|
|
||||||
title="This item has been locked by WebDAV" />
|
|
||||||
</dtml-if wl_isLocked>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<dtml-if "_.has_key('help_topic') and _.has_key('help_product')">
|
|
||||||
<td align="right" valign="top">
|
|
||||||
<div class="std-text">
|
|
||||||
<dtml-var "HelpSys.helpLink(help_product, help_topic)">
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<dtml-else>
|
|
||||||
<dtml-if manage_options>
|
|
||||||
<dtml-with "_(option=manage_options[a_])">
|
|
||||||
<dtml-if "option.has_key('help')">
|
|
||||||
<td align="right" valign="top">
|
|
||||||
<div class="std-text">
|
|
||||||
<dtml-var "HelpSys.helpLink(option['help'][0], option['help'][1])">
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</dtml-if>
|
|
||||||
</dtml-with>
|
|
||||||
</dtml-if>
|
|
||||||
</dtml-if>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<dtml-if Zope-Version>
|
|
||||||
<div class="system-msg">
|
|
||||||
<em>You are currently working in version <a href="&dtml-SERVER_URL;&dtml-Zope-Version;/manage_main"><dtml-var Zope-Version html_quote></a></em>
|
|
||||||
</div>
|
|
||||||
</dtml-if>
|
|
||||||
</dtml-unless>
|
|
||||||
|
|
||||||
<dtml-if manage_tabs_message>
|
|
||||||
<div class="system-msg">
|
|
||||||
<dtml-var manage_tabs_message>
|
|
||||||
(<dtml-var ZopeTime fmt="%Y-%m-%d %H:%M">)
|
|
||||||
</div>
|
|
||||||
</dtml-if>
|
|
||||||
|
|
||||||
</dtml-with>
|
|
@ -1,19 +0,0 @@
|
|||||||
<dtml-with "_(manage_options=filtered_manage_options())">
|
|
||||||
<dtml-if manage_options>
|
|
||||||
<table cellpadding="0" cellspacing="0" width="100%" border="2">
|
|
||||||
<tr><td>
|
|
||||||
<table cellpadding="0" cellspacing="5" width="100%" border="0">
|
|
||||||
<tr>
|
|
||||||
<td valign="bottom" align="left" class="tab-small">
|
|
||||||
<dtml-in manage_options mapping>
|
|
||||||
<b><a <dtml-if action>href="<dtml-var action>"
|
|
||||||
<dtml-else>href="<dtml-var URL1>" </dtml-if>
|
|
||||||
<dtml-if target> target="<dtml-var target>"</dtml-if>>
|
|
||||||
[<dtml-babel src="'en'" literal="1"><dtml-var label></dtml-babel>]</a></b>
|
|
||||||
</dtml-in>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</td></tr></table>
|
|
||||||
</dtml-if>
|
|
||||||
</dtml-with>
|
|
@ -1,119 +0,0 @@
|
|||||||
Frequently Asked Questions
|
|
||||||
|
|
||||||
1. Why shouldn't I use Core Session Tracking + Login Manager?
|
|
||||||
XUF serves a different set of users to the combination above. XUF
|
|
||||||
aims to be a simple out of the box solution. Login Manager allows
|
|
||||||
for very complex authorisation schemes that can query multiple user
|
|
||||||
sources. We don't do that.
|
|
||||||
|
|
||||||
2. Why use XUF at all?
|
|
||||||
In its simplest configuration, XUF provides the same functionality
|
|
||||||
as the standard User Folder, but, is more secure. Passwords are
|
|
||||||
stored encrypted, which is not the case for the standard User Folder.
|
|
||||||
So even if you don't want to set properties on users, or any
|
|
||||||
membership facilities, there is a benefit to running XUF.
|
|
||||||
|
|
||||||
3. Do I have to have all this other stuff?
|
|
||||||
No. The only thing you need to enable is authentication. There is
|
|
||||||
a null property source, and a null membership source. Everything
|
|
||||||
other than authentication is optional.
|
|
||||||
|
|
||||||
4. Can I use it as a root folder?
|
|
||||||
Some people have reported success in doing so. We don't recommend
|
|
||||||
it for various reasons. The main one is that the internal Zope API
|
|
||||||
can change without warning, which could break XUF and lock you out
|
|
||||||
of your Zope. This can happen with any User Folder product. We
|
|
||||||
recommend you look at VHM and other Site Access methods to allow
|
|
||||||
you to store your main site in a sub-folder.
|
|
||||||
|
|
||||||
5. When will XUF support authentication against XYZ system?
|
|
||||||
That depends. First the active developers need to have an interest
|
|
||||||
in it, and more importantly they need to be able to test it. Writing
|
|
||||||
your authentication method is very simple, so if you understand
|
|
||||||
what you want to authenticate against, and know some python you
|
|
||||||
could write one in an hour. You can also use the usAuthSource to
|
|
||||||
write one using PythonScripts, ExternalMethods, DTML, or any other
|
|
||||||
callable method that Zope supports.
|
|
||||||
|
|
||||||
6. I wrote this cool authentication source can I get it into the main
|
|
||||||
distribution?
|
|
||||||
Yes and No. If your authentication is Open Source, and has a
|
|
||||||
compatible license with XUF, and doesn't require any external
|
|
||||||
libraries, odds are it'll go into the main distribution. If it
|
|
||||||
depends on external libraries, it's possible it can conditionally
|
|
||||||
go into the main distribution. The nice thing about XUF is that
|
|
||||||
Authentication, Property, and Membership sources are all packagable
|
|
||||||
as independent products, so you can distribute it as a standalone
|
|
||||||
product, and it'll work (without having to have the code drop into
|
|
||||||
the XUF directory either).
|
|
||||||
|
|
||||||
7. Is XUF going to be part of the Core Zope?
|
|
||||||
No idea. At the moment (0.10.5) XUF is probably not at a level that
|
|
||||||
Zope Corporation would consider mature enough for core inclusion
|
|
||||||
anyway.
|
|
||||||
|
|
||||||
Actually the answer now, is probably not. At a minimum smbAuthSource,
|
|
||||||
and radiusAuthSource would have to be stripped and distributed
|
|
||||||
seperately. Over and above that, I would have to assign Zope Corp
|
|
||||||
co-ownership rights on the IP, which amongst other things gives
|
|
||||||
them or anyone that buys them unlimited access to future derived
|
|
||||||
works. I refuse to do this on principle, the liberal licensing of
|
|
||||||
the product should be more than adequate for any (especially open
|
|
||||||
source) endeavour.
|
|
||||||
|
|
||||||
8. What's with the Management Screens?
|
|
||||||
It's a joke on the Zope World.
|
|
||||||
|
|
||||||
9. But they're really ugly I want to change them.
|
|
||||||
That's fine, you do that, that's the point.
|
|
||||||
|
|
||||||
10. Can I send you patches to put them back to standard Management
|
|
||||||
Screens?
|
|
||||||
You can put patches into the tracker at Source Forge if you want to.
|
|
||||||
|
|
||||||
11. HELP!!!! I tried to install XUF as my root folder, without
|
|
||||||
reading the FAQ, or really knowing what I'm doing, and now I'm
|
|
||||||
hosed!!!
|
|
||||||
That's a shame.
|
|
||||||
|
|
||||||
12. Will XUF work with ZEO?
|
|
||||||
Unknown. However, it's almost certain that in its current form
|
|
||||||
credential caching will not work across ZEO -- you will get a
|
|
||||||
seperate User Cache for each Zope Client (which isn't really all
|
|
||||||
that bad). However, it means that if you want to use Session Tracking,
|
|
||||||
you need to lock users to one particular server. Most commercial
|
|
||||||
Load Balancers do this for you anyhow. A persistent cache will form
|
|
||||||
part of an upcoming release which will allow all of this to work
|
|
||||||
transparently across ZEO farms.
|
|
||||||
|
|
||||||
13. Shouldn't it be EUF?
|
|
||||||
No, it should be XUF :-P
|
|
||||||
|
|
||||||
14. How can I log in a user via a form submission?
|
|
||||||
Yes, the key is sending the __ac_name and __ac_password (yes that's
|
|
||||||
two underscores in front) as form variables to any object covered
|
|
||||||
by the XUF.
|
|
||||||
|
|
||||||
This form will take your users to the /index_html and log them in.
|
|
||||||
You can place this anywhere in your site including /index_html.
|
|
||||||
|
|
||||||
<form action="/index_html">
|
|
||||||
Name: <input type="text" size="20" name="__ac_name"><br>
|
|
||||||
Password: <input type="password" size="20" name="__ac_password">
|
|
||||||
<input type="submit" value="Log in">
|
|
||||||
</form>
|
|
||||||
|
|
||||||
15. That Dialog box sure is ugly! How can I customize it so it looks
|
|
||||||
right in my site?
|
|
||||||
Under the contents tab add an object called MessageDialog , it can
|
|
||||||
be a dtml document, method, or even a page template. Make it look
|
|
||||||
how you want, it will acquire all objects from the folder the XUF
|
|
||||||
is in so you can use the standard_html_header, call scripts, etc.
|
|
||||||
|
|
||||||
16. Why can't I change the default crypto method?
|
|
||||||
Because someone will change it, and all the existing passwords will cease
|
|
||||||
to work. Then I'll get lots of hate-mail, or get slagged off on other mailing
|
|
||||||
lists :-)
|
|
||||||
|
|
||||||
17. Where is the Zen Master's Guide to exUserFolder?
|
|
||||||
Everywhere and nowhere.
|
|