diff -Nru /tmp/b8LULaiWF2/splatd-1.1/TODO /tmp/cDUfizKKfF/splatd-1.1.1/TODO --- /tmp/b8LULaiWF2/splatd-1.1/TODO 2006-08-25 16:45:50.000000000 -0700 +++ /tmp/cDUfizKKfF/splatd-1.1.1/TODO 2007-01-30 20:23:59.000000000 -0800 @@ -1,3 +1,5 @@ +- Minimize code duplication in plugin parseOptions() methods by extending + ZConfig. - Plugins for cleaning up mediawiki and bugzilla databases when accounts are deleted. - Make ./setup.py install also install man pages and perhaps xhtml diff -Nru /tmp/b8LULaiWF2/splatd-1.1/debian/changelog /tmp/cDUfizKKfF/splatd-1.1.1/debian/changelog --- /tmp/b8LULaiWF2/splatd-1.1/debian/changelog 2007-08-21 08:10:13.000000000 -0700 +++ /tmp/cDUfizKKfF/splatd-1.1.1/debian/changelog 2007-08-21 08:10:13.000000000 -0700 @@ -1,3 +1,9 @@ +splatd (1.1.1-0ubuntu1) gutsy; urgency=low + + * New upstream version. + + -- Nick Barkas Tue, 21 Aug 2007 09:32:23 -0400 + splatd (1.1-0ubuntu1) feisty; urgency=low * Initial release. diff -Nru /tmp/b8LULaiWF2/splatd-1.1/debian/control /tmp/cDUfizKKfF/splatd-1.1.1/debian/control --- /tmp/b8LULaiWF2/splatd-1.1/debian/control 2007-08-21 08:10:13.000000000 -0700 +++ /tmp/cDUfizKKfF/splatd-1.1.1/debian/control 2007-08-21 08:10:13.000000000 -0700 @@ -3,10 +3,10 @@ XS-Python-Version: >= 2.4 Priority: optional Maintainer: Ubuntu MOTU Developers +XSBC-Original-Maintainer: Nick Barkas Build-Depends: debhelper (>= 5.0.38), po-debconf Build-Depends-Indep: python-central (>= 0.5.8), python-all-dev Standards-Version: 3.7.2 -Uploaders: Nick Barkas Package: splatd Architecture: all diff -Nru /tmp/b8LULaiWF2/splatd-1.1/docs/xhtml/ch01.html /tmp/cDUfizKKfF/splatd-1.1.1/docs/xhtml/ch01.html --- /tmp/b8LULaiWF2/splatd-1.1/docs/xhtml/ch01.html 2006-11-04 12:24:14.000000000 -0800 +++ /tmp/cDUfizKKfF/splatd-1.1.1/docs/xhtml/ch01.html 2007-03-13 10:28:44.000000000 -0700 @@ -1,6 +1,6 @@ -Chapter 1. Installing and Using Splat

Chapter 1. Installing and Using Splat

Installation

Prerequisites

Splat depends on the following components:

Installing with the Python Distutils

Splat uses the standard Python distutils. To install, simply run +Chapter 1. Installing and Using Splat

Chapter 1. Installing and Using Splat

Installation

Prerequisites

Splat depends on the following components:

Installing with the Python Distutils

Splat uses the standard Python distutils. To install, simply run setup.py:

./setup.py install

The Splat library will be installed in the Python site-packages directory. The splatd daemon will be installed in the Python-specified bin directory. An example configuration file, diff -Nru /tmp/b8LULaiWF2/splatd-1.1/docs/xhtml/ch01s02.html /tmp/cDUfizKKfF/splatd-1.1.1/docs/xhtml/ch01s02.html --- /tmp/b8LULaiWF2/splatd-1.1/docs/xhtml/ch01s02.html 2006-11-04 12:24:14.000000000 -0800 +++ /tmp/cDUfizKKfF/splatd-1.1.1/docs/xhtml/ch01s02.html 2007-03-13 10:28:44.000000000 -0700 @@ -1,8 +1,8 @@ -Configuring Splat

Configuring Splat

splat.conf

Splat is configured by a configuration file usually named +Configuring Splat

Configuring Splat

splat.conf

Splat is configured by a configuration file usually named splat.conf. See the example config below (this - config file is also included with the Splat source), and the splat.conf man page.

Example 1.1. Example splat.conf

+        config file is also included with the Splat source), and the splat.conf man page.

Example 1.1. Example splat.conf

 <LDAP>
     # The LDAP Server configuration.
     # URI of the server(s)
@@ -136,7 +136,8 @@
     	Value 1000
     </Option>
     <Option skeldir>
-        # Skeletal home directory
+        # Skeletal home directory to copy files from. By default, created 
+        # home directories are empty.
         Value /usr/share/skel
     </Option>
     <Option postcreate>
diff -Nru /tmp/b8LULaiWF2/splatd-1.1/docs/xhtml/ch01s03.html /tmp/cDUfizKKfF/splatd-1.1.1/docs/xhtml/ch01s03.html
--- /tmp/b8LULaiWF2/splatd-1.1/docs/xhtml/ch01s03.html	2006-11-04 12:24:14.000000000 -0800
+++ /tmp/cDUfizKKfF/splatd-1.1.1/docs/xhtml/ch01s03.html	2007-03-13 10:28:44.000000000 -0700
@@ -1,6 +1,6 @@
 
 
-Running Splat

Running Splat

splatd

splatd detaches from the terminal and +Running Splat

Running Splat

splatd

splatd detaches from the terminal and executes your service modules according to the intervals specified in the configuration file. While the main distribution doesn't include any startup scripts, one is installed by the FreeBSD port, Ubuntu package, diff -Nru /tmp/b8LULaiWF2/splatd-1.1/docs/xhtml/ch02.html /tmp/cDUfizKKfF/splatd-1.1.1/docs/xhtml/ch02.html --- /tmp/b8LULaiWF2/splatd-1.1/docs/xhtml/ch02.html 2006-11-04 12:24:15.000000000 -0800 +++ /tmp/cDUfizKKfF/splatd-1.1.1/docs/xhtml/ch02.html 2007-03-13 10:28:45.000000000 -0700 @@ -1,6 +1,6 @@ -Chapter 2. Included Modules

Chapter 2. Included Modules

Introduction

Splat was originally written for the purpose of distributing SSH +Chapter 2. Included Modules

Chapter 2. Included Modules

Introduction

Splat was originally written for the purpose of distributing SSH keys from LDAP in a way that did not require modifying the SSH daemon.

In the process, we designed a generic daemon capable of pulling nearly any information from LDAP and using it in any way you see fit. diff -Nru /tmp/b8LULaiWF2/splatd-1.1/docs/xhtml/ch02s02.html /tmp/cDUfizKKfF/splatd-1.1.1/docs/xhtml/ch02s02.html --- /tmp/b8LULaiWF2/splatd-1.1/docs/xhtml/ch02s02.html 2006-11-04 12:24:14.000000000 -0800 +++ /tmp/cDUfizKKfF/splatd-1.1.1/docs/xhtml/ch02s02.html 2007-03-13 10:28:45.000000000 -0700 @@ -1,16 +1,20 @@ -Home Directories

Home Directories

The Home Directory service module will query LDAP for users, +Home Directories

Home Directories

The Home Directory service module will query LDAP for users, create a home directory if it does not already exist, copy in template home directory, and optionally run a user supplied post-create - script.

LDAP Schema

The Home Directory service module uses the standard RFC 2307 (or - 2307bis) schema.

Home Directory Service Options

The Home Directory service module supports sanity checking on + script.

LDAP Schema

The Home Directory service module uses the standard RFC 2307 (or + 2307bis) schema.

Home Directory Service Options

The Home Directory service module supports sanity checking on the uid, gid, and home directory, home directory templates, and - optional post-create script.

Home Directory Service Options

home

Only create home directories that are subdirectories of + optional post-create script.

Home Directory Service Options

home

Only create home directories that are subdirectories of the specified directory.

minuid

Do not create home directories for uids lower than minuid.

mingid

Do not create home directories for gids lower than - mingid.

skeldir

Home directory template. Defaults to - /usr/share/skel.

postcreate

Execute a post-create command after the user's home + mingid.

skeldir

Path to a template or skeletal home directory. If this + option is used, files named with a prefix of dot. such as + dot.filename will be copied to + .filename in the created home directory. Files + without this dot. prefix will have the same name in the new + home directory.

postcreate

Execute a post-create command after the user's home directory has been created. The command is passed the new user's numeric uid, numeric gid, and the full path to the new home directory.

diff -Nru /tmp/b8LULaiWF2/splatd-1.1/docs/xhtml/ch02s03.html /tmp/cDUfizKKfF/splatd-1.1.1/docs/xhtml/ch02s03.html --- /tmp/b8LULaiWF2/splatd-1.1/docs/xhtml/ch02s03.html 2006-11-04 12:24:14.000000000 -0800 +++ /tmp/cDUfizKKfF/splatd-1.1.1/docs/xhtml/ch02s03.html 2007-03-13 10:28:45.000000000 -0700 @@ -1,9 +1,9 @@ -OpenSSH Public Keys

OpenSSH Public Keys

The SSH service module will extract SSH keys from LDAP, optionally +OpenSSH Public Keys

OpenSSH Public Keys

The SSH service module will extract SSH keys from LDAP, optionally create a home directory if it does not already exist, apply any key command limitations specified by the administrator, and atomically - overwrite the user's authorized_keys file.

LDAP Schema

To store the SSH keys in LDAP, we created an sshAccount schema + overwrite the user's authorized_keys file.

LDAP Schema

