Logo Search packages:      
Sourcecode: zope-exuserfolder version File versions

basicMemberSource.py

#
# 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.
#
00174 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)

00254       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


Generated by  Doxygen 1.6.0   Back to index