>From 014963eee8b3506f9bd192f2d79014b58a28c93f Mon Sep 17 00:00:00 2001
From: Builder Draco <builder@draco>
Date: Tue, 19 Jul 2011 14:31:19 +0200
Subject: [PATCH] LDAP authentication

---
 cumin/bin/cumin-admin                 |   35 ++++++++++--
 cumin/bin/cumin-web                   |    4 +-
 cumin/model/cumin.xml                 |    1 +
 cumin/python/cumin/account/widgets.py |   11 ++--
 cumin/python/cumin/admin.py           |    3 +-
 cumin/python/cumin/authenticator.py   |   96 +++++++++++++++++++++++++++++++++
 cumin/python/cumin/config.py          |    3 +
 cumin/python/cumin/main.py            |    7 ++-
 8 files changed, 146 insertions(+), 14 deletions(-)
 create mode 100644 cumin/python/cumin/authenticator.py

diff --git a/cumin/bin/cumin-admin b/cumin/bin/cumin-admin
index bdbce3d..b1d5be8 100755
--- a/cumin/bin/cumin-admin
+++ b/cumin/bin/cumin-admin
@@ -93,7 +93,7 @@ def generate_usage():
     lines.append("")
     lines.append("User commands:")
     lines.append("")
-    lines.append("  add-user USER [PASSWORD]      Add USER")
+    lines.append("  add-user [database] USER [PASSWORD]      Add USER")
     lines.append("  remove-user USER              Remove USER")
     lines.append("  add-assignment USER ROLE      Add USER to ROLE")
     lines.append("  remove-assignment USER ROLE   Remove USER from ROLE")
@@ -109,6 +109,9 @@ def generate_usage():
     lines.append("  create-schema                 Create schema objects")
     lines.append("  drop-schema                   Drop schema and all data")
     lines.append("  print-schema                  Print the schema definition")
+    lines.append("LDAP commands:")
+    lines.append("  ldap-status                   Display sync status")
+    lines.append("  ldap-sync <remove>            Sync internal database with LDAP")
 
     return "\n".join(lines)
 
@@ -181,21 +184,35 @@ def handle_list_users(app, cursor, opts, args):
 
 def handle_add_user(app, cursor, opts, args):
     try:
-        name = args[0]
+        external = args[0]
+        if external == 'external':
+            external = 1
+        elif external == 'internal':
+            external = 0
+        else:
+            error("Please select external or internal database")
     except IndexError:
         error("USER is required")
 
     try:
-        password = args[1]
+        name = args[1]
     except IndexError:
-        password = prompt_password()
+        error("USER is required")
+
+    if external != 1:
+        try:
+            password = args[2]
+        except IndexError:
+            password = prompt_password()
 
-    crypted = crypt_password(password)
+        crypted = crypt_password(password)
+    else:
+        crypted = ""
 
     role = app.admin.get_role(cursor, "user")
 
     try:
-        user = app.admin.add_user(cursor, name, crypted)
+        user = app.admin.add_user(cursor, name, crypted, external)
     except IntegrityError:
         error("A user called '%s' already exists" % name)
 
@@ -288,6 +305,12 @@ def handle_remove_user(app, cursor, opts, args):
 
     print "User '%s' is removed" % name
 
+def handle_ldap_sync(app,cursor,opts,args):
+    print "def %s %s " % (opts,args)
+
+def handle_ldap_status(app,cursor,opts,args):
+    print "asdf %s %s" % (opts,args)
+
 def handle_list_roles(app, cursor, opts, args):
     cls = app.model.com_redhat_cumin.Role
     roles = cls.get_selection(cursor)
diff --git a/cumin/bin/cumin-web b/cumin/bin/cumin-web
index bf5e897..3159ebe 100755
--- a/cumin/bin/cumin-web
+++ b/cumin/bin/cumin-web
@@ -81,9 +81,11 @@ def main():
                                       values.log_max_archives)
 
             broker_uris = [x.strip() for x in opts.brokers.split(",")]
