savannah-cvs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Savannah-cvs] [SCM] Savane-cleanup framework branch, master, updated. 7


From: Sylvain Beucler
Subject: [Savannah-cvs] [SCM] Savane-cleanup framework branch, master, updated. 7552a50da01bd07ab62f229f407c399fbfbb1d67
Date: Sun, 02 Aug 2009 15:03:47 +0000

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Savane-cleanup framework".

The branch, master has been updated
       via  7552a50da01bd07ab62f229f407c399fbfbb1d67 (commit)
      from  84cb87f6346ac1cf7b54f48ce87e6313e8c35baa (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
http://git.savannah.gnu.org/cgit/savane-cleanup/framework.git/commit/?id=7552a50da01bd07ab62f229f407c399fbfbb1d67

commit 7552a50da01bd07ab62f229f407c399fbfbb1d67
Author: Sylvain Beucler <address@hidden>
Date:   Sun Aug 2 17:03:29 2009 +0200

    First draft of LDAP export / population

diff --git a/TODO b/TODO
index f40728a..30ca8ce 100644
--- a/TODO
+++ b/TODO
@@ -1,6 +1,14 @@
+- models
+
+  - add DB indexes (db_model=True - but is that possible for
+    auth_user.username? :/)
+
 - now we need the screens for users to modify them
+
 - work on the web design
+
 - export to LDAP and reimplement .ssh replication and VCS creation
+
 - implement mod_rewrite URL migration list
 
 
diff --git a/sbin/sv b/sbin/sv
new file mode 100755
index 0000000..0ddea03
--- /dev/null
+++ b/sbin/sv
@@ -0,0 +1,31 @@
+#!/usr/bin/python
+# Wrapper to locate and execute Savane backend commands
+# Copyright (C) 2009  Sylvain Beucler
+#
+# This file is part of Savane.
+# 
+# Savane is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+# 
+# Savane is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+# 
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# (Inspiration from django.core.management.execute_manager)
+
+# Prepare environment to locate settings.py and models.py
+import os, sys
+if not os.environ.has_key('DJANGO_SETTINGS_MODULE'):
+    os.environ['DJANGO_SETTINGS_MODULE'] = 'settings';
+
+if os.path.exists('../src'): # debug
+    sys.path.insert(0, '../src')
+
+import savane.backend
+savane.backend.wrapper()
diff --git a/sbin/sv-populate-ldap b/sbin/sv-populate-ldap
new file mode 100755
index 0000000..82fd906
--- /dev/null
+++ b/sbin/sv-populate-ldap
@@ -0,0 +1,7 @@
+#!/bin/bash
+/etc/init.d/slapd stop
+rm -f /var/lib/ldap/*
+# '-q' disables integrity checks and is nearly 30x faster
+./sv auth_ldif_export | slapadd -q
+chown -R openldap: /var/lib/ldap/*
+/etc/init.d/slapd start
diff --git a/src/savane/backend/__init__.py b/src/savane/backend/__init__.py
new file mode 100644
index 0000000..2f4bab7
--- /dev/null
+++ b/src/savane/backend/__init__.py
@@ -0,0 +1,29 @@
+# Run a subcommand specified on the command line
+# Copyright (C) 2009  Sylvain Beucler
+#
+# This file is part of Savane.
+# 
+# Savane is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+# 
+# Savane is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+# 
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import os, sys
+import imp
+
+def wrapper():
+    """
+    Load python savane.backend submodule specified on the first
+    argument of the command line
+    """
+    command_name = sys.argv[1]
+    (f, path, descr) = imp.find_module(command_name, __path__)
+    imp.load_module(command_name, f, path, descr)
diff --git a/src/savane/backend/auth_ldif_export.py 
b/src/savane/backend/auth_ldif_export.py
new file mode 100644
index 0000000..2e4b759
--- /dev/null
+++ b/src/savane/backend/auth_ldif_export.py
@@ -0,0 +1,181 @@
+# Replicate users and groups to an OpenLDAP directory
+# Copyright (C) 2009  Sylvain Beucler
+#
+# This file is part of Savane.
+# 
+# Savane is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+# 
+# Savane is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+# 
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Recommended indexes:
+# index                uid,sn,uidNumber,gidNumber,memberUid,shadowExpire eq
+
+# TODO: most settings are hard-coded and need to be made configurable
+# - base: dc=savannah,dc=gnu,dc=org
+# - users ou: "users"
+# - groups ou: "groups"
+# - create 'organization' and 'organizationalUnit' objects?
+# - min uid: 1000
+# - min gid: 1000
+# - default group: cn=svusers / gid=1000
+
+import sys
+import codecs
+import base64, binascii
+import savane.svmain.models as svmain_models
+
+# Convert stdout to UTF-8 - if the stdout is redirected to a file
+# sys.stdout.encoding is autodetected as 'None' and you get the
+# obnoxious UnicodeEncodeError python error.
+sys.stdout = codecs.getwriter('UTF-8')(sys.stdout)
+
+print """dn: dc=savannah,dc=gnu,dc=org
+objectClass: top
+objectClass: dcObject
+objectClass: organization
+o: GNU
+dc: savannah
+structuralObjectClass: organization
+
+dn: ou=users,dc=savannah,dc=gnu,dc=org
+ou: users
+objectClass: organizationalUnit
+objectClass: top
+structuralObjectClass: organizationalUnit
+
+dn: ou=groups,dc=savannah,dc=gnu,dc=org
+ou: groups
+objectClass: organizationalUnit
+objectClass: top
+structuralObjectClass: organizationalUnit
+"""
+
+# Add user admin/admin
+# (REMOVE WHEN TESTING IS DONE!)
+print """
+dn: cn=admin,dc=savannah,dc=gnu,dc=org
+objectClass: simpleSecurityObject
+objectClass: organizationalRole
+cn: admin
+description: LDAP administrator
+userPassword:: e2NyeXB0fWt0YVZ1TFNDaEg0Wi4=
+structuralObjectClass: organizationalRole
+"""
+
+#count = svmain_models.ExtendedUser.objects.count()
+#print str(count) + " users in the database."
+
+uidNumber=1000
+for user in svmain_models.ExtendedUser.objects.only('username', 'first_name', 
'last_name', 'email',
+                                                    'password', 'uidNumber', 
'gidNumber'):
+    uidNumber=uidNumber+1
+    ##if uidNumber == 0: # either non-assigned, or mistakenly assigned to root
+    #if uidNumber < 1000: # either non-assigned, or mistakenly assigned to 
privileged user
+    #    uidn = UidNumber()
+    #    uidn.save()
+    #    user.uidNumber = uidn
+    #    user.save()
+
+    cleanup = [user.first_name, user.last_name, user.email]
+    for i in range(0, len(cleanup)):
+        cleanup[i] = cleanup[i].replace('\n', ' ')
+        cleanup[i] = cleanup[i].replace('\r', ' ')
+        cleanup[i] = cleanup[i].strip()
+    (first_name, last_name, email) = cleanup
+
+    ldap_password = '{CRYPT}!'  # default = unusable password
+    if user.password.startswith('sha1$'):
+        # Django-specific algorithm: it sums 5-char-salt+pass instead
+        # of SSHA's pass+4-bytes-salt, so we can't store it in LDAP -
+        # /me curses django devs
+        pass
+    elif user.password.startswith('md5$$'):
+        # MD5 without salt
+        algo, empty, hash_hex = user.password.split('$')
+        if (len(hash_hex) == 32): # filter out empty or disabled passwords
+            ldap_password = "{MD5}" + 
base64.b64encode(binascii.a2b_hex(hash_hex))
+    elif user.password.startswith('md5$'):
+        # md5$salt$ vs. {SMD5} is similar to sha1$salt$ vs. {SSHA} -
+        # cf. above
+        pass
+    elif user.password.startswith('crypt$'):
+        # glibc crypt has improved algorithms, but where salt contains
+        # three '$'s, which Django doesn't support (since '$' is
+        # already the salt field separator). So this is only weak,
+        # passwd-style (not shadow-style) crypt.
+        algo, salt_hex, hash_hex = user.password.split('$')
+        # salt_hex is 2-chars long and already prepended to hash_hex
+        ldap_password = "{CRYPT}" + 
base64.b64encode(binascii.a2b_hex(hash_hex))
+    elif '$' not in user.password:
+        # MD5 without salt, alternate Django syntax
+        hash_hex = user.password
+        if (len(hash_hex) == 32): # filter out empty or disabled passwords
+            ldap_password = "{MD5}" + 
base64.b64encode(binascii.a2b_hex(hash_hex))
+
+    # Object classes:
+    # - posixAccount: base class for libnss-ldap/pam-ldap support
+    # - shadowAccount: for shadowExpire
+    # - inetOrgPerson: for mail and givenName, and structural class
+    print u"""
+dn: uidNumber=%(uidNumber)d,ou=users,dc=savannah,dc=gnu,dc=org
+uid: %(username)s
+cn:: %(full_name)s
+sn:: %(last_name)s
+mail: %(email)s
+userPassword: %(ldap_password)s
+uidNumber: %(uidNumber)d
+gidNumber: %(gidNumber)d
+homeDirectory: %(homedir)s
+objectClass: shadowAccount
+objectClass: posixAccount
+objectClass: inetOrgPerson
+objectClass: top
+structuralObjectClass: inetOrgPerson""" % {
+        'username' : user.username,
+        'full_name' : base64.b64encode((first_name + u' ' + 
last_name).encode('UTF-8')),
+        'last_name' : base64.b64encode((last_name or u'-').encode('UTF-8')),
+        'email' : email,
+        'ldap_password' : ldap_password,
+        'uidNumber' : uidNumber,
+        'gidNumber' : 1000,
+        'homedir' : u'/home/' + user.username[:1] + u'/' + user.username[:2] + 
u'/' + user.username,
+        }
+    # non-mandatory fields - slapadd doesn't accept empty fields apparently
+    if len(first_name) > 0:
+        print "givenName::" + base64.b64encode(first_name.encode('UTF-8'))
+    # disallow login for users that are not part of any group
+    #if user.extendedgroup_set.count() == 0:
+    #    print "shadowExpire: 10" # timestamp - avoid 0 as it may be
+    #                             # interpreted at 'no expiration'
+
+print u"""
+dn: cn=svusers,ou=groups,dc=savannah,dc=gnu,dc=org
+cn: svusers
+gidNumber: 1000
+objectClass: posixGroup
+objectClass: top
+structuralObjectClass: posixGroup"""
+i=1000
+for group in svmain_models.ExtendedGroup.objects.only('name'):
+    i=i+1
+    print u"""
+dn: cn=%(name)s,ou=groups,dc=savannah,dc=gnu,dc=org
+cn: %(name)s
+gidNumber: %(gidNumber)s
+objectClass: posixGroup
+objectClass: top
+structuralObjectClass: posixGroup""" % {
+     'name' : group.name,
+     'gidNumber' : i,
+     }
+    for user in group.extendeduser_set.only('username'):
+        print "memberUid: " + user.username
diff --git a/src/savane/svmain/models.py b/src/savane/svmain/models.py
index 9a5c0f2..b2fd83b 100644
--- a/src/savane/svmain/models.py
+++ b/src/savane/svmain/models.py
@@ -58,10 +58,11 @@ we don't use it.
 from django.db import models
 from django.contrib.auth import models as auth_models
 
+
 class ExtendedUser(auth_models.User):
     """Django base User class + extra Savane fields"""
 
-    # Migrated to 'firstname' in auth.User
+    # Migrated to 'first_name' and 'last_name' in auth.User
     #realname = models.CharField(max_length=96)
 
     # Old Savane can be Active/Deleted/Pending/Suspended/SQuaD
@@ -74,6 +75,10 @@ class ExtendedUser(auth_models.User):
         )
     status = models.CharField(max_length=3, choices=status_CHOICES)
 
+    # Unix mapping, used when populating a LDAP directory
+    uidNumber = models.IntegerField(default=0)
+    gidNumber = models.IntegerField(default=0)
+
     # Used by trackers only but it could be used more widely
     spamscore = models.IntegerField(null=True, blank=True)
     # Previously used for e-mail changes and password recovery, Django
@@ -355,6 +360,8 @@ class ExtendedGroup(auth_models.Group):
         ('I', 'Incomplete (failure during registration)'),
         )
     status = models.CharField(max_length=1, choices=status_CHOICES, 
default='A')
+    gidNumber = models.IntegerField(default=0)
+
     short_description = models.CharField(max_length=255, blank=True)
     long_description = models.TextField(blank=True)
     license = models.ForeignKey(License, blank=True, null=True)

-----------------------------------------------------------------------

Summary of changes:
 TODO                                               |    8 +
 src/savane/context_processors.py => sbin/sv        |   21 ++-
 sbin/sv-populate-ldap                              |    7 +
 .../{context_processors.py => backend/__init__.py} |   17 +-
 src/savane/backend/auth_ldif_export.py             |  181 ++++++++++++++++++++
 src/savane/svmain/models.py                        |    9 +-
 6 files changed, 225 insertions(+), 18 deletions(-)
 copy src/savane/context_processors.py => sbin/sv (62%)
 mode change 100644 => 100755
 create mode 100755 sbin/sv-populate-ldap
 copy src/savane/{context_processors.py => backend/__init__.py} (69%)
 create mode 100644 src/savane/backend/auth_ldif_export.py


hooks/post-receive
-- 
Savane-cleanup framework




reply via email to

[Prev in Thread] Current Thread [Next in Thread]