To store the SSH keys in LDAP, we created an sshAccount schema in our Three Rings Enterprise OID space. The schema is included with the Splat distribution as schema/ooossh.schema. It depends on another included schema: schema/ooo.schema @@ -22,9 +22,9 @@ DESC 'OpenSSH Account' SUP top AUXILIARY MAY ( sshPublicKey ) ) -

SSH Service Options

The SSH service module supports +

SSH Service Options

The SSH service module supports authorized_keys command limitations and sanity - checking on the uid, gid, and home directory.

SSH Service Options

makehome

Create home directories for users. Set + checking on the uid, gid, and home directory.

SSH Service Options

makehome

Create home directories for users. Set to true to enable home directory creation, but be aware that other plugins (such as the homeDirectory plugin) may diff -Nru /tmp/b8LULaiWF2/splatd-1.1/docs/xhtml/ch02s04.html /tmp/cDUfizKKfF/splatd-1.1.1/docs/xhtml/ch02s04.html --- /tmp/b8LULaiWF2/splatd-1.1/docs/xhtml/ch02s04.html 2006-11-04 12:24:15.000000000 -0800 +++ /tmp/cDUfizKKfF/splatd-1.1.1/docs/xhtml/ch02s04.html 2007-03-13 10:28:45.000000000 -0700 @@ -1,15 +1,15 @@ -User Mail Forward Files

User Mail Forward Files

The User Mail Forward service module was written by Kevin Van +User Mail Forward Files

User Mail Forward Files

The User Mail Forward service module was written by Kevin Van Vechten of the OpenDarwin project. The module will extract the mailForwardingAddress attribute from LDAP, optionally create a home directory if it does not already exist, and atomically overwrite the user's - .forward file.

LDAP Schema

The mailForwardingAddress attribute can be + .forward file.

LDAP Schema

The mailForwardingAddress attribute can be found in several schema definitions.

  • As an optional attribute of the qmailUser object class as defined in the Qmail LDAP schema.

  • As an optional attribute of the nsMessagingServerUser - class in the Netscape LDAP schema.

User Mail Forwarding Service Options

The User Mail Forwarding service module supports sanity checking - on the uid, gid, and home directory.

User Mail Forwarding Service Options

makehome

Create home directories for users. Set + class in the Netscape LDAP schema.

User Mail Forwarding Service Options

The User Mail Forwarding service module supports sanity checking + on the uid, gid, and home directory.

User Mail Forwarding Service Options

makehome

Create home directories for users. Set to true to enable home directory creation, but be aware that other plugins (such as the homeDirectory plugin) may diff -Nru /tmp/b8LULaiWF2/splatd-1.1/docs/xhtml/ch02s05.html /tmp/cDUfizKKfF/splatd-1.1.1/docs/xhtml/ch02s05.html --- /tmp/b8LULaiWF2/splatd-1.1/docs/xhtml/ch02s05.html 2006-11-04 12:24:15.000000000 -0800 +++ /tmp/cDUfizKKfF/splatd-1.1.1/docs/xhtml/ch02s05.html 2007-03-13 10:28:45.000000000 -0700 @@ -1,8 +1,8 @@ -Purge User Home Directories

Purge User Home Directories

The Home Directory Purging service module can archive, delete, +Purge User Home Directories

Purge User Home Directories

The Home Directory Purging service module can archive, delete, and later delete archives of the home directories of users set to be - purgeable in LDAP.

LDAP Schema

We created a purgeableAccount schema in the Three Rings + purgeable in LDAP.

LDAP Schema

We created a purgeableAccount schema in the Three Rings Enterprise OID space to keep track of the date and time at which an LDAP user's home directory should be archived and deleted. The needed schema can be found in schema/ooo.schema @@ -22,7 +22,7 @@ DESC 'Purgeable Account' SUP top AUXILIARY MAY ( pendingPurge ) ) -

Home Directory Purging Service Options

+

Home Directory Purging Service Options

The Home Directory Purging module supports the following options. The home, minuid diff -Nru /tmp/b8LULaiWF2/splatd-1.1/docs/xhtml/ch03.html /tmp/cDUfizKKfF/splatd-1.1.1/docs/xhtml/ch03.html --- /tmp/b8LULaiWF2/splatd-1.1/docs/xhtml/ch03.html 2006-11-04 12:24:15.000000000 -0800 +++ /tmp/cDUfizKKfF/splatd-1.1.1/docs/xhtml/ch03.html 2007-03-13 10:28:46.000000000 -0700 @@ -1,6 +1,6 @@ -Chapter 3. Writing New Modules

Chapter 3. Writing New Modules

Introduction

Splat Helper Modules are implemented as a Python module containing +Chapter 3. Writing New Modules

Chapter 3. Writing New Modules

Introduction

Splat Helper Modules are implemented as a Python module containing a single subclass of splat.plugin.Helper. The subclass must implement three methods: attributes, diff -Nru /tmp/b8LULaiWF2/splatd-1.1/docs/xhtml/ch03s02.html /tmp/cDUfizKKfF/splatd-1.1.1/docs/xhtml/ch03s02.html --- /tmp/b8LULaiWF2/splatd-1.1/docs/xhtml/ch03s02.html 2006-11-04 12:24:15.000000000 -0800 +++ /tmp/cDUfizKKfF/splatd-1.1.1/docs/xhtml/ch03s02.html 2007-03-13 10:28:45.000000000 -0700 @@ -1,12 +1,12 @@ -Helper Class Implementation

Helper Class Implementation

Splat Helper Modules are implemented as a Python module containing +Helper Class Implementation

Helper Class Implementation

Splat Helper Modules are implemented as a Python module containing a single subclass of splat.plugin.Helper. The subclass must implement three methods: parseOptions, work, and attributes.

For a working example, refer to the SSH public key helper located - in splat/helpers/sshPublicKeys.py.

Example 3.1. Skeleton of Splat Helper Module

+      in splat/helpers/sshPublicKeys.py.

Example 3.1. Skeleton of Splat Helper Module

 class MyHelper(splat.plugin.Helper):
     def attributes(self):
         ...
@@ -14,16 +14,16 @@
         ...
     def work(self, context, ldapEntry, modified):
         ...
-        

Error Handling and Logging

If an error occurs, your module must raise an exception of type +

Error Handling and Logging

If an error occurs, your module must raise an exception of type splat.plugin.SplatPluginError, with an error message provided as the second argument. This error will be presented to the user by the splat daemon.

raise splat.plugin.SplatPluginError, "No such user"

Logging is provided by the standard logging module. To find the - Splat logger, use splat.LOG_NAME:

logger = logging.getLogger(splat.LOG_NAME)

Parsing Configuration Options

Helper-specific configuration options may be specified in the + Splat logger, use splat.LOG_NAME:

logger = logging.getLogger(splat.LOG_NAME)

Parsing Configuration Options

Helper-specific configuration options may be specified in the splat.conf configuration file. These options are passed to the helper's parseOptions method as a dictionary of key/value strings. These options are not validated by the configuration parser -- it is your responsibility to validate - these strings and convert them to the appropriate data type.

Example 3.2. Helper Module parseOptions() Method

+        these strings and convert them to the appropriate data type.

Example 3.2. Helper Module parseOptions() Method

 def parseOptions(self, options):
     context = {}
 
@@ -34,7 +34,7 @@
         raise splat.plugin.SplatPluginError, "Invalid options '%s' specified." % key
 
     return context
-            

Handling LDAP Entries

The splat daemon will perform an LDAP search on the helper's +

Handling LDAP Entries

The splat daemon will perform an LDAP search on the helper's behalf, filter based on group restrictions, and pass individual LDAP Entry instances to the work method. The following method arguments @@ -68,7 +68,7 @@ requested attributes, and/or the modifyTimestamp attribute. It is your responsibility to ensure that any attributes required by your helper - are supplied.

Example 3.3. Helper Module attributes() and work() Methods

+          are supplied.

Example 3.3. Helper Module attributes() and work() Methods

 class MyHelper(splat.plugin.Helper):
     def attributes(self):
         return ('uid', 'sshPublicKey')
diff -Nru /tmp/b8LULaiWF2/splatd-1.1/docs/xhtml/index.html /tmp/cDUfizKKfF/splatd-1.1.1/docs/xhtml/index.html
--- /tmp/b8LULaiWF2/splatd-1.1/docs/xhtml/index.html	2006-11-04 12:24:16.000000000 -0800
+++ /tmp/cDUfizKKfF/splatd-1.1.1/docs/xhtml/index.html	2007-03-13 10:28:46.000000000 -0700
@@ -1,8 +1,8 @@
 
 
-Splat: Scalable Periodic LDAP Attribute Transmogrifier

Splat: Scalable Periodic LDAP Attribute Transmogrifier

The content of this guide is the original work of Three Rings +Splat: Scalable Periodic LDAP Attribute Transmogrifier

Splat: Scalable Periodic LDAP Attribute Transmogrifier

The content of this guide is the original work of Three Rings Design, Inc, Landon Fuller, and Nick Barkas. All rights reserved.

The XML and CSS used to generate this guide is based on the work of Will Barton and Michael Maibaum, as contributed to the DarwinPorts - Project under the 3 clause BSD license. Their copyright remains.



+
diff -Nru /tmp/b8LULaiWF2/splatd-1.1/docs/xhtml/pr01.html /tmp/cDUfizKKfF/splatd-1.1.1/docs/xhtml/pr01.html --- /tmp/b8LULaiWF2/splatd-1.1/docs/xhtml/pr01.html 2006-11-04 12:24:13.000000000 -0800 +++ /tmp/cDUfizKKfF/splatd-1.1.1/docs/xhtml/pr01.html 2007-03-13 10:28:44.000000000 -0700 @@ -1,6 +1,6 @@ -About Splat

About Splat

Splat was written by Landon Fuller +About Splat

About Splat

