ScoDoc-PE/ZopeProducts/exUserFolder/exUserFolder.py

1381 lines
44 KiB
Python
Raw Normal View History

2020-09-26 16:19:37 +02:00
# Zope User Folder for ScoDoc
# Adapte de l'Extensible User Folder
# simplifie pour les besoins de ScoDoc.
# Emmanuel Viennet 2013
#
# 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: exUserFolder.py,v 1.93 2004/11/10 14:15:33 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.
#
##############################################################################
# Portions Copyright (c) 2002 Nuxeo SARL <http://nuxeo.com>,
# Copyright (c) 2002 Florent Guillaume <mailto:fg@nuxeo.com>.
#
# 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 COPYRIGHT HOLDERS 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 COPYRIGHT
# OWNER 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.
import Globals, App.Undo, socket, os, string, sha, random, sys, zLOG
from Globals import DTMLFile, PersistentMapping
from string import join,strip,split,lower,upper,find
from OFS.Folder import Folder
from OFS.CopySupport import CopyContainer
from base64 import decodestring, encodestring
from urllib import quote, unquote
from Acquisition import aq_base
from AccessControl import ClassSecurityInfo
from AccessControl.Role import RoleManager
from AccessControl.User import BasicUser, BasicUserFolder, readUserAccessFile
from AccessControl.PermissionRole import PermissionRole
from AccessControl.ZopeSecurityPolicy import _noroles
from OFS.DTMLMethod import DTMLMethod
from time import time
from OFS.ObjectManager import REPLACEABLE
from Persistence import Persistent
from PropertyEditor import *
from User import User, AnonUser
from UserCache.UserCache import GlobalUserCache, GlobalNegativeUserCache, GlobalAdvancedCookieCache, SessionExpiredException
from LoginRequiredMessages import LoginRequiredMessages
from AccessControl import Unauthorized
class LoginRequired(Exception):
"""Login required"""
pass
# If there is no NUG Product just define a dummy class
try:
from Products.NuxUserGroups.UserFolderWithGroups import BasicGroupFolderMixin, _marker
except ImportError:
class BasicGroupFolderMixin:
pass
_marker = None
# Little function to create temp usernames
def createTempName():
t=time()
t1=time()
t2=time()
t3 = 0.0
t3 = (t + t1 + t2) / 3
un = "Anonymous %.0f"%(t3)
return(un)
manage_addexUserFolderForm=DTMLFile('dtml/manage_addexUserFolder', globals(), __name__='manage_addexUserFolderForm')
def manage_addexUserFolder(self, authId, propId, memberId,
cookie_mode=0, session_length=0,
not_session_length=0,
sessionTracking=None, idleTimeout=None,
REQUEST={}, groupId=None, cryptoId=None):
""" """
if hasattr(self.aq_base, 'acl_users'):
return Globals.MessageDialog(self,REQUEST,
title ='Item Exists',
message='This object already contains a User Folder',
action ='%s/manage_main' % REQUEST['URL1'])
ob=exUserFolder(authId, propId, memberId, groupId, cryptoId, cookie_mode,
session_length, sessionTracking, idleTimeout,
not_session_length)
self._setObject('acl_users', ob, None, None, 0)
self.__allow_groups__=self.acl_users
ob=getattr(self, 'acl_users')
ob.postInitialisation(REQUEST)
if REQUEST:
return self.manage_main(self, REQUEST)
return ''
#
# Module level caches
#
XUFUserCache=GlobalUserCache()
XUFNotUserCache=GlobalNegativeUserCache()
XUFCookieCache=GlobalAdvancedCookieCache()
class exUserFolder(Folder,BasicUserFolder,BasicGroupFolderMixin,
CopyContainer):
""" """
# HACK! We use this meta_type internally so we can be pasted into
# the root. We registered with 'exUserFolder' meta_type however, so
# our constructors work.
meta_type='User Folder'
id ='acl_users'
title ='Extensible User Folder'
icon ='misc_/exUserFolder/exUserFolder.gif'
isPrincipiaFolderish=1
isAUserFolder=1
__allow_access_to_unprotected_subobjects__=1
authSources={}
propSources={}
cryptoSources={}
membershipSources={}
groupSources={} # UNUSED by ScoDoc
manage_options=(
{'label':'Users', 'action':'manage_main'},
{'label':'Groups', 'action':'manage_userGroups'},
{'label':'Parameters', 'action':'manage_editexUserFolderForm'},
{'label':'Authentication Source','action':'manage_editAuthSourceForm'},
{'label':'Properties Source','action':'manage_editPropSourceForm'},
{'label':'Membership Source', 'action':'manage_editMembershipSourceForm'},
{'label':'Cache Data', 'action':'manage_showCacheData'},
{'label':'Security', 'action':'manage_access'},
{'label':'Contents', 'action':'manage_contents'},
{'label':'Ownership', 'action':'manage_owner'},
{'label':'Undo', 'action':'manage_UndoForm'},
)
__ac_permissions__=(
('View management screens', ('manage','manage_menu','manage_main',
'manage_copyright', 'manage_tabs',
'manage_properties', 'manage_UndoForm',
'manage_edit', 'manage_contents',
'manage_cutObjects','manage_copyObjects',
'manage_pasteObjects',
'manage_renameForm',
'manage_renameObject',
'manage_renameObjects', ),
('Manager',)),
('Undo changes', ('manage_undo_transactions',),
('Manager',)),
('Change permissions', ('manage_access',),
('Manager',)),
('Manage users', ('manage_users', 'manage_editUserForm',
'manage_editUser', 'manage_addUserForm',
'manage_addUser', 'manage_userActions',
'userFolderAddGroup',
'userFolderDelGroups',
'getGroupNames',
'getGroupById',
'manage_userGroups',
'manage_addGroup',
'manage_showGroup',),
('Manager',)),
('Change exUser Folders', ('manage_edit',),
('Manager',)),
('View', ('manage_changePassword',
2021-04-23 10:24:45 +02:00
'manage_forgotPassword','docLoginRedirect',
'logout', 'DialogHeader',
2020-09-26 16:19:37 +02:00
'DialogFooter', 'manage_signupUser',
'MessageDialog', 'redirectToLogin','manage_changeProps'),
('Anonymous', 'Authenticated', 'Manager')),
('Manage properties', ('manage_addProperty',
'manage_editProperties',
'manage_delProperties',
'manage_changeProperties',
'manage_propertiesForm',
'manage_propertyTypeForm',
'manage_changePropertyTypes',
),
('Manager',)),
('Access contents information', ('hasProperty', 'propertyIds',
'propertyValues','propertyItems',
'getProperty', 'getPropertyType',
2021-04-23 10:24:45 +02:00
'propertyMap', 'docLoginRedirect',
2020-09-26 16:19:37 +02:00
'DialogHeader', 'DialogFooter',
'MessageDialog', 'redirectToLogin',),
('Anonymous', 'Authenticated', 'Manager')),
)
manage_access=DTMLFile('dtml/access',globals())
manage_tabs=DTMLFile('common/manage_tabs',globals())
manage_properties=DTMLFile('dtml/properties', globals())
manage_main=DTMLFile('dtml/mainUser', globals())
manage_contents=Folder.manage_main
manage_showCacheData=DTMLFile('dtml/manage_showCacheData', globals())
# This is going away soon...
docLoginRedirect=DTMLFile('dtml/docLoginRedirect', globals())
# Stupid crap
try:
manage_contents._setName('manage_contents')
except AttributeError:
pass
MessageDialog=DTMLFile('common/MessageDialog', globals())
MessageDialog.__replaceable__ = REPLACEABLE
manage_addUserForm=DTMLFile('dtml/manage_addUserForm',globals())
manage_editUserForm=DTMLFile('dtml/manage_editUserForm',globals())
DialogHeader__roles__=()
DialogHeader=DTMLFile('common/DialogHeader',globals())
DialogFooter__roles__=()
DialogFooter=DTMLFile('common/DialogFooter',globals())
manage_editAuthSourceForm=DTMLFile('dtml/manage_editAuthSourceForm',globals())
manage_editPropSourceForm=DTMLFile('dtml/manage_editPropSourceForm',globals())
manage_editMembershipSourceForm=DTMLFile('dtml/manage_editMembershipSourceForm', globals())
manage_addPropertyForm=DTMLFile('dtml/manage_addPropertyForm', globals())
manage_createPropertyForm=DTMLFile('dtml/manage_createPropertyForm', globals())
manage_editUserPropertyForm=DTMLFile('dtml/manage_editUserPropertyForm', globals())
manage_editexUserFolderForm=DTMLFile('dtml/manage_editexUserFolderForm', globals())
manage_userGroups=DTMLFile('dtml/mainGroup',globals())
# Use pages from NUG if it's there, otherwise no group support
try:
manage_addGroup = BasicGroupFolderMixin.manage_addGroup
manage_showGroup = BasicGroupFolderMixin.manage_showGroup
except:
manage_addGroup = None
manage_showGroup = None
# No more class globals
# sessionLength=0 # Upgrading users should get no caching.
# notSessionLength=0 # bad cache limit
# cookie_mode=0
# sessionTracking=None # Or session tracking.
# idleTimeout=0
def __init__(self, authId, propId, memberId, groupId, cryptoId,
cookie_mode=0, session_length=0, sessionTracking=None,
idleTimeout=0, not_session_length=0):
self.cookie_mode=cookie_mode
self.sessionLength=session_length
self.notSessionLength=not_session_length
self.sessionTracking=sessionTracking
self.idleTimeout=idleTimeout
_docLogin=DTMLFile('dtml/docLogin',globals())
_docLogout=DTMLFile('dtml/docLogout',globals())
docLogin=DTMLMethod(__name__='docLogin')
docLogin.manage_edit(data=_docLogin, title='Login Page')
self._setObject('docLogin', docLogin, None, None, 0)
docLogout=DTMLMethod(__name__='docLogout')
docLogout.manage_edit(data=_docLogout, title='Logout Page')
self._setObject('docLogout', docLogout, None, None, 0)
postUserCreate=DTMLMethod(__name__='postUserCreate')
postUserCreate.manage_edit(data=_postUserCreate, title='Post User Creation methods')
self._setObject('postUserCreate', postUserCreate, None, None, 0)
self.manage_addAuthSource=self.authSources[authId].manage_addMethod
self.manage_addPropSource=self.propSources[propId].manage_addMethod
self.manage_addMembershipSource=self.membershipSources[memberId].manage_addMethod
self.manage_addGroupSource=None # UNUSED by ScoDoc
self.currentGroupsSource=None
if cryptoId:
self.cryptoId = cryptoId
else:
self.cryptoId = 'Crypt'
def __setstate__(self, state):
Persistent.__setstate__(self, state)
if not hasattr(self, 'currentGroupSource'):
self.currentGroupSource = None
if not hasattr(self, 'sessionLength'):
self.sessionLength = 0
if not hasattr(self, 'notSessionLength'):
self.notSessionLength = 0
if not hasattr(self, 'cookie_mode'):
self.cookie_mode = 0
if not hasattr(self, 'sessionTraining'):
self.sessionTracking = None
if not hasattr(self, 'idleTimeout'):
self.idleTimeout=0
def manage_beforeDelete(self, item, container):
zLOG.LOG("exUserFolder", zLOG.BLATHER, "Attempting to delete an exUserFolder instance")
if item is self:
try:
self.cache_deleteCache()
self.xcache_deleteCache()
zLOG.LOG("exUserFolder", zLOG.BLATHER, "-- Caches deleted")
except:
#pass
zLOG.LOG("exUserFolder", zLOG.BLATHER, "-- Cache deletion failed")
try:
del container.__allow_groups__
zLOG.LOG("exUserFolder", zLOG.BLATHER, "-- container.__allow_groups_ deleted")
except:
#pass
zLOG.LOG("exUserFolder", zLOG.BLATHER, "-- container.__allow_groups_ deletion failed")
def manage_afterAdd(self, item, container):
zLOG.LOG("exUserFolder", zLOG.BLATHER, "Adding an exUserFolder")
if item is self:
if hasattr(self, 'aq_base'): self=self.aq_base
container.__allow_groups__=self
def manage_editPropSource(self, REQUEST):
""" Edit Prop Source """
if self.currentPropSource:
self.currentPropSource.manage_editPropSource(REQUEST)
return self.manage_main(self, REQUEST)
def manage_editAuthSource(self, REQUEST):
""" Edit Auth Source """
self.currentAuthSource.manage_editAuthSource(REQUEST)
return self.manage_main(self, REQUEST)
def manage_editMembershipSource(self, REQUEST):
""" Edit Membership Source """
if self.currentMembershipSource:
return self.currentMembershipSource.manage_editMembershipSource(REQUEST)
def postInitialisation(self, REQUEST):
self.manage_addAuthSource(self=self,REQUEST=REQUEST)
self.manage_addPropSource(self=self,REQUEST=REQUEST)
self.manage_addMembershipSource(self=self,REQUEST=REQUEST)
self.currentGroupSource = None
def addAuthSource(self, REQUEST={}):
return self.manage_addAuthSourceForm(self, REQUEST)
def addPropSource(self, REQUEST={}):
return self.manage_addPropSourceForm(self, REQUEST)
def addMembershipSource(self, REQUEST={}):
return self.manage_editMembershipSourceForm(self, REQUEST)
def listUserProperties(self, username):
if self.currentPropSource:
return self.currentPropSource.listUserProperties(username=username)
def getUserProperty(self, username, key):
if self.currentPropSource:
return self.currentPropSource.getUserProperty(key=key, username=username)
def reqattr(self, request, attr, default=None):
try: return request[attr]
except: return default
def getAuthFailedMessage(self, code):
""" Return a code """
if LoginRequiredMessages.has_key(code):
return LoginRequiredMessages[code]
return 'Login Required'
# Called when we are deleted
def cache_deleteCache(self):
pp = string.join(self.getPhysicalPath(), '/')
XUFUserCache.deleteCache(pp)
def cache_addToCache(self, username, password, user):
if not self.sessionLength:
return
# fix by emmanuel
if username == self._emergency_user.getUserName():
return
# /fix
pp = string.join(self.getPhysicalPath(), '/')
x = XUFUserCache.getCache(pp)
if not x:
x = XUFUserCache.createCache(pp, self.sessionLength)
x.addToCache(username, password, user)
def cache_getUser(self, username, password, checkpassword=1):
if not self.sessionLength:
return None
pp = string.join(self.getPhysicalPath(), '/')
x = XUFUserCache.getCache(pp)
if not x:
return None
u = x.getUser(self, username, password, checkpassword)
if u is not None:
u = u.__of__(self)
return u
def cache_removeUser(self, username):
if not self.sessionLength:
return
pp = string.join(self.getPhysicalPath(), '/')
x = XUFUserCache.getCache(pp)
if x:
x.removeUser(username)
def cache_getCacheStats(self):
pp = string.join(self.getPhysicalPath(), '/')
x = XUFUserCache.getCache(pp)
if not x:
x = XUFUserCache.createCache(pp, self.sessionLength)
if x:
return x.getCacheStats()
def cache_getCurrentUsers(self):
pp = string.join(self.getPhysicalPath(), '/')
x = XUFUserCache.getCache(pp)
if x:
return x.getCurrentUsers(self)
# negative cache functions
def xcache_deleteCache(self):
pp = string.join(self.getPhysicalPath(), '/')
XUFNotUserCache.deleteCache(pp)
def xcache_addToCache(self, username):
if not self.notSessionLength:
return
pp = string.join(self.getPhysicalPath(), '/')
x = XUFNotUserCache.getCache(pp)
if not x:
x = XUFNotUserCache.createCache(pp, self.notSessionLength)
x.addToCache(username)
def xcache_getUser(self, username):
if not self.notSessionLength:
return None
pp = string.join(self.getPhysicalPath(), '/')
x = XUFNotUserCache.getCache(pp)
if not x:
return None
return x.getUser(username)
def xcache_removeUser(self, username):
#zLOG.LOG('exUserFolder', zLOG.PANIC, 'xcache_removeUser(%s)' % username)
if not self.notSessionLength:
return
pp = string.join(self.getPhysicalPath(), '/')
x = XUFNotUserCache.getCache(pp)
if x:
#zLOG.LOG('exUserFolder', zLOG.PANIC, 'xcache_removeUser removing')
x.removeUser(username)
# Cookie Cache Functions
def cache_deleteCookieCache(self):
pp = string.join(self.getPhysicalPath(), '/')
XUFCookieCache.deleteCache(pp)
def cache_addToCookieCache(self, username, password, key):
pp = string.join(self.getPhysicalPath(), '/')
c = XUFCookieCache.getCache(pp)
if not c:
c = XUFCookieCache.createCache(pp, 86400)
c.addToCache(username, password, key)
def cache_getCookieCacheUser(self, key):
pp = string.join(self.getPhysicalPath(), '/')
c = XUFCookieCache.getCache(pp)
if not c:
return None
return c.getUser(key)
def cache_removeCookieCacheUser(self, key):
pp = string.join(self.getPhysicalPath(), '/')
c = XUFCookieCache.getCache(pp)
if c:
c.removeUser(key)
def manage_editUser(self, username, REQUEST={}): # UNUSED by ScoDoc
""" Edit a User """
# username=self.reqattr(REQUEST,'username')
password=self.reqattr(REQUEST,'password')
password_confirm=self.reqattr(REQUEST,'password_confirm')
roles=self.reqattr(REQUEST,'roles', [])
groups=self.reqattr(REQUEST, 'groupnames', [])
if not username:
return self.MessageDialog(self,REQUEST=REQUEST,
title ='Illegal value',
message='A username must be specified',
action ='manage_main')
if (password or password_confirm) and (password != password_confirm):
return self.MessageDialog(self,REQUEST=REQUEST,
title ='Illegal value',
message='Password and confirmation do not match',
action ='manage_main')
self._doChangeUser(username, password, roles, domains='', groups=groups, REQUEST=REQUEST)
return self.MessageDialog(self,REQUEST=REQUEST,
title = 'User Updated',
message= 'User %s was updated.'%(username),
action = 'manage_main')
# Methode special pour ScoDoc: evite le code inutile dans notre contexte
# et accede a la BD via le curseur psycopg2 fourni
# (facilitera la separation de Zope)
def scodoc_editUser(self, cursor, username, password=None, roles=[]):
"""Edit a ScoDoc user"""
roles = list(roles)
rolestring= ','.join(roles)
# Don't change passwords if it's null
if password:
secret=self.cryptPassword(username, password)
# Update just the password:
# self.sqlUpdateUserPassword(username=username, password=secret)
cursor.execute("UPDATE sco_users SET passwd=%(secret)s WHERE user_name=%(username)s",
{ 'secret':secret, 'username': username } )
#self.sqlUpdateUser(username=username, roles=rolestring)
cursor.execute("UPDATE sco_users SET roles=%(rolestring)s WHERE user_name=%(username)s",
{ 'rolestring':rolestring, 'username': username } )
if hasattr(self.currentAuthSource, '_v_lastUser'):
# Specific for pgAuthSource:
self.currentAuthSource._v_lastUser={} # clear pg user cache
# We may have updated roles or passwords... flush the user...
self.cache_removeUser(username)
self.xcache_removeUser(username)
#
# Membership helper
#
def goHome(self, REQUEST, RESPONSE):
""" Go to home directory """
if self.currentMembershipSource:
self.currentMembershipSource.goHome(REQUEST, RESPONSE)
#
# Membership method of changing user properties
#
def manage_changeProps(self, REQUEST):
""" Change Properties """
if self.currentMembershipSource:
return self.currentMembershipSource.changeProperties(REQUEST)
else:
return self.MessageDialog(self,REQUEST,
title = 'This is a test',
message= 'This was a test',
action = '..')
#
# Membership method of adding a new user.
# If everything goes well the membership plugin calls manage_addUser()
#
def manage_signupUser(self, REQUEST):
""" Signup a new user """
""" This is seperate so you can add users using the normal """
""" interface w/o going through membership policy """
username=self.reqattr(REQUEST,'username')
roles=self.reqattr(REQUEST,'roles')
if not username:
return self.MessageDialog(self,REQUEST=REQUEST,
title ='Illegal value',
message='A username must be specified',
action ='manage_main')
if (self.getUser(username) or
(self._emergency_user and
username == self._emergency_user.getUserName())):
return self.MessageDialog(self,REQUEST=REQUEST,
title ='Illegal value',
message='A user with the specified name already exists',
action ='manage_main')
if self.currentMembershipSource:
return self.currentMembershipSource.createUser(REQUEST)
#
# Membership method of changing passwords
#
def manage_changePassword(self, REQUEST):
""" Change a password """
if self.currentMembershipSource:
return self.currentMembershipSource.changePassword(REQUEST)
#
# User says they can't remember their password
#
def manage_forgotPassword(self, REQUEST):
""" So something about forgetting your password """
if self.currentMembershipSource:
return self.currentMembershipSource.forgotPassword(REQUEST)
def __creatable_by_emergency_user__(self): return 1
def manage_addUser(self, REQUEST):
""" Add a New User """
username=self.reqattr(REQUEST,'username')
password=self.reqattr(REQUEST,'password')
password_confirm=self.reqattr(REQUEST,'password_confirm')
roles=self.reqattr(REQUEST,'roles')
groups=self.reqattr(REQUEST, 'groupnames', [])
if not username:
return self.MessageDialog(self,REQUEST=REQUEST,
title ='Illegal value',
message='A username must be specified',
action ='manage_main')
if not password or not password_confirm:
return self.MessageDialog(self,REQUEST=REQUEST,
title ='Illegal value',
message='Password and confirmation must be specified',
action ='manage_main')
if (self.getUser(username) or
(self._emergency_user and
username == self._emergency_user.getUserName())):
return self.MessageDialog(self,REQUEST=REQUEST,
title ='Illegal value',
message='A user with the specified name already exists',
action ='manage_main')
if (password or password_confirm) and (password != password_confirm):
return self.MessageDialog(self,REQUEST=REQUEST,
title ='Illegal value',
message='Password and confirmation do not match',
action ='manage_main')
self._doAddUser(username, password, roles, domains='', groups=groups, REQUEST=REQUEST)
#
# Explicitly check our contents, do not just acquire postUserCreate
#
if 'postUserCreate' in self.objectIds():
self.postUserCreate(self, REQUEST)
return self.MessageDialog(self,REQUEST=REQUEST,
title = 'User Created',
message= 'User %s was created.'%(username),
action = 'manage_main')
def _doAddUser(self, name, password, roles, domains='', groups=(), **kw):
""" For programatically adding simple users """
self.currentAuthSource.createUser(name, password, roles)
if self.currentPropSource:
# copy items not in kw from REQUEST
REQUEST = kw.get('REQUEST', self.REQUEST)
map(kw.setdefault, REQUEST.keys(), REQUEST.values())
self.currentPropSource.createUser(name, kw)
def _doChangeUser(self, name, password, roles, domains='', groups=(), **kw):
self.currentAuthSource.updateUser(name, password, roles)
if self.currentPropSource:
# copy items not in kw from REQUEST
REQUEST = kw.get('REQUEST', self.REQUEST)
map(kw.setdefault, REQUEST.keys(), REQUEST.values())
self.currentPropSource.updateUser(name, kw)
# We may have updated roles or passwords... flush the user...
self.cache_removeUser(name)
self.xcache_removeUser(name)
def _doDelUsers(self, names):
self.deleteUsers(names)
def _createInitialUser(self):
if len(self.getUserNames()) <= 1:
info = readUserAccessFile('inituser')
if info:
name, password, domains, remote_user_mode = info
self._doAddUser(name, password, ('Manager',), domains)
def getUsers(self):
"""Return a list of user objects or [] if no users exist"""
data=[]
try:
items=self.listUsers()
for people in items:
user=User({'name': people['username'],
'password': people['password'],
'roles': people['roles'],
'domains': ''},
self.currentPropSource,
self.cryptPassword,
self.currentAuthSource,
self.currentGroupSource)
data.append(user)
except:
import traceback
traceback.print_exc()
pass
return data
getUsers__roles__=('Anonymous','Authenticated')
def getUser(self, name):
"""Return the named user object or None if no such user exists"""
user = self.cache_getUser(name, '', 0)
#zLOG.LOG('exUserFolder.getUser', zLOG.PANIC, 'cache_getUser(%s)=%s' % (name,user))
if user:
return user
try:
items=self.listOneUser(name)
#zLOG.LOG('exUserFolder.getUser', zLOG.PANIC, 'listOneUser=%s' % items)
except:
zLOG.LOG("exUserFolder", zLOG.ERROR,
"error trying to list user %s" % name,
'',
sys.exc_info())
return None
if not items:
return None
for people in items:
user = User({'name': people['username'],
'password':people['password'],
'roles': people['roles'],
'domains': ''},
self.currentPropSource,
self.cryptPassword,
self.currentAuthSource,
self.currentGroupSource)
return user
return None
def manage_userActions(self, submit=None, userids=None, REQUEST={}):
""" Do things to users """
if submit==' Add ':
if hasattr(self.currentAuthSource,'manage_addUserForm'):
return self.currentAuthSource.manage_addUserForm(self, REQUEST)
else:
return self.manage_addUserForm(self, REQUEST)
if submit==' Delete ':
self.deleteUsers(userids)
return self.MessageDialog(self,REQUEST=REQUEST,
title ='Users Deleted',
message='Selected Users have been deleted',
action =REQUEST['URL1']+'/manage_main',
target ='manage_main')
if REQUEST:
return self.manage_main(self,REQUEST)
return ''
def identify(self, auth):
# Identify the username and password. This is where new modes should
# be called from, and if pluggable modes ever take shape, here ya go!
if self.cookie_mode and not auth:
# The identify signature does not include the request, sadly.
# I think that's dumb.
request = self.REQUEST
response = request.RESPONSE
if request.has_key('__ac_name') and request.has_key('__ac_password'):
return request['__ac_name'], request['__ac_password']
elif request.has_key('__ac') and self.cookie_mode == 1:
return self.decodeBasicCookie(request, response)
elif request.has_key('__aca') and self.cookie_mode == 2:
return self.decodeAdvancedCookie(request, response)
if auth and lower(auth[:6]) == 'basic ':
return tuple(split(decodestring(split(auth)[-1]), ':', 1))
return None, None
def decodeUserCookie(self, request, response):
return self.identify('')
def validate(self, request, auth='', roles=_noroles):
"""
Perform identification, authentication, and authorization.
"""
# Called at each web request
#zLOG.LOG('exUserFolder', zLOG.PANIC, 'validate')
v = request['PUBLISHED']
a, c, n, v = self._getobcontext(v, request)
name, password = self.identify(auth) # decode cookie, and raises LoginRequired if no ident info
# password is the cleartext passwd
# zLOG.LOG('exUserFolder', zLOG.DEBUG, 'identify returned %s, %s' % (name, password))
response = request.RESPONSE
if name is not None:
try:
xcached_user = self.xcache_getUser(name)
#zLOG.LOG('exUserFolder.validate', zLOG.PANIC, 'xcached_user=%s' % xcached_user)
if xcached_user:
#zLOG.LOG('exUserFolder.validate', zLOG.PANIC, 'returning None')
return None
except:
zLOG.LOG('exUserFolder', zLOG.ERROR,
"error while looking up '%s' on the xcache" % name,
'',
sys.exc_info())
user = self.authenticate(name, password, request)
#zLOG.LOG('exUserFolder.validate', zLOG.PANIC, 'user=%s' % user)
if user is None:
# If it's none, because there's no user by that name,
# don't raise a login, allow it to go higher...
# This kinda breaks for people putting in the wrong username
# when the Folder above uses a different auth method.
# But it doesn't lock Manager users out inside Zope.
# Perhaps this should be a tunable.
# modified by Emmanuel
try:
lou = self.listOneUser(name)
except:
lou = None
if lou:
self.challenge(request, response, 'login_failed', auth)
return None
self.remember(name, password, request)
self.cache_addToCache(name, password, user)
emergency = self._emergency_user
if emergency and user is emergency:
if self._isTop():
return emergency.__of__(self)
else:
return None
if self.authorize(user, a, c, n, v, roles):
return user.__of__(self)
if self._isTop() and self.authorize(self._nobody, a, c, n, v, roles):
return self._nobody.__of__(self)
self.challenge(request, response, 'unauthorized')
return None
else:
if self.sessionTracking and self.currentPropSource:
user = self.createAnonymousUser(request, response)
if self.authorize(user, a, c, n, v, roles):
return user.__of__(self)
if self.authorize(self._nobody, a, c, n, v, roles):
if self._isTop():
return self._nobody.__of__(self)
else:
return None
else:
self.challenge(request, response, None, auth)
return None
def authenticate(self, name, password, request):
#zLOG.LOG('exUserFolder.authenticate', zLOG.PANIC, '%s %s' % (name, password))
emergency = self._emergency_user
if emergency and name == emergency.getUserName():
return emergency
try:
user = self.cache_getUser(name, password)
#zLOG.LOG('exUserFolder.authenticate', zLOG.PANIC, 'cache_getUser=%s' % user)
if user:
return user
except SessionExpiredException:
if self.idleTimeout:
self.logout(request)
self.challenge(request, request.RESPONSE, 'session_expired')
return None
user = self.getUser(name)
#zLOG.LOG('exUserFolder.authenticate', zLOG.PANIC, 'getUser=%s' % user)
if user is not None:
if user.authenticate(self.currentAuthSource.listOneUser,
password,
request,
self.currentAuthSource.remoteAuthMethod):
return user
return None
def challenge(self, request, response, reason_code='unauthorized',
auth=''):
# Give whatever mode we're in a chance to challenge the validation
# failure. We do this to preserve LoginRequired behavior. The
# other thing we could do is let the None propagate on up and patch
# the request's unauthorized method to
if self.cookie_mode and not auth:
zLOG.LOG('exUserFolder', zLOG.DEBUG, 'raising LoginRequired for %s' % reason_code)
if reason_code == 'login_failed':
response.expireCookie('__ac', path='/')
response.expireCookie('__aca', path='/')
if reason_code:
request.set('authFailedCode', reason_code)
raise LoginRequired(self.docLogin(self, request))
else:
zLOG.LOG('exUserFolder', zLOG.DEBUG, 'not raising LoginRequired for %s' % reason_code)
def remember(self, name, password, request):
response = request.RESPONSE
if self.cookie_mode == 1:
self.setBasicCookie(name, password, request, response)
elif self.cookie_mode == 2:
self.setAdvancedCookie(name, password, request, response)
if self.cookie_mode:
try:
del request.form['__ac_name']
del request.form['__ac_password']
except KeyError:
pass
def makeRedirectPath(self):
REQUEST=self.REQUEST
if not REQUEST.has_key('destination'):
script=REQUEST['SCRIPT_NAME']
pathinfo=REQUEST['PATH_INFO']
redirectstring=script+pathinfo
if REQUEST.has_key('QUERY_STRING'):
querystring='?'+quote(REQUEST['QUERY_STRING'])
redirectstring=redirectstring+querystring
REQUEST['destination']=redirectstring
def redirectToLogin(self, REQUEST):
""" Allow methods to call from Web """
script=''
pathinfo=''
querystring=''
redirectstring=''
authFailedCode=''
if not REQUEST.has_key('destination'):
if self.currentMembershipSource:
redirectstring = self.currentMembershipSource.getLoginDestination(REQUEST)
else:
script=REQUEST['SCRIPT_NAME']
pathinfo=REQUEST['PATH_INFO']
redirectstring=script+pathinfo
if REQUEST.has_key('QUERY_STRING'):
querystring='?'+REQUEST['QUERY_STRING']
redirectstring=redirectstring+querystring
REQUEST['destination']=redirectstring
if REQUEST.has_key('authFailedCode'):
authFailedCode='&authFailedCode='+REQUEST['authFailedCode']
if self.currentMembershipSource and self.currentMembershipSource.loginPage:
try:
REQUEST.RESPONSE.redirect('%s/%s?destination=%s%s'%(self.currentMembershipSource.baseURL, self.currentMembershipSource.loginPage,REQUEST['destination'],authFailedCode))
return
except:
pass
return self.docLogin(self,REQUEST)
def decodeBasicCookie(self, request, response):
c=request['__ac']
c=unquote(c)
try:
c=decodestring(c)
except:
response.expireCookie('__ac', path='/')
raise LoginRequired(self.docLogin(self, request))
name,password=tuple(split(c, ':', 1))
return name, password
def decodeAdvancedCookie(self, request, response):
c = ''
try:
c = request['__aca']
c = unquote(c)
except:
response.expireCookie('__aca', path='/')
response.expireCookie('__ac', path='/') # Precaution
response.flush()
raise LoginRequired(self.docLogin(self, request))
u = self.cache_getCookieCacheUser(c)
if u:
return u
response.expireCookie('__aca', path='/')
response.expireCookie('__ac', path='/') # Precaution
response.flush()
raise LoginRequired(self.docLogin(self, request))
def setBasicCookie(self, name, password, request, response):
token='%s:%s' % (name, password)
token=encodestring(token)
token=quote(token)
response.setCookie('__ac', token, path='/')
request['__ac']=token
def setAdvancedCookie(self, name, password, request, response):
xufid = self._p_oid
hash = encodestring(sha.new('%s%s%f%f%s'%(
name, password, time(), random.random(), str(request))).digest())
token=quote(hash)
response.setCookie('__aca', token, path='/')
response.flush()
request['__aca']=token
self.cache_addToCookieCache(name, password, hash)
def setAnonCookie(self, name, request, resp):
token='%s:%s' % (name, '')
token=encodestring(token)
token=quote(token)
resp.setCookie('__ac', token, path='/')
request['__ac']=token
def createAnonymousUser(self, request, resp):
aName=createTempName()
bogusREQUEST={}
bogusREQUEST['user_realname']='Guest User'
self.currentPropSource.createUser(aName, bogusREQUEST)
ob = AnonUser(aName, [], self.currentPropSource)
ob = ob.__of__(self)
self.cache_addToCache(aName, '', ob)
self.setAnonCookie(aName, request, resp)
return ob
def manage_edit(self, cookie_mode, session_length, sessionTracking=None,
idleTimeout=0, not_session_length=0,
title=None,
REQUEST=None):
"""Change properties"""
self.cookie_mode=cookie_mode
self.sessionLength=session_length
self.notSessionLength=not_session_length
self.sessionTracking=sessionTracking
self.idleTimeout=idleTimeout
if title:
self.title = title
if REQUEST:
return self.MessageDialog(self,REQUEST=REQUEST,
title ='exUserFolder Changed',
message='exUserFolder properties have been updated',
action =REQUEST['URL1']+'/manage_main',
target ='manage_main')
def logout(self, REQUEST):
"""Logout"""
try:
self.cache_removeUser(REQUEST['AUTHENTICATED_USER'].getUserName())
except:
pass
REQUEST['RESPONSE'].expireCookie('__ac', path='/')
REQUEST.cookies['__ac']=''
try:
acc = REQUEST['__aca']
self.cache_removeCookieCacheUser(acc)
REQUEST.cookies['__aca']=''
except:
pass
REQUEST['RESPONSE'].expireCookie('__aca', path='/')
return self.docLogout(self, REQUEST)
#
# Methods to be supplied by Auth Source
#
def deleteUsers(self, userids):
self.currentAuthSource.deleteUsers(userids)
# Comment out to use Andreas' pgSchema
if self.currentPropSource:
self.currentPropSource.deleteUsers(userids)
if self.currentGroupSource:
self.currentGroupSource.deleteUsers(userids)
def listUsers(self):
return self.currentAuthSource.listUsers()
def user_names(self):
return self.currentAuthSource.listUserNames()
def getUserNames(self):
return self.currentAuthSource.listUserNames()
def listOneUser(self,username):
return self.currentAuthSource.listOneUser(username)
def cryptPassword(self, username, password):
if hasattr(aq_base(self.currentAuthSource), 'cryptPassword'):
return self.currentAuthSource.cryptPassword(username, password)
if hasattr(self, 'cryptoId'):
return self.cryptoSources[self.cryptoId].plugin(self, username, password)
return self.cryptoSources['Crypt'].plugin(self, username, password)
def PropertyEditor(self):
""" """
if self.REQUEST.has_key(self.REQUEST['propName']):
return PropertyEditor.EditMethods[self.REQUEST['propType']](self.REQUEST['propName'], self.REQUEST[self.REQUEST['propName']])
return PropertyEditor.EditMethods[self.REQUEST['propType']](self.REQUEST['propName'], None)
def PropertyView(self):
""" """
if self.REQUEST.has_key(self.REQUEST['propName']):
return PropertyEditor.ViewMethods[self.REQUEST['propType']](self.REQUEST['propName'], self.REQUEST[self.REQUEST['propName']])
return PropertyEditor.ViewMethods[self.REQUEST['propType']](self.REQUEST['propName'], None)
def manage_addUserProperty(self, username, propName, propValue, REQUEST):
""" add a new property """
self.currentPropSource.setUserProperty(propName, username, propValue)
if hasattr(self.currentAuthSource,'manage_editUserForm'):
return self.currentAuthSource.manage_editUserForm(self, REQUEST)
else:
return self.manage_editUserForm(self,REQUEST)
def getUserCacheStats(self):
""" Stats """
if self.sessionLength:
if self.cache_getCacheStats()['attempts']:
return self.cache_getCacheStats()
return None
def getUserCacheUsers(self):
""" Current Users """
if self.sessionLength:
return self.cache_getCurrentUsers()
return None
def userFolderAddGroup(self, groupname, title='', **kw):
"""Creates a group"""
if self.currentGroupSource:
apply(self.currentGroupSource.addGroup, (groupname, title), kw)
def userFolderDelGroups(self, groupnames):
"""Deletes groups"""
if self.currentGroupSource:
for groupname in groupnames:
self.currentGroupSource.delGroup(groupname)
def getGroupNames(self):
"""Returns a list of group names"""
if self.currentGroupSource:
return self.currentGroupSource.listGroups()
else:
return []
def getGroupById(self, groupname, default=_marker):
"""Returns the given group"""
if self.currentGroupSource:
group = self.currentGroupSource.getGroup(groupname, default)
if group:
return group.__of__(self)
else:
return None
def setUsersOfGroup(self, usernames, groupname):
"""Sets the users of the group"""
if self.currentGroupSource:
return self.currentGroupSource.setUsersOfGroup(usernames, groupname)
def addUsersToGroup(self, usernames, groupname):
"""Adds users to a group"""
if self.currentGroupSource:
return self.currentGroupSource.addUsersToGroup(usernames, groupname)
def delUsersFromGroup(self, usernames, groupname):
"""Deletes users from a group"""
if self.currentGroupSource:
return self.currentGroupSource.delUsersFromGroup(usernames, groupname)
def setGroupsOfUser(self, groupnames, username):
"""Sets the groups of a user"""
if self.currentGroupSource:
return self.currentGroupSource.setGroupsOfUser(groupnames, username)
def addGroupsOfUser(self, groupnames, username):
"""Add groups to a user"""
if self.currentGroupSource:
return self.currentGroupSource.addGroupsToUser(groupnames, username)
def delGroupsOfUser(self, groupnames, username):
"""Deletes groups from a user"""
if self.currentGroupSource:
return self.currentGroupSource.delGroupsFromUser(groupnames, username)
# We lie.
def hasUsers(self):
return 1
def doAuthSourceForm(self,authId):
""" la de da """
return exUserFolder.authSources[authId].manage_addForm
def doPropSourceForm(self,propId):
""" la de da """
return exUserFolder.propSources[propId].manage_addForm
def doMembershipSourceForm(self, memberId):
""" doot de doo """
return exUserFolder.membershipSources[memberId].manage_addForm
#def doGroupSourceForm(self,groupId):
# """ la de da """
# return exUserFolder.groupSources[groupId].manage_addForm
def getAuthSources(self):
""" Hrm I need a docstring """
l=[]
for o in exUserFolder.authSources.keys():
l.append(
exUserFolder.authSources[o]
)
return l
def getPropSources(self):
""" Hrm I need a docstring """
l=[]
for o in exUserFolder.propSources.keys():
l.append(
exUserFolder.propSources[o]
)
return l
def getMembershipSources(self):
""" Hrm I need a docstring """
l=[]
for o in exUserFolder.membershipSources.keys():
l.append(
exUserFolder.membershipSources[o]
)
return l
def getGroupSources(self):
""" Hrm I need a docstring """
return [] # UNUSED by ScoDoc: empty
def getCryptoSources(self):
""" Doc String """
l = []
for o in exUserFolder.cryptoSources.keys():
l.append(
exUserFolder.cryptoSources[o]
)
return l
def MailHostIDs(self):
"""Find SQL database connections in the current folder and above
This function return a list of ids.
"""
return [] # UNUSED BY SCODOC
from types import ListType, IntType, LongType, FloatType, NoneType, DictType, StringType
def getVariableType(self, o):
if type(o) == ListType:
return 'List'
if type(o) == IntType:
return 'Int'
if type(o) == LongType:
return 'Long'
if type(o) == FloatType:
return 'Float'
if type(o) == NoneType:
return 'None'
if type(o) == DictType:
return 'Dict'
if type(o) == StringType:
return 'String'
return 'Unknown or Restricted'
_postUserCreate='''
<dtml-comment>
Replace this method with whatever you want to do
when a user is created, you can use a Python Script,
or External Method, or keep it as a DTML Method if you
want to
</dtml-comment>
'''