+            authmech = [ x.strip() for x in values.auth.split(" ")]
 
             cumin = Cumin(config.get_home(), broker_uris, opts.database, 
-                          opts.host, opts.port, values.persona)
+                          opts.host, opts.port, values.persona,
+                          authmech)
 
             if type(values.sasl_mech_list) == str:
                 cumin.sasl_mech_list = values.sasl_mech_list.upper()
diff --git a/cumin/model/cumin.xml b/cumin/model/cumin.xml
index ae7efb2..503ade2 100644
--- a/cumin/model/cumin.xml
+++ b/cumin/model/cumin.xml
@@ -16,6 +16,7 @@
 
     <class name="User">
       <property name="name" type="sstr" index="y"/>
+      <property name="external" type="sstr" />
       <property name="password" type="sstr"/>
     </class>
 
diff --git a/cumin/python/cumin/account/widgets.py b/cumin/python/cumin/account/widgets.py
index 44bcf02..51eafa5 100644
--- a/cumin/python/cumin/account/widgets.py
+++ b/cumin/python/cumin/account/widgets.py
@@ -165,15 +165,16 @@ class LoginForm(Form):
                 if not user:
                     self.login_invalid.set(session, True)
                     return
-
-                crypted = user.password
-
-                if crypted and crypt(password, crypted) == crypted:
+# hack begin
+                authenticated = self.app.authenticator.authenticate(name,password)
+#                crypted = user.password
+                if authenticated:
+#                if crypted and crypt(password, crypted) == crypted:
                     # You're in!
 
                     login = LoginSession(self.app, user)
                     session.client_session.attributes["login_session"] = login
-
+                    log.debug(session.client_session)
                     url = self.page.origin.get(session)
 
                     self.page.redirect.set(session, url)
diff --git a/cumin/python/cumin/admin.py b/cumin/python/cumin/admin.py
index 20d3143..2737e7a 100644
--- a/cumin/python/cumin/admin.py
+++ b/cumin/python/cumin/admin.py
@@ -64,12 +64,13 @@ class CuminAdmin(object):
         cls = self.app.model.com_redhat_cumin.User
         return cls.get_object(cursor, name=name)
 
-    def add_user(self, cursor, name, crypted_password):
+    def add_user(self, cursor, name, crypted_password, external = 0):
         cls = self.app.model.com_redhat_cumin.User
 
         user = cls.create_object(cursor)
         user.name = name
         user.password = crypted_password
+        user.external = external
         user.fake_qmf_values()
         user.save(cursor)
 