Splat was written by Landon Fuller and Will Barton for the purpose of distributing SSH keys from LDAP in a way that did not require modifying the SSH diff -Nru /tmp/b8LULaiWF2/splatd-1.1/docs/xhtml/rn01.html /tmp/cDUfizKKfF/splatd-1.1.1/docs/xhtml/rn01.html --- /tmp/b8LULaiWF2/splatd-1.1/docs/xhtml/rn01.html 2006-11-04 12:24:16.000000000 -0800 +++ /tmp/cDUfizKKfF/splatd-1.1.1/docs/xhtml/rn01.html 2007-03-13 10:28:46.000000000 -0700 @@ -1,5 +1,5 @@ -Splat Man Pages

Splat Man Pages


Table of Contents

splatd - Scalable Periodic LDAP Attribute Transmogrifier
splat.conf - +Splat Man Pages

Splat Man Pages


Table of Contents

splatd - Scalable Periodic LDAP Attribute Transmogrifier
splat.conf - Scalable Periodic LDAP Attribute Transmogrifier configuration file
diff -Nru /tmp/b8LULaiWF2/splatd-1.1/docs/xhtml/rn01re01.html /tmp/cDUfizKKfF/splatd-1.1.1/docs/xhtml/rn01re01.html --- /tmp/b8LULaiWF2/splatd-1.1/docs/xhtml/rn01re01.html 2006-11-04 12:24:15.000000000 -0800 +++ /tmp/cDUfizKKfF/splatd-1.1.1/docs/xhtml/rn01re01.html 2007-03-13 10:28:46.000000000 -0700 @@ -1,11 +1,11 @@ -splatd

Name

splatd — Scalable Periodic LDAP Attribute Transmogrifier

Synopsis

splatd [-h] [-d] [-f config] [-p pidfile]

Description

+splatd

Name

splatd — Scalable Periodic LDAP Attribute Transmogrifier

Synopsis

splatd [-h] [-d] [-f config] [-p pidfile]

Description

Splat is a daemon designed to help keep information in an LDAP directory in sync with information outside of an LDAP directory. This information can be any set of attributes on any object in the LDAP directory. -

Options

-h

Show summary of options.

-d

Debug mode. Will not fork and run in the background.

-f

Splat configuration file.

-p

File to write daemon pid to.

See Also

+

Options

-h

Show summary of options.

-d

Debug mode. Will not fork and run in the background.

-f

Splat configuration file.

-p

File to write daemon pid to.

See Also

splat.conf(5)

http://dpw.threerings.net/projects/splat. diff -Nru /tmp/b8LULaiWF2/splatd-1.1/docs/xhtml/rn01re02.html /tmp/cDUfizKKfF/splatd-1.1.1/docs/xhtml/rn01re02.html --- /tmp/b8LULaiWF2/splatd-1.1/docs/xhtml/rn01re02.html 2006-11-04 12:24:15.000000000 -0800 +++ /tmp/cDUfizKKfF/splatd-1.1.1/docs/xhtml/rn01re02.html 2007-03-13 10:28:46.000000000 -0700 @@ -2,7 +2,7 @@ splat.conf

Name

splat.conf — Scalable Periodic LDAP Attribute Transmogrifier configuration file -

Introduction

The Splat configuration file is composed of three different +

Introduction

The Splat configuration file is composed of three different section types:

  • LDAP Configuration

  • Service Helper Configuration

  • Logging Configuration

The configuration uses an Apache-style syntax:

 <LDAP>
@@ -12,23 +12,23 @@
     # The default search base for the server
     BaseDN      dc=example,dc=com
 </LDAP>
-                    

LDAP Configuration

The LDAP section defines +

LDAP Configuration

The LDAP section defines connection parameters for your LDAP server, and may appear once within a Splat configuration file. If you make use of SSL/TLS, you will need to ensure that the appropriate settings are enabled in your system ldap.conf (e.g. TLS_CACERT or - TLS_CACERTDIR, if necessary).

LDAP Configuration Options

URI

URI of the LDAP server(s).

BaseDN

Default Search Base DN.

BindDN

LDAP Bind DN. If omitted, Slap will use an anonymous - bind.

Password

LDAP Bind Password.

Logging Configuration

The Logging section configures + TLS_CACERTDIR, if necessary).

LDAP Configuration Options

URI

URI of the LDAP server(s).

BaseDN

Default Search Base DN.

BindDN

LDAP Bind DN. If omitted, Slap will use an anonymous + bind.

Password

LDAP Bind Password.

Logging Configuration

The Logging section configures logging for the Splat daemon. It is composed of any number of syslog or - logfile subsections.

Syslog Configuration Options

Level

Log Verbosity. One of: debug, info, warning, error, + logfile subsections.

Syslog Configuration Options

Level

Log Verbosity. One of: debug, info, warning, error, critical

Facility

Syslog Facility.

Address

Address of syslog server, or pathname to syslog socket. This varies between hosts. FreeBSD's syslog socket is located at /var/run/log, while Linux systems often - use /dev/log.

Logfile Configuration Options

Level

Log Verbosity. One of: debug, info, warning, error, + use /dev/log.

Logfile Configuration Options

Level

Log Verbosity. One of: debug, info, warning, error, critical

Path

Path to log file, or reference to Python file object - such as STDOUT.

Service Helper Configuration

The Service section configures + such as STDOUT.

Service Helper Configuration

The Service section configures Splat's service helper modules. It is composed service-specific settings, including any number of Option, and @@ -47,20 +47,20 @@ # Unset the Command option <Option command/> -

Service Configuration Options

Helper

Python Helper Module. Example: +

Service Configuration Options

Helper

Python Helper Module. Example: splat.helpers.sshPublicKeys

Frequency

Frequency at which helper is invoked. Units may be specified in hours (h), minutes (m), or seconds (s).

SearchBase

LDAP search base. If left unspecified, defaults to the BaseDN specified in the LDAP configuration section.

SearchFilter

LDAP search filter. All records that match this filter (and, optionally, are members of a permitted group) will be passed to the service helper module.

RequireGroup (yes/no)

Require that returned entries match one of the specified - Groups.

Group Configuration Options

SearchBase

LDAP search base. If left unspecified, defaults to the + Groups.

Group Configuration Options

SearchBase

LDAP search base. If left unspecified, defaults to the BaseDN specified in the LDAP configuration section.

SearchFilter

LDAP search filter. All records that match this filter will determine this groups membership. The filter should return groupOfUniqueNames or groupOfNames objects. I suggest using RFC2307bis to combine posixGroup and groupOfUniqueName.

MemberAttribute

LDAP attribute used to store member DNs. Defaults to - uniqueMember.

Examples

An example configuration file splat.conf is - included with the Splat source.

See Also

+ uniqueMember.

Examples

An example configuration file splat.conf is + included with the Splat source.

See Also

splatd(8) ldap.conf(5)