diff --git a/cumin/python/cumin/authenticator.py b/cumin/python/cumin/authenticator.py
new file mode 100644
index 0000000..0e9c0b3
--- /dev/null
+++ b/cumin/python/cumin/authenticator.py
@@ -0,0 +1,96 @@
+from util import *
+#import ldap
+#import ldapurl
+
+log = logging.getLogger("cumin.authenticator")
+
+class CuminAuthenticator(object):
+
+	def __init__(self,app):
+		self.authenticators=[]
+		self.app = app
+		log.info("Initializing %s", self)
+		for authmech in app.authmech:
+			if authmech.startswith("ldap="):
+				try:
+					log.info("Adding ldap authenticator")
+					self.authenticators.append(CuminAuthenticatorLDAP(authmech))
+				except:
+					log.error("Cannot make instance of %s, missing LDAP module.", authmech )
+			elif authmech.startswith("script="):
+				log.info("Adding script authenticator")
+				self.authenticators.append(CuminAuthenticatorScript(authmech))
+			elif authmech.startswith("internal"):
+				log.info("Adding internal authenticator.")
+			else:
+				log.error("Unsupported authenticator type %s", authmech)
+		
+	
+	def authenticate(self,username,password):
+		log.debug("Calling authenticate")
+		cursor = self.app.database.get_read_cursor()
+		cls = self.app.model.com_redhat_cumin.User
+		user = cls.get_object(cursor, name=username)
+		log.debug("is external user %s", user.external)
+		if user.external != "1":
+			if user.password and crypt(password, user.password) == user.password:
+				return True
+		else:
+			for authenticator in self.authenticators:
+				log.debug("Trying authenticator %s", authenticator )
+				if authenticator.authenticate(username,password):
+					return True
+			return False
+	
+
+
+class CuminAuthenticatorLDAP(object):
+	global ldap
+	import ldap
+	global ldapurl 
+	import ldapurl
+
+	def __init__(self,url):
+		log.info("Initializing %s", self)
+		myurl = url.split("=",1)[1]
+		log.debug("Parsing ldap url: %s", myurl)
+		try:
+			self.url = ldapurl.LDAPUrl(myurl)
+		except Exception,e:
+			log.error("Cannot parse ldap url %s", e)
+
+	
+		
+	def authenticate(self,username,password):
+		log.debug("Authenticating against %s", self)
+		try:
+			conn = ldap.initialize(self.url.urlscheme + "://" + self.url.hostport )
+		except Exception,e:
+			log.error("Cannot contact ldap server %s",e)
+			return False
+
+		if self.url.who and self.url.cred:
+			try:
+				self.conn.simple_bind_s(self.url.who,self.url.cred)
+			except:
+				log.error("Cannot authenticate as service %s",self.url.who)
+				return False
+		filter = self.url.filterstr % username
+		res = conn.search_s(self.url.dn,self.url.scope,filter)
+		for dn,ent in res:
+			try:
+				conn.simple_bind_s(dn,password)
+				return True
+			except:
+				log.info("Unable to authenticate %s " , dn )
+		return False
+
+class CuminAuthenticatorScript(object):
+	def __init__(self,commandline):
+		self.commandline = commandline
+
+	def authenticate(self,username,password):
+		log.debug("Executing %s %s",(self.commandline,username))
+			
+		
+		
diff --git a/cumin/python/cumin/config.py b/cumin/python/cumin/config.py
index d65c2c8..d773b0b 100644
--- a/cumin/python/cumin/config.py
+++ b/cumin/python/cumin/config.py
@@ -36,6 +36,9 @@ class CuminConfig(Config):
         param = ConfigParameter(web, "port", int)
         param.default = 45672
 
+        param = ConfigParameter(web, "auth", str)
+        param.default = 'internal'
+
         param = ConfigParameter(web, "operator-email", str)
 
         param = ConfigParameter(web, "user", str)
diff --git a/cumin/python/cumin/main.py b/cumin/python/cumin/main.py
index 0342a49..67dd879 100644
--- a/cumin/python/cumin/main.py
+++ b/cumin/python/cumin/main.py
@@ -21,6 +21,7 @@ from sqladapter import *
 from user import *
 from util import *
 from widgets import *
+from authenticator import *
 
 from wooly import Session
 from cumin.stat import PieChartPage
@@ -30,10 +31,12 @@ log = logging.getLogger("cumin")
 
 class Cumin(Application):
     def __init__(self, home, broker_uris, database_dsn,
-                 host="localhost", port=45672, persona="default"):
+                 host="localhost", port=45672, persona="default", 
+                 authmech=["internal"]):
         super(Cumin, self).__init__()
 
         self.home = home
+        self.authmech = authmech
 
         model_dir = os.path.join(self.home, "model")
 
@@ -42,6 +45,7 @@ class Cumin(Application):
         self.database = CuminDatabase(self, database_dsn)
         self.server = CuminServer(self, host, port)
         self.admin = CuminAdmin(self)
+        self.authenticator = CuminAuthenticator(self)
 
         self.add_resource_dir(os.path.join(self.home, "resources-wooly"))
         self.add_resource_dir(os.path.join(self.home, "resources"))
@@ -589,6 +593,7 @@ class TopSubmissionTable(TopTable):
 
         def render_cell_content(self, session, record):
             created = self.field.get_content(session, record)
+            import time
             return fmt_duration(time.time() - secs(created))
 
         def render_text_align(self, session):
-- 
1.7.4.1