diff -Nru /tmp/b8LULaiWF2/splatd-1.1/docs/xml/splat.xml /tmp/cDUfizKKfF/splatd-1.1.1/docs/xml/splat.xml --- /tmp/b8LULaiWF2/splatd-1.1/docs/xml/splat.xml 2006-09-12 11:36:57.000000000 -0700 +++ /tmp/cDUfizKKfF/splatd-1.1.1/docs/xml/splat.xml 2006-11-09 12:01:04.000000000 -0800 @@ -213,8 +213,12 @@ skeldir - Home directory template. Defaults to - /usr/share/skel. + Path to a template or skeletal home directory. If this + option is used, files named with a prefix of dot. such as + dot.filename will be copied to + .filename in the created home directory. Files + without this dot. prefix will have the same name in the new + home directory. diff -Nru /tmp/b8LULaiWF2/splatd-1.1/splat/helpers/homeDirectory.py /tmp/cDUfizKKfF/splatd-1.1.1/splat/helpers/homeDirectory.py --- /tmp/b8LULaiWF2/splatd-1.1/splat/helpers/homeDirectory.py 2006-08-23 20:19:02.000000000 -0700 +++ /tmp/cDUfizKKfF/splatd-1.1.1/splat/helpers/homeDirectory.py 2007-01-31 15:48:24.000000000 -0800 @@ -34,31 +34,38 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -import os, logging, re, shutil, errno - +import os import splat from splat import plugin +import homeutils -import homeHelper - -logger = logging.getLogger(splat.LOG_NAME) - -class WriterContext(homeHelper.WriterContext): +class WriterContext(object): def __init__(self): - homeHelper.WriterContext.__init__(self) - self.skeldir = '/usr/share/skel' # Default skeletal home directory + self.home = None + self.minuid = None + self.mingid = None + self.skeldir = None self.postcreate = None -class Writer(homeHelper.Writer): +class Writer(plugin.Helper): + def attributes(self): + return homeutils.requiredAttributes() + def parseOptions(self, options): context = WriterContext() - # Get basic options using superclass parseOptions method, and copy them - # to this context object. - superContext = vars(homeHelper.Writer.parseOptions(self, options)) - for opt in superContext.keys(): - setattr(context, opt, superContext[opt]) - for key in options.keys(): + for key in options.iterkeys(): + if (key == 'home'): + context.home = str(options[key]) + if (context.home[0] != '/'): + raise plugin.SplatPluginError, "Relative paths for the home option are not permitted" + continue + if (key == 'minuid'): + context.minuid = int(options[key]) + continue + if (key == 'mingid'): + context.mingid = int(options[key]) + continue if (key == 'skeldir'): context.skeldir = os.path.abspath(options[key]) # Validate skel directory @@ -72,89 +79,11 @@ return context - # Recursively copy a directory tree, preserving permission modes and access - # times, but changing ownership of files to uid:gid. Also, renames - # files/directories named dot.foo to .foo. - def _copySkelDir(self, srcDir, destDir, uid, gid): - # Regular expression matching files named dot.foo - pattern = re.compile('^dot\.') - for srcFile in os.listdir(srcDir): - destFile = pattern.sub('.', srcFile) - # Not portable: hardcoded / as path delimeter - srcPath = srcDir + '/' + srcFile - destPath = destDir + '/' + destFile - - # Go deeper if we are copying a sub directory - if (os.path.isdir(srcPath)): - try: - os.makedirs(destPath) - shutil.copystat(srcPath, destPath) - except OSError, e: - raise plugin.SplatPluginError, "Failed to create destination directory: %s" % destPath - continue - - self._copySkelDir(srcPath, destPath, uid, gid) - - # Copy regular files - else: - try: - shutil.copy2(srcPath, destPath) - except IOError, e: - raise plugin.SplatPluginError, "Failed to copy %s to %s: %s" % (srcPath, destPath, e) - continue - - # Change ownership of files/directories after copied - try: - os.chown(destPath, uid, gid) - except OSError, e: - raise plugin.SplatPluginError, "Failed to change ownership of %s to %d:%d" % (destPath, uid, gid) - continue - def work(self, context, ldapEntry, modified): - (home, uid, gid) = self.getAttributes(context, ldapEntry) - - # Create the home directory, unless it already exists - if (not os.path.isdir(home)): - try: - os.makedirs(home) - os.chown(home, uid, gid) - except OSError, e: - raise plugin.SplatPluginError, "Failed to create home directory, %s" % e - # If it does already exist, do nothing at all and we are done - else: + # Skip unmodified entries + if (not modified): return - - # Copy files from skeletal directories to user's home directory - self._copySkelDir(context.skeldir, home, uid, gid) - - # Fork and run post create script if it was defined - if (context.postcreate != None): - pipe = os.pipe() - inf = os.fdopen(pipe[0], 'r') - - pid = os.fork() - if (pid == 0): - try: - os.execl(context.postcreate, context.postcreate, str(uid), str(gid), home) - except OSError, e: - raise plugin.SplatPluginError, "Failed to execute post-creation script %s [Errno %d] %s." % (context.postcreate, e.errno, e.strerror) - - else: - while (1): - try: - result = os.waitpid(pid, 0) - except OSError, e: - if (e.errno == errno.EINTR): - continue - raise - break - status = os.WEXITSTATUS(result[1]) - - # Check if child process exited happily. - if (status == 0): - inf.close() - return - else: - errstr = inf.readline() - inf.close() - raise plugin.SplatPluginError, "Post creation script %s %d %d %s exited abnormally: %s" % (context.postcreate, uid, gid, home, errstr) + + # Otherwise create the home directory + (home, uid, gid) = homeutils.getLDAPAttributes(ldapEntry, context.home, context.minuid, context.mingid) + homeutils.makeHomeDir(home, uid, gid, context.skeldir, context.postcreate) diff -Nru /tmp/b8LULaiWF2/splatd-1.1/splat/helpers/homeHelper.py /tmp/cDUfizKKfF/splatd-1.1.1/splat/helpers/homeHelper.py --- /tmp/b8LULaiWF2/splatd-1.1/splat/helpers/homeHelper.py 2006-08-23 20:19:02.000000000 -0700 +++ /tmp/cDUfizKKfF/splatd-1.1.1/splat/helpers/homeHelper.py 1969-12-31 16:00:00.000000000 -0800 @@ -1,113 +0,0 @@ -# homeHelper.py vi:ts=4:sw=4:expandtab: -# -# Generic helper class for plugins that deal with home directories. -# Authors: -# Nick Barkas -# -# Copyright (c) 2006 Three Rings Design, Inc. -# 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 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. -# 3. Neither the name of the copyright owner nor the names of contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# 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 os -import splat -from splat import plugin - -class WriterContext(object): - """ Option Context """ - def __init__(self): - self.minuid = None - self.mingid = None - self.home = None - self.splitHome = None - -class Writer(plugin.Helper): - # Required Attributes - def attributes(self): - return ('homeDirectory', 'gidNumber', 'uidNumber') - - def parseOptions(self, options): - """ - Returns context object with attributes set to values of the - home, uid, and gid keys (if they exist) in options dictionary. - These three key value pairs will be deleted so subclasses may - later parse other options from the dictionary and not have to - worry about option key/value pairs that have already been - handled by a call to this method. This method ignores any other - key value pairs in the dictionary. - """ - context = WriterContext() - - for key in options.keys(): - if (key == 'home'): - context.home = str(options[key]) - if (context.home[0] != '/'): - raise plugin.SplatPluginError, "Relative paths for the home option are not permitted" - splitHome = context.home.split('/') - context.splitHome = splitHome - del options[key] - continue - if (key == 'minuid'): - context.minuid = int(options[key]) - del options[key] - continue - if (key == 'mingid'): - context.mingid = int(options[key]) - del options[key] - continue - return context - - def getAttributes(self, context, ldapEntry): - attributes = ldapEntry.attributes - - # Test for required attributes - if (not (attributes.has_key('homeDirectory') and attributes.has_key('uidNumber') and attributes.has_key('gidNumber'))): - raise plugin.SplatPluginError, "Required attributes homeDirectory, uidNumber, and gidNumber not all specified." - - home = attributes.get("homeDirectory")[0] - uid = int(attributes.get("uidNumber")[0]) - gid = int(attributes.get("gidNumber")[0]) - - # Validate the home directory - if (context.home != None): - givenPath = os.path.abspath(home).split('/') - if (len(givenPath) < len(context.splitHome)): - raise plugin.SplatPluginError, "LDAP Server returned home directory (%s) located outside of %s for entry '%s'" % (home, context.home, ldapEntry.dn) - - for i in range(0, len(context.splitHome)): - if (context.splitHome[i] != givenPath[i]): - raise plugin.SplatPluginError, "LDAP Server returned home directory (%s) located outside of %s for entry '%s'" % (home, context.home, ldapEntry.dn) - - # Validate the UID - if (context.minuid != None): - if (context.minuid > uid): - raise plugin.SplatPluginError, "LDAP Server returned uid %d less than specified minimum uid of %d for entry '%s'" % (uid, context.minuid, ldapEntry.dn) - - # Validate the GID - if (context.mingid != None): - if (context.mingid > gid): - raise plugin.SplatPluginError, "LDAP Server returned gid %d less than specified minimum gid of %d for entry '%s'" % (gid, context.mingid, ldapEntry.dn) - - return (home, uid, gid) diff -Nru /tmp/b8LULaiWF2/splatd-1.1/splat/helpers/homeutils.py /tmp/cDUfizKKfF/splatd-1.1.1/splat/helpers/homeutils.py --- /tmp/b8LULaiWF2/splatd-1.1/splat/helpers/homeutils.py 1969-12-31 16:00:00.000000000 -0800 +++ /tmp/cDUfizKKfF/splatd-1.1.1/splat/helpers/homeutils.py 2007-01-31 15:48:24.000000000 -0800 @@ -0,0 +1,202 @@ +# homeutils.py vi:ts=4:sw=4:expandtab: +# +# Support functions for plugins that deal with user home directories. +# Authors: +# Nick Barkas +# +# Copyright (c) 2007 Three Rings Design, Inc. +# 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 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. +# 3. Neither the name of the copyright owner nor the names of contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# 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 os +import shutil +import errno +import re +import splat +from splat import plugin + +def requiredAttributes(): + """ + LDAP attributes needed by any home directory related helper. + + @returns tuple of required attributes. + """ + return ('homeDirectory', 'gidNumber', 'uidNumber') + +def getLDAPAttributes(ldapEntry, homePath=None, minuid=None, mingid=None): + """ + Extract home directory, numberic uid, and numeric gid + attributes from an LDAP record. Also validates these attributes + against minuid, mingid, and home, if they are defined. + + @param ldapEntry: ldaputils.client.Entry object representing + an LDAP record for a user. + @param homePath: LDAP record's homeDirectory must be located within + path given by this string. + @param minuid: LDAP record's minimum acceptable uidNumber. + @param mingid: LDAP record's minimum acceptable gidNumber. + @returns tuple containing the first homeDirectory, uidNumber, + and gidNumber attributes in ldapEntry. + """ + attributes = ldapEntry.attributes + + # Test for required attributes + if (not (attributes.has_key('homeDirectory') and attributes.has_key('uidNumber') and attributes.has_key('gidNumber'))): + raise plugin.SplatPluginError, "Required attributes homeDirectory, uidNumber, and gidNumber not all specified for dn %s." % ldapEntry.dn + + home = attributes.get("homeDirectory")[0] + uid = int(attributes.get("uidNumber")[0]) + gid = int(attributes.get("gidNumber")[0]) + + # Validate the home directory + if (homePath != None): + # Path the user's home directory must be within. + splitHomePath = homePath.split('/') + # User's actual home directory. + splitHome = home.split('/') + for i in range(0, len(splitHomePath)): + if (splitHomePath[i] != splitHome[i]): + raise plugin.SplatPluginError, "LDAP Server returned home directory %s located outside of %s for dn %s" % (home, homePath, ldapEntry.dn) + + # Validate the UID + if (minuid != None): + if (minuid > uid): + raise plugin.SplatPluginError, "LDAP Server returned uid %d less than specified minimum uid of %d for dn %s" % (uid, minuid, ldapEntry.dn) + + # Validate the GID + if (mingid != None): + if (mingid > gid): + raise plugin.SplatPluginError, "LDAP Server returned gid %d less than specified minimum gid of %d for entry '%s'" % (gid, mingid, ldapEntry.dn) + + return (home, uid, gid) + +def makeHomeDir(home, uid, gid, skeldir=None, postcreate=None): + """ + Create a home directory. + + @param home: Path of home directory to create. + @param uid: Numeric user ID of home directory owner. + @param gid: Numerid group ID of home directory owner. + @param skeldir: Optional skeletal home directory to copy files + from. Files with names such as dot.foo will be copied to + the user's home directory as .foo. + @param postcreate: Optional script to run after a home directory + has been created. The script will be given the user's uid, + gid, and home directory as arguments. + """ + # Create the home directory, unless it already exists + if (not os.path.isdir(home)): + try: + os.makedirs(home) + os.chown(home, uid, gid) + except OSError, e: + raise plugin.SplatPluginError, "Failed to create home directory, %s" % e + # If it does already exist, do nothing at all and we are done + else: + return + + # Copy files from skeletal directories to user's home directory if we + # are using a skeldir + if (skeldir != None): + _copySkelDir(skeldir, home, uid, gid) + + # Fork and run post create script if it was defined + if (postcreate != None): + pipe = os.pipe() + inf = os.fdopen(pipe[0], 'r') + + pid = os.fork() + if (pid == 0): + try: + os.execl(postcreate, postcreate, str(uid), str(gid), home) + except OSError, e: + raise plugin.SplatPluginError, "Failed to execute post-creation script %s [Errno %d] %s." % (postcreate, e.errno, e.strerror) + + else: + while (1): + try: + result = os.waitpid(pid, 0) + except OSError, e: + if (e.errno == errno.EINTR): + continue + raise + break + status = os.WEXITSTATUS(result[1]) + + # Check if child process exited happily. + if (status == 0): + inf.close() + return + else: + errstr = inf.readline() + inf.close() + raise plugin.SplatPluginError, "Post creation script %s %d %d %s exited abnormally: %s" % (postcreate, uid, gid, home, errstr) + + +def _copySkelDir(srcDir, destDir, uid, gid): + """ + Recursively copy a directory tree, preserving permission modes and + access times, but changing ownership of files to uid:gid. Also, + renames files/directories named dot.foo to .foo. + + @param srcDir: Skeletel dir to copy from. E.g. '/usr/share/skel' + @param destDir: Destionation home directory. + @param uid: Numeric UID of user whose home directory is destDir. + @param gid: Numeric GID of user whose home directory is destDir. + """ + # Regular expression matching files named dot.foo + pattern = re.compile('^dot\.') + for srcFile in os.listdir(srcDir): + destFile = pattern.sub('.', srcFile) + # Not portable: hardcoded / as path delimeter + srcPath = srcDir + '/' + srcFile + destPath = destDir + '/' + destFile + + # Go deeper if we are copying a sub directory + if (os.path.isdir(srcPath)): + try: + os.makedirs(destPath) + shutil.copystat(srcPath, destPath) + except OSError, e: + raise plugin.SplatPluginError, "Failed to create destination directory: %s" % destPath + continue + + _copySkelDir(srcPath, destPath, uid, gid) + + # Copy regular files + else: + try: + shutil.copy2(srcPath, destPath) + except IOError, e: + raise plugin.SplatPluginError, "Failed to copy %s to %s: %s" % (srcPath, destPath, e) + continue + + # Change ownership of files/directories after copied + try: + os.chown(destPath, uid, gid) + except OSError, e: + raise plugin.SplatPluginError, "Failed to change ownership of %s to %d:%d" % (destPath, uid, gid) + continue diff -Nru /tmp/b8LULaiWF2/splatd-1.1/splat/helpers/mailForwardingAddress.py /tmp/cDUfizKKfF/splatd-1.1.1/splat/helpers/mailForwardingAddress.py --- /tmp/b8LULaiWF2/splatd-1.1/splat/helpers/mailForwardingAddress.py 2006-08-23 20:19:02.000000000 -0700 +++ /tmp/cDUfizKKfF/splatd-1.1.1/splat/helpers/mailForwardingAddress.py 2007-01-31 15:48:24.000000000 -0800 @@ -39,7 +39,7 @@ import splat from splat import plugin -import homeDirectory +import homeutils logger = logging.getLogger(splat.LOG_NAME) @@ -49,31 +49,48 @@ HELPER_ERR_PRIVSEP = 2 HELPER_ERR_WRITE = 3 -class WriterContext(homeDirectory.WriterContext): +class WriterContext(object): def __init__(self): - homeDirectory.WriterContext.__init__(self) + self.home = None + self.minuid = None + self.mingid = None + self.skeldir = None + self.postcreate = None self.makehome = False -class Writer(homeDirectory.Writer): +class Writer(plugin.Helper): # Required Attributes def attributes(self): - return ('mailForwardingAddress',) + homeDirectory.Writer.attributes(self) + return ('mailForwardingAddress',) + homeutils.requiredAttributes() def parseOptions(self, options): context = WriterContext() - # Get makehome option, if was given - for key in options.keys(): + for key in options.iterkeys(): + if (key == 'home'): + context.home = str(options[key]) + if (context.home[0] != '/'): + raise plugin.SplatPluginError, "Relative paths for the home option are not permitted" + continue + if (key == 'minuid'): + context.minuid = int(options[key]) + continue + if (key == 'mingid'): + context.mingid = int(options[key]) + continue + if (key == 'skeldir'): + context.skeldir = os.path.abspath(options[key]) + # Validate skel directory + if (not os.path.isdir(context.skeldir)): + raise plugin.SplatPluginError, "Skeletal home directory %s does not exist or is not a directory" % context.skeldir + continue + if (key == 'postcreate'): + context.postcreate = os.path.abspath(options[key]) + continue if (key == 'makehome'): context.makehome = self._parseBooleanOption(str(options[key])) - # Superclass parseOptions() method won't like this option - del options[key] continue - - # Add options superclass is concerned with to context. - superContext = vars(homeDirectory.Writer.parseOptions(self, options)) - for opt in superContext.keys(): - setattr(context, opt, superContext[opt]) + raise plugin.SplatPluginError, "Invalid option '%s' specified." % key return context @@ -85,14 +102,14 @@ # Get LDAP attributes, and make sure we have all the ones we need attributes = ldapEntry.attributes if (not attributes.has_key('mailForwardingAddress')): - raise plugin.SplatPluginError, "Required attribute mailForwardingAddress not specified." + raise plugin.SplatPluginError, "Required attribute mailForwardingAddress not found for dn %s." % ldapEntry.dn addresses = attributes.get("mailForwardingAddress") - (home, uid, gid) = self.getAttributes(context, ldapEntry) + (home, uid, gid) = homeutils.getLDAPAttributes(ldapEntry, context.home, context.minuid, context.mingid) - # Make sure the home directory exists, and make it if config says to + # If config says to create the home directory and it doesn't exist, do so. if (not os.path.isdir(home)): if (context.makehome == True): - homeDirectory.Writer.work(self, context, ldapEntry, modified) + homeutils.makeHomeDirectory(home, uid, gid, context.skeldir, context.postcreate) else: # If we weren't told to make homedir, log a warning and quit logger.warning(".forward file not being written because home directory %s does not exist. To have this home directory created automatically by this plugin, set the makehome option to true in your splat configuration file, or use the homeDirectory plugin." % home) @@ -114,7 +131,7 @@ # This will only occur on the very first daemon iteration, # where modified is always 'True' if (entryTime < fileTime): - logger.info("Skipping %s, up-to-date" % filename) + logger.debug("Skipping %s, up-to-date" % filename) return except OSError: diff -Nru /tmp/b8LULaiWF2/splatd-1.1/splat/helpers/purgeUser.py /tmp/cDUfizKKfF/splatd-1.1.1/splat/helpers/purgeUser.py --- /tmp/b8LULaiWF2/splatd-1.1/splat/helpers/purgeUser.py 2006-09-13 21:09:05.000000000 -0700 +++ /tmp/cDUfizKKfF/splatd-1.1.1/splat/helpers/purgeUser.py 2007-01-31 15:48:24.000000000 -0800 @@ -37,7 +37,7 @@ import tarfile import time import errno -import homeHelper +import homeutils import splat from splat import plugin @@ -48,27 +48,36 @@ PURGE_ERR_PRIVSEP = 1 PURGE_ERR_RM = 2 -class WriterContext(homeHelper.WriterContext): +class WriterContext(object): def __init__(self): - homeHelper.WriterContext.__init__(self) + self.home = None + self.minuid = None + self.mingid = None self.archiveHomeDir = True self.purgeHomeDir = True self.purgeHomeArchive = True self.archiveDest = '/home' self.purgeArchiveWait = 14 -class Writer(homeHelper.Writer): +class Writer(plugin.Helper): def attributes(self): - return ('pendingPurge', 'uid') + homeHelper.Writer.attributes(self) + return ('pendingPurge', 'uid') + homeutils.requiredAttributes() def parseOptions(self, options): context = WriterContext() - # Add options superclass is concerned with to context. - superContext = vars(homeHelper.Writer.parseOptions(self, options)) - for opt in superContext.keys(): - setattr(context, opt, superContext[opt]) - for key in options.keys(): + for key in options.iterkeys(): + if (key == 'home'): + context.home = str(options[key]) + if (context.home[0] != '/'): + raise plugin.SplatPluginError, "Relative paths for the home option are not permitted" + continue + if (key == 'minuid'): + context.minuid = int(options[key]) + continue + if (key == 'mingid'): + context.mingid = int(options[key]) + continue if (key == 'archivehomedir'): context.archiveHomeDir = self._parseBooleanOption(str(options[key])) continue @@ -203,12 +212,12 @@ # Get all needed LDAP attributes, and verify we have what we need attributes = ldapEntry.attributes if (not attributes.has_key('pendingPurge')): - raise plugin.SplatPluginError, "Required attribute pendingPurge not found in LDAP entry." + raise plugin.SplatPluginError, "Required attribute pendingPurge not found for dn %s." % ldapEntry.dn if (not attributes.has_key('uid')): - raise plugin.SplatPluginError, "Required attribute uid not found in LDAP entry." + raise plugin.SplatPluginError, "Required attribute uid not found for dn %s." % ldapEntry.dn pendingPurge = attributes.get('pendingPurge')[0] username = attributes.get('uid')[0] - (home, uidNumber, gidNumber) = self.getAttributes(context, ldapEntry) + (home, uidNumber, gidNumber) = homeutils.getLDAPAttributes(ldapEntry, context.home, context.minuid, context.mingid) # Get current time (in GMT). now = int(time.strftime('%Y%m%d%H%M%S', time.gmtime(time.time()))) diff -Nru /tmp/b8LULaiWF2/splatd-1.1/splat/helpers/sshPublicKeys.py /tmp/cDUfizKKfF/splatd-1.1.1/splat/helpers/sshPublicKeys.py --- /tmp/b8LULaiWF2/splatd-1.1/splat/helpers/sshPublicKeys.py 2006-08-23 20:19:02.000000000 -0700 +++ /tmp/cDUfizKKfF/splatd-1.1.1/splat/helpers/sshPublicKeys.py 2007-01-31 15:48:24.000000000 -0800 @@ -37,8 +37,7 @@ import splat from splat import plugin - -import homeDirectory +import homeutils logger = logging.getLogger(splat.LOG_NAME) @@ -47,37 +46,55 @@ SSH_ERR_MISC = 1 SSH_ERR_PRIVSEP = 2 SSH_ERR_WRITE = 3 -class WriterContext(homeDirectory.WriterContext): +class WriterContext(object): def __init__(self): - homeDirectory.WriterContext.__init__(self) - self.command = None + self.home = None + self.minuid = None + self.mingid = None + self.skeldir = None + self.postcreate = None self.makehome = False + self.command = None -class Writer(homeDirectory.Writer): +class Writer(plugin.Helper): # Required Attributes def attributes(self): - return ('sshPublicKey',) + homeDirectory.Writer.attributes(self) + return ('sshPublicKey',) + homeutils.requiredAttributes() def parseOptions(self, options): context = WriterContext() # Get command and makehome options, if they were given - for key in options.keys(): + for key in options.iterkeys(): + if (key == 'home'): + context.home = str(options[key]) + if (context.home[0] != '/'): + raise plugin.SplatPluginError, "Relative paths for the home option are not permitted" + continue + if (key == 'minuid'): + context.minuid = int(options[key]) + continue + if (key == 'mingid'): + context.mingid = int(options[key]) + continue + if (key == 'skeldir'): + context.skeldir = os.path.abspath(options[key]) + # Validate skel directory + if (not os.path.isdir(context.skeldir)): + raise plugin.SplatPluginError, "Skeletal home directory %s does not exist or is not a directory" % context.skeldir + continue + if (key == 'postcreate'): + context.postcreate = os.path.abspath(options[key]) + continue if (key == 'command'): context.command = options[key] # Superclass parseOptions() method won't like this option - del options[key] continue if (key == 'makehome'): context.makehome = self._parseBooleanOption(str(options[key])) - del options[key] continue - - # Add options superclass is concerned with to context. - superContext = vars(homeDirectory.Writer.parseOptions(self, options)) - for opt in superContext.keys(): - setattr(context, opt, superContext[opt]) - + raise plugin.SplatPluginError, "Invalid option '%s' specified." % key + return context def work(self, context, ldapEntry, modified): @@ -88,20 +105,19 @@ # Get all needed LDAP attributes, and verify we have what we need attributes = ldapEntry.attributes if (not attributes.has_key('sshPublicKey')): - raise plugin.SplatPluginError, "Required attribute sshPublicKey not specified." + raise plugin.SplatPluginError, "Required attribute sshPublicKey not found for dn %s." % ldapEntry.dn keys = attributes.get("sshPublicKey") - (home, uid, gid) = self.getAttributes(context, ldapEntry) + (home, uid, gid) = homeutils.getLDAPAttributes(ldapEntry, context.home, context.minuid, context.mingid) # Make sure the home directory exists, and make it if config says to if (not os.path.isdir(home)): if (context.makehome == True): - homeDirectory.Writer.work(self, context, ldapEntry, modified) + homeutils.makeHomeDir(home, uid, gid, context.skeldir, context.postcreate) else: # If we weren't told to make homedir, log a warning and quit logger.warning("SSH keys not being written because home directory %s does not exist. To have this home directory created automatically by this plugin, set the makehome option to true in your splat configuration file, or use the homeDirectory plugin." % home) return - sshdir = "%s/.ssh" % home tmpfilename = "%s/.ssh/authorized_keys.tmp" % home filename = "%s/.ssh/authorized_keys" % home @@ -119,7 +135,7 @@ # This will only occur on the very first daemon iteration, # where modified is always 'True' if (entryTime < keyTime): - logger.info("Skipping %s, up-to-date" % filename) + logger.debug("Skipping %s, up-to-date" % filename) return except OSError: diff -Nru /tmp/b8LULaiWF2/splatd-1.1/splat/helpers/test/test_homeDirectory.py /tmp/cDUfizKKfF/splatd-1.1.1/splat/helpers/test/test_homeDirectory.py --- /tmp/b8LULaiWF2/splatd-1.1/splat/helpers/test/test_homeDirectory.py 2006-09-20 13:09:48.000000000 -0700 +++ /tmp/cDUfizKKfF/splatd-1.1.1/splat/helpers/test/test_homeDirectory.py 2007-01-31 16:43:13.000000000 -0800 @@ -52,84 +52,92 @@ # Test Cases class HomeDirtestCase(unittest.TestCase): + """ Test Splat Home Directory Helper """ + + # Return a valid options dictionary to parse. Note that these options are + # not necessarily the same as the defaults specified in the various plugin + # WriterContext classes. def _getDefaultOptions(self): # Ubuntu (and probably Debian and other linuxes) use /etc/skel instead # of /usr/share skel. - defSkel = '/usr/share/skel' - if (not os.path.isdir(defSkel)): + skelDir = '/usr/share/skel' + if (not os.path.isdir(skelDir)): if (os.path.isdir('/etc/skel')): - defSkel = '/etc/skel' + skelDir = '/etc/skel' else: - self.fail('Can not find a useable default skeletal directory') + self.fail('Can not find a useable skeletal directory') return { - 'home':'/home', 'minuid':'0', 'mingid':'0', - 'skeldir':defSkel + 'skeldir':skelDir } - - """ Test Splat Home Directory Helper """ def setUp(self): self.slapd = slapd.LDAPServer() self.conn = ldapclient.Connection(slapd.SLAPD_URI) - self.hc = plugin.HelperController('test', 'splat.helpers.homeDirectory', 5, 'dc=example,dc=com', '(uid=john)', False, self._getDefaultOptions()) + self.options = self._getDefaultOptions() + self.hc = plugin.HelperController('test', 'splat.helpers.homeDirectory', 5, 'dc=example,dc=com', '(objectClass=sshAccount)', False, self.options) self.entries = self.conn.search(self.hc.searchBase, ldap.SCOPE_SUBTREE, self.hc.searchFilter, self.hc.searchAttr) def tearDown(self): self.slapd.stop() - def test_option_parser(self): - """ Test Options Parser """ + def test_valid_options(self): + """ Test Parsing of Valid Options """ + options = self.options + assert self.hc.helper.parseOptions(options) + # Also make sure parser works when skeldir has not been defined + del options['skeldir'] + assert self.hc.helper.parseOptions(options) + + def test_invalid_options(self): + """ Test Invalid Options """ # foo is not a valid option - options = self._getDefaultOptions() + options = self.options options['foo'] = 'bar' self.assertRaises(splat.SplatError, self.hc.helper.parseOptions, options) - # Make sure the parser works when all options are valid - assert self.hc.helper.parseOptions(self._getDefaultOptions()) def test_option_parse_home(self): """ Test Home Option Parser """ # Relative paths shouldn't be allowed for home - options = self._getDefaultOptions() + options = self.options options['home'] = 'home' self.assertRaises(splat.SplatError, self.hc.helper.parseOptions, options) def test_option_parse_skeldir(self): """ Test Skel Directory Option Parser """ # Paths that don't exist should generate an exception - options = self._getDefaultOptions() + options = self.options options['skeldir'] = '/asdf/jklh/qwer' self.assertRaises(splat.SplatError, self.hc.helper.parseOptions, options) - def test_validation_home(self): - """ Test Home Validation """ - options = self._getDefaultOptions() - options['home'] = '/etc' - self.context = self.hc.helper.parseOptions(options) - self.assertRaises(splat.SplatError, self.hc.helper.getAttributes, self.context, self.entries[0]) - - def test_validation_uid(self): - """ Test UID Validation """ - options = self._getDefaultOptions() - options['minuid'] = '9000000' - self.context = self.hc.helper.parseOptions(options) - self.assertRaises(splat.SplatError, self.hc.helper.getAttributes, self.context, self.entries[0]) - - def test_validation_home(self): - """ Test GID Validation """ - options = self._getDefaultOptions() - options['mingid'] = '9000000' - self.context = self.hc.helper.parseOptions(options) - self.assertRaises(splat.SplatError, self.hc.helper.getAttributes, self.context, self.entries[0]) - - def test_attributes(self): - """ Test Attributes """ - options = self._getDefaultOptions() - self.context = self.hc.helper.parseOptions(options) - realAttrs = ('/home/john', 10001, 10001) - attrs = self.hc.helper.getAttributes(self.context, self.entries[0]) - self.failUnlessEqual(realAttrs, attrs) - + def test_context(self): + """ Test Context Consistency With Options """ + context = self.hc.helper.parseOptions(self.options) + self.assertEquals(context.home, '/home') + self.assertEquals(context.minuid, 0) + self.assertEquals(context.mingid, 0) + + def test_group_context(self): + """ Test Group Context Consistency With Service Options """ + filter = ldapclient.GroupFilter(slapd.BASEDN, ldap.SCOPE_SUBTREE, '(&(objectClass=groupOfUniqueNames)(cn=developers))', 'uniqueMember') + self.hc.addGroup(filter) + self.assertEquals(self.hc.groupsCtx[filter].home, '/home') + self.assertEquals(self.hc.groupsCtx[filter].minuid, 0) + self.assertEquals(self.hc.groupsCtx[filter].mingid, 0) + + def test_group_context_custom(self): + """ Test Group Context Consistency With Group Specific Options """ + options = self.options + # Run parseOptions here to make sure the options dictionary is not + # being modified by it. + self.hc.helper.parseOptions(options) + # Now update with a custom option for this group. + options['minuid'] = '10' + filter = ldapclient.GroupFilter(slapd.BASEDN, ldap.SCOPE_SUBTREE, '(&(objectClass=groupOfUniqueNames)(cn=developers))', 'uniqueMember') + self.hc.addGroup(filter, options) + self.assertEquals(self.hc.groupsCtx[filter].minuid, 10) + self.assertEquals(self.hc.groupsCtx[filter].home, '/home') + self.assertEquals(self.hc.groupsCtx[filter].mingid, 0) diff -Nru /tmp/b8LULaiWF2/splatd-1.1/splat/helpers/test/test_homeutils.py /tmp/cDUfizKKfF/splatd-1.1.1/splat/helpers/test/test_homeutils.py --- /tmp/b8LULaiWF2/splatd-1.1/splat/helpers/test/test_homeutils.py 1969-12-31 16:00:00.000000000 -0800 +++ /tmp/cDUfizKKfF/splatd-1.1.1/splat/helpers/test/test_homeutils.py 2007-01-31 15:48:24.000000000 -0800 @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# test_homeutils.py vi:ts=4:sw=4:expandtab: +# +# Scalable Periodic LDAP Attribute Transmogrifier +# Author: +# Nick Barkas +# +# Copyright (c) 2007 Three Rings Design, Inc. +# 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 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. +# 3. Neither the name of the copyright owner nor the names of contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# 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. + +""" LDAP Unit Tests """ + +from twisted.trial import unittest + +import ldap +import os + +import splat +#from splat import plugin +from splat.ldaputils.test import slapd +from splat.ldaputils import client as ldapclient +from splat.helpers import homeutils + +# Useful Constants +from splat.test import DATA_DIR + +# Test Cases +class HomeUtilstestCase(unittest.TestCase): + """ Test Splat Home Directory Library """ + + def setUp(self): + self.slapd = slapd.LDAPServer() + self.conn = ldapclient.Connection(slapd.SLAPD_URI) + self.entry = self.conn.search(slapd.BASEDN, ldap.SCOPE_SUBTREE, '(uid=john)')[0] + + def tearDown(self): + self.slapd.stop() + + def test_valid_attributes(self): + """ Test getLDAPAttributes() for Valid Entry """ + (home, uid, gid) = homeutils.getLDAPAttributes(self.entry, '/home', 10000, 10000) + self.assertEquals(('/home/john', 10001, 10001), (home, uid, gid)) + + def test_invalid_uid(self): + """ Test getLDAPAttributes() for Entry with UID Lower than Minimum """ + self.assertRaises(splat.SplatError, homeutils.getLDAPAttributes, self.entry, '/home', 20000, 10000) + + def test_invalid_gid(self): + """ Test getLDAPAttributes() for Entry with GID Lower than Minimum """ + self.assertRaises(splat.SplatError, homeutils.getLDAPAttributes, self.entry, '/home', 10000, 20000) + + def test_invalid_home(self): + """ Test getLDAPAttributes() for Entry with Invalid Home Directory """ + self.assertRaises(splat.SplatError, homeutils.getLDAPAttributes, self.entry, '/tmp', 10000, 10000) diff -Nru /tmp/b8LULaiWF2/splatd-1.1/splat/helpers/test/test_mailForwardingAddress.py /tmp/cDUfizKKfF/splatd-1.1.1/splat/helpers/test/test_mailForwardingAddress.py --- /tmp/b8LULaiWF2/splatd-1.1/splat/helpers/test/test_mailForwardingAddress.py 2006-09-20 13:09:48.000000000 -0700 +++ /tmp/cDUfizKKfF/splatd-1.1.1/splat/helpers/test/test_mailForwardingAddress.py 2007-01-31 16:43:13.000000000 -0800 @@ -50,19 +50,16 @@ # Test Cases class MailForwardingAddresstestCase(unittest.TestCase): """ Test Splat Mail Forwarding Helper """ - def setUp(self): + self.options = { + 'home':'/home', + 'minuid':'0', + 'mingid':'0', + 'makehome':'true' + } self.slapd = slapd.LDAPServer() self.conn = ldapclient.Connection(slapd.SLAPD_URI) - - # Benign options - options = { - 'home':'/home', - 'minuid':'0', - 'mingid':'0' - } - - self.hc = plugin.HelperController('test', 'splat.helpers.mailForwardingAddress', 5, 'dc=example,dc=com', '(uid=john)', False, options) + self.hc = plugin.HelperController('test', 'splat.helpers.mailForwardingAddress', 5, 'dc=example,dc=com', '(objectClass=sshAccount)', False, self.options) self.entries = self.conn.search(self.hc.searchBase, ldap.SCOPE_SUBTREE, self.hc.searchFilter, self.hc.searchAttr) # We test that checking the modification timestamp on entries works in # plugin.py's test class, so just assume the entry is modified here. @@ -71,23 +68,20 @@ def tearDown(self): self.slapd.stop() - def test_option_parser(self): - """ Test Option Parser """ - options = { - 'home':'/home', - 'minuid':'0', - 'mingid':'0', - 'foo':'bar' - } + def test_valid_options(self): + """ Test Parsing of Valid Options """ + assert self.hc.helper.parseOptions(self.options) + + def test_invalid_option(self): + """ Test Invalid Option """ + options = self.options + options['foo'] = 'bar' self.assertRaises(splat.SplatError, self.hc.helper.parseOptions, options) - # Make sure the parser works when all options are valid - del options['foo'] - assert self.hc.helper.parseOptions(options) def test_option_home(self): """ Test Home Directory Validation """ options = { - 'home':'/fred', + 'home':'/fred' } self.context = self.hc.helper.parseOptions(options) self.assertRaises(splat.SplatError, self.hc.helper.work, self.context, self.entries[0], self.modified) @@ -107,3 +101,29 @@ } self.context = self.hc.helper.parseOptions(options) self.assertRaises(splat.SplatError, self.hc.helper.work, self.context, self.entries[0], self.modified) + + def test_context(self): + """ Test Context Consistency With Options """ + context = self.hc.helper.parseOptions(self.options) + self.assertEquals(context.makehome, True) + + def test_group_context(self): + """ Test Group Context Consistency With Service Options """ + filter = ldapclient.GroupFilter(slapd.BASEDN, ldap.SCOPE_SUBTREE, '(&(objectClass=groupOfUniqueNames)(cn=developers))', 'uniqueMember') + self.hc.addGroup(filter) + self.assertEquals(self.hc.groupsCtx[filter].makehome, True) + self.assertEquals(self.hc.groupsCtx[filter].minuid, 0) + + def test_group_context_custom(self): + """ Test Group Context Consistency With Group Specific Options """ + options = self.options + # Run parseOptions here to make sure the options dictionary is not + # being modified by it. + self.hc.helper.parseOptions(options) + # Now update with a custom option for this group. + options['minuid'] = '10' + filter = ldapclient.GroupFilter(slapd.BASEDN, ldap.SCOPE_SUBTREE, '(&(objectClass=groupOfUniqueNames)(cn=developers))', 'uniqueMember') + self.hc.addGroup(filter, options) + self.assertEquals(self.hc.groupsCtx[filter].minuid, 10) + self.assertEquals(self.hc.groupsCtx[filter].mingid, 0) + self.assertEquals(self.hc.groupsCtx[filter].makehome, True) diff -Nru /tmp/b8LULaiWF2/splatd-1.1/splat/helpers/test/test_purgeUser.py /tmp/cDUfizKKfF/splatd-1.1.1/splat/helpers/test/test_purgeUser.py --- /tmp/b8LULaiWF2/splatd-1.1/splat/helpers/test/test_purgeUser.py 2006-09-20 13:09:48.000000000 -0700 +++ /tmp/cDUfizKKfF/splatd-1.1.1/splat/helpers/test/test_purgeUser.py 2007-01-29 19:14:30.000000000 -0800 @@ -58,19 +58,23 @@ self.conn.modify(mod) return yesterday + # Return a valid options dictionary to parse. Note that these options are + # not necessarily the same as the defaults specified in the various plugin + # WriterContext classes. + def _getDefaultOptions(self): + return { + 'archivehomedir':'false', + 'purgehomedir':'false', + 'purgehomearchive':'false', + 'archivedest':'/tmp', + 'purgearchivewait':'5' + } + def setUp(self): self.slapd = slapd.LDAPServer() self.conn = ldapclient.Connection(slapd.SLAPD_URI) - # Benign options - options = { - 'archivehomedir':'true', - 'purgehomedir':'true', - 'purgehomearchive':'true', - 'archivedest':'/' - } - - self.hc = plugin.HelperController('test', 'splat.helpers.purgeUser', 5, 'dc=example,dc=com', '(uid=chris)', False, options) + self.hc = plugin.HelperController('test', 'splat.helpers.purgeUser', 5, 'dc=example,dc=com', '(objectClass=purgeableAccount)', False, self._getDefaultOptions()) self.entries = self.conn.search(self.hc.searchBase, ldap.SCOPE_SUBTREE, self.hc.searchFilter, self.hc.searchAttr) # We test that checking the modification timestamp on entries works in # plugin.py's test class, so just assume the entry is modified here. @@ -80,31 +84,20 @@ self.slapd.stop() def test_pendingPurge(self): - # Make sure pendingPurge attribute gets set right for test user + """ Test pendingPurge Attribute """ yesterday = self._setPendingPurge('uid=chris,ou=People,dc=example,dc=com') results = self.conn.search('dc=example,dc=com', ldap.SCOPE_SUBTREE, '(uid=chris)', None) self.assertEqual(yesterday, results[0].attributes['pendingPurge'][0]) - def test_parseOptions(self): - """ Test parseOptions() """ - options = { - 'archivehomedir':'false', - 'purgehomedir':'false', - 'purgehomearchive':'false', - 'archivedest':'/tmp', - 'purgearchivewait':'5', - 'foo':'bar' - } + def test_invalid_options(self): + """ Test Invalid Options """ + options = self._getDefaultOptions() + options['foo'] = 'bar' # First test if the parser complains about invalid options. self.assertRaises(splat.SplatError, self.hc.helper.parseOptions, options) # Then make sure it works when the options are valid. del options['foo'] - context = self.hc.helper.parseOptions(options) - self.assertEqual(False, context.archiveHomeDir) - self.assertEqual(False, context.purgeHomeDir) - self.assertEqual(False, context.purgeHomeArchive) - self.assertEqual('/tmp', context.archiveDest) - self.assertEqual(5, context.purgeArchiveWait) + assert self.hc.helper.parseOptions(options) def test_default_options(self): """ Test Default Options """ @@ -115,8 +108,17 @@ self.assertEqual('/home', context.archiveDest) self.assertEqual(14, context.purgeArchiveWait) - def test_invalid_options(self): - """ Test Error Handling of Invalid Options """ + def test_bad_option_values(self): + """ Test Validation of Bad Option Values """ self.assertRaises(splat.SplatError, self.hc.helper.parseOptions, {'archivedest':'/asdf'}) self.assertRaises(splat.SplatError, self.hc.helper.parseOptions, {'purgehomedir':'42'}) self.assertRaises(splat.SplatError, self.hc.helper.parseOptions, {'archivehomedir':'false', 'purgehomearchive':'true'}) + + def test_context(self): + """ Test Context Consistency With Options """ + context = self.hc.helper.parseOptions(self._getDefaultOptions()) + self.assertEqual(False, context.archiveHomeDir) + self.assertEqual(False, context.purgeHomeDir) + self.assertEqual(False, context.purgeHomeArchive) + self.assertEqual('/tmp', context.archiveDest) + self.assertEqual(5, context.purgeArchiveWait) diff -Nru /tmp/b8LULaiWF2/splatd-1.1/splat/helpers/test/test_sshPublicKeys.py /tmp/cDUfizKKfF/splatd-1.1.1/splat/helpers/test/test_sshPublicKeys.py --- /tmp/b8LULaiWF2/splatd-1.1/splat/helpers/test/test_sshPublicKeys.py 2006-09-20 13:09:48.000000000 -0700 +++ /tmp/cDUfizKKfF/splatd-1.1.1/splat/helpers/test/test_sshPublicKeys.py 2007-01-31 16:43:13.000000000 -0800 @@ -50,20 +50,17 @@ # Test Cases class SSHPublicKeystestCase(unittest.TestCase): """ Test Splat SSH Helper """ - def setUp(self): - self.slapd = slapd.LDAPServer() - self.conn = ldapclient.Connection(slapd.SLAPD_URI) - - # Benign options - options = { + self.options = { 'home':'/home', 'minuid':'0', 'mingid':'0', - 'command':'/bin/sh' + 'command':'/bin/sh', + 'makehome':'true' } - - self.hc = plugin.HelperController('test', 'splat.helpers.sshPublicKeys', 5, 'dc=example,dc=com', '(uid=john)', False, options) + self.slapd = slapd.LDAPServer() + self.conn = ldapclient.Connection(slapd.SLAPD_URI) + self.hc = plugin.HelperController('test', 'splat.helpers.sshPublicKeys', 5, 'dc=example,dc=com', '(objectClass=sshAccount)', False, self.options) self.entries = self.conn.search(self.hc.searchBase, ldap.SCOPE_SUBTREE, self.hc.searchFilter, self.hc.searchAttr) # We test that checking the modification timestamp on entries works in # plugin.py's test class, so just assume the entry is modified here. @@ -72,19 +69,15 @@ def tearDown(self): self.slapd.stop() - def test_option_parser(self): - """ Test Option Parser """ - options = { - 'home':'/home', - 'minuid':'0', - 'mingid':'0', - 'command':'/bin/sh', - 'foo':'bar' - } + def test_valid_options(self): + """ Test Parsing of Valid Options """ + assert self.hc.helper.parseOptions(self.options) + + def test_invalid_option(self): + """ Test Invalid Option """ + options = self.options + options['foo'] = 'bar' self.assertRaises(splat.SplatError, self.hc.helper.parseOptions, options) - # Make sure the parser works when all options are valid - del options['foo'] - assert self.hc.helper.parseOptions(options) def test_option_home(self): """ Test Home Directory Validation """ @@ -109,3 +102,30 @@ } self.context = self.hc.helper.parseOptions(options) self.assertRaises(splat.SplatError, self.hc.helper.work, self.context, self.entries[0], self.modified) + + def test_context(self): + """ Test Context Consistency With Options """ + context = self.hc.helper.parseOptions(self.options) + self.assertEquals(context.makehome, True) + self.assertEquals(context.command, '/bin/sh') + + def test_group_context(self): + """ Test Group Context Consistency With Service Options """ + filter = ldapclient.GroupFilter(slapd.BASEDN, ldap.SCOPE_SUBTREE, '(&(objectClass=groupOfUniqueNames)(cn=developers))', 'uniqueMember') + self.hc.addGroup(filter) + self.assertEquals(self.hc.groupsCtx[filter].makehome, True) + self.assertEquals(self.hc.groupsCtx[filter].command, '/bin/sh') + + def test_group_context_custom(self): + """ Test Group Context Consistency With Group Specific Options """ + options = self.options + # Run parseOptions here to make sure the options dictionary is not + # being modified by it. + self.hc.helper.parseOptions(options) + # Now update with a custom option for this group. + options['command'] = '/bin/csh' + filter = ldapclient.GroupFilter(slapd.BASEDN, ldap.SCOPE_SUBTREE, '(&(objectClass=groupOfUniqueNames)(cn=developers))', 'uniqueMember') + self.hc.addGroup(filter, options) + self.assertEquals(self.hc.groupsCtx[filter].command, '/bin/csh') + self.assertEquals(self.hc.groupsCtx[filter].makehome, True) + self.assertEquals(self.hc.groupsCtx[filter].minuid, 0) diff -Nru /tmp/b8LULaiWF2/splatd-1.1/splat/ldaputils/client.py /tmp/cDUfizKKfF/splatd-1.1.1/splat/ldaputils/client.py --- /tmp/b8LULaiWF2/splatd-1.1/splat/ldaputils/client.py 2006-09-18 11:20:50.000000000 -0700 +++ /tmp/cDUfizKKfF/splatd-1.1.1/splat/ldaputils/client.py 2006-12-08 18:44:05.000000000 -0800 @@ -35,6 +35,9 @@ import ldap, ldap.modlist, ldap.sasl import time +class LDAPUtilsClientError(Exception): + pass + class Connection(object): """ Simple wrapper around an LDAP connection @@ -53,6 +56,14 @@ @param bind_dn: Bind DN @param password: Bind Password """ + # If an empty password is given with a valid DN, some LDAP server + # implementations (OpenLDAP) will return an error. Others (Active + # Directory, Novell) will treat as an anonymous bind and return + # success. We want DNs with no password to not work always, so catch + # that here. + if (password == '' and bind_dn != ''): + raise LDAPUtilsClientError, "Invalid bind: DN with no password" + self._ldap.simple_bind_s(bind_dn, password) def gssapi_bind(self, authz_id=''): diff -Nru /tmp/b8LULaiWF2/splatd-1.1/splat/ldaputils/test/test_client.py /tmp/cDUfizKKfF/splatd-1.1.1/splat/ldaputils/test/test_client.py --- /tmp/b8LULaiWF2/splatd-1.1/splat/ldaputils/test/test_client.py 2006-09-18 11:20:50.000000000 -0700 +++ /tmp/cDUfizKKfF/splatd-1.1.1/splat/ldaputils/test/test_client.py 2006-12-08 18:44:05.000000000 -0800 @@ -56,6 +56,13 @@ def test_initialize(self): self.assertEquals(self.conn._ldap.protocol_version, ldap.VERSION3) + def test_anonymous_simple_bind(self): + self.conn.simple_bind('', '') + + def test_invalid_simple_bind(self): + # Make sure binding with a valid DN and empty password fails. + self.assertRaises(ldapclient.LDAPUtilsClientError, self.conn.simple_bind, slapd.ROOTDN, '') + def test_simple_bind(self): self.conn.simple_bind(slapd.ROOTDN, slapd.ROOTPW) diff -Nru /tmp/b8LULaiWF2/splatd-1.1/splat.conf /tmp/cDUfizKKfF/splatd-1.1.1/splat.conf --- /tmp/b8LULaiWF2/splatd-1.1/splat.conf 2006-08-31 19:02:31.000000000 -0700 +++ /tmp/cDUfizKKfF/splatd-1.1.1/splat.conf 2006-11-09 12:01:04.000000000 -0800 @@ -131,7 +131,8 @@ Value 1000