Author: tmckay
Date: 2012-04-19 17:48:29 +0000 (Thu, 19 Apr 2012)
New Revision: 5316
Modified:
trunk/cumin/bin/cumin-admin
trunk/cumin/python/cumin/account/widgets.py
trunk/cumin/python/cumin/authenticator.py
trunk/cumin/python/cumin/widgets.py
Log:
Modify external user treatment so that the cumin database may be sparse,
easing maintenance of external user accounts.
Cumin will look for users externally if they are not defined in the
local database, and will assume a role of 'user' for such a user.
External users with roles other than 'user' may still be defined in the local
database with the cumin-admin external-user command.
Bulk import (external-sync) has been disabled because it is not necessary.
BZ737979
Modified: trunk/cumin/bin/cumin-admin
===================================================================
--- trunk/cumin/bin/cumin-admin 2012-04-19 17:08:41 UTC (rev 5315)
+++ trunk/cumin/bin/cumin-admin 2012-04-19 17:48:29 UTC (rev 5316)
@@ -125,9 +125,11 @@
lines.append("User commands:")
lines.append("")
lines.append(" add-user USER [PASSWORD] Add USER")
- lines.append(" external-user USER Add USER with external
authentication")
- lines.append(" external-sync AUTH [VERBOSE] Batch import users with external
authentication")
- lines.append(" AUTH is the name of a mechanism
like 'ldap' or 'script'")
+ lines.append(" external-user USER Add USER with external
authentication.")
+ lines.append(" Use this command when a role other
than 'user'")
+ lines.append(" will be set for this external
user. Otherwise,")
+ lines.append(" this command is not
necessary.")
+ lines.append("")
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")
@@ -368,6 +370,9 @@
print "External user '%s' is added" % name
def handle_external_sync(app, cursor, opts, args):
+ print "This command is not supported at this time."
+ return
+
try:
authenticatorname = args[0]
except IndexError:
Modified: trunk/cumin/python/cumin/account/widgets.py
===================================================================
--- trunk/cumin/python/cumin/account/widgets.py 2012-04-19 17:08:41 UTC (rev 5315)
+++ trunk/cumin/python/cumin/account/widgets.py 2012-04-19 17:48:29 UTC (rev 5316)
@@ -118,31 +118,34 @@
self.validate(session)
if not self.errors.get(session):
- cursor = self.app.database.get_read_cursor()
-
- cls = self.app.model.com_redhat_cumin.User
- user = cls.get_object(cursor, name=name)
-
- if not user:
- self.login_invalid.set(session, "credentials")
- return
-
- authenticated = self.app.authenticator.authenticate(name,password)
- if authenticated:
+ user, ok = self.app.authenticator.authenticate(name, password)
+ if ok:
# You're in! almost...
# Check for a valid group if group authorization is on
+ roles = []
if self.app.authorizator.is_enforcing():
- roles = self.app.admin.get_roles_for_user(cursor, user)
- if not self.app.authorizator.contains_valid_group(roles):
+ if user is None:
+ # This was an external user with no role
+ # entry in the Cumin database. We default the
+ # role to 'user'. In the future we may store
+ # roles externally as well.
+ roles = ['user']
+ else:
+ cursor = self.app.database.get_read_cursor()
+ roles = self.app.admin.get_roles_for_user(
+ cursor, user)
+ ok = self.app.authorizator.contains_valid_group(roles)
+ if not ok:
self.login_invalid.set(session, "roles")
- return
- else:
- roles = []
- login = LoginSession(self.app, user, roles)
- session.client_session.attributes["login_session"] = login
- url = self.page.origin.get(session)
- self.page.redirect.set(session, url)
+ # If we're still okay, set up the login
+ if ok:
+ if user is None:
+ user = name
+ login = LoginSession(self.app, user, roles)
+ session.client_session.attributes["login_session"] =
login
+ url = self.page.origin.get(session)
+ self.page.redirect.set(session, url)
else:
self.login_invalid.set(session, "credentials")
@@ -188,7 +191,8 @@
user = session.client_session.attributes["login_session"].user
# In case a different login session for this user has made
# changes, refresh the user object
- user.load(session.cursor)
+ if hasattr(user, "load"):
+ user.load(session.cursor)
new0 = self.new0.get(session)
new1 = self.new1.get(session)
Modified: trunk/cumin/python/cumin/authenticator.py
===================================================================
--- trunk/cumin/python/cumin/authenticator.py 2012-04-19 17:08:41 UTC (rev 5315)
+++ trunk/cumin/python/cumin/authenticator.py 2012-04-19 17:48:29 UTC (rev 5316)
@@ -51,17 +51,24 @@
return False
return conn
- def change_pw(self,user,oldpass,newpass):
+ def _find_user(self, user):
+ res = False
conn = self.__prep_con()
- if not conn:
- return False
- filter = self.url.filterstr % user
- try:
- res = conn.search_s(self.url.dn, self.url.scope, filter)
- except Exception, e:
- log.error("Authenticator: update password, "\
- "query returned exception %s", e)
- res = []
+ if conn:
+ filter = self.url.filterstr % user
+ try:
+ res = conn.search_s(self.url.dn, self.url.scope, filter)
+ except Exception, e:
+ log.error("Authenticator: find_user, "\
+ "query returned exception %s", e)
+ return conn, res
+
+ def find_user(self, user):
+ conn, res = self._find_user(user)
+ return res is not False
+
+ def change_pw(self, user, oldpass, newpass):
+ conn, res = self._find_user(user)
if not res:
log.info("Authenticator: update password, "\
"query returned no results in %s",
self.__class__.__name__)
@@ -72,8 +79,8 @@
log.info("Authenticator: update password succeeded in %s",
self.__class__.__name__)
_syslog.log("cumin: updated password via LDAP for user %s" % dn)
- return True
- return False
+ res = True
+ return res
def batch_import(self):
log.debug("Authenticator: batch import in %s",
self.__class__.__name__)
@@ -100,16 +107,7 @@
def authenticate(self, username, password):
log.debug("Authenticating against %s", self.__class__.__name__)
- conn = self.__prep_con()
- if not conn:
- return False
- filter = self.url.filterstr % username
- try:
- res = conn.search_s(self.url.dn, self.url.scope, filter)
- except Exception, e:
- log.error("Authenticator: authenticate, "\
- "query returned exception %s", e)
- res = []
+ conn, res = self._find_user(username)
if not res:
log.info("Authenticator: authentication failed, "\
"query returned no results in %s",
self.__class__.__name__)
@@ -160,6 +158,9 @@
if self.params.has_key('list'):
log.debug("Authenticator: adding list capability to %s", me)
self.cap.append('l')
+ if self.params.has_key('find'):
+ log.debug("Authenticator: adding find capability to %s", me)
+ self.cap.append('f')
else:
log.error('Authenticator: script parameter missing or target file '\
'not executable in %s', me)
@@ -167,8 +168,7 @@
def authenticate(self, username, password):
me = self.__class__.__name__
if 'a' in self.cap:
- log.debug("Authenticator: executing %s in %s",
- self.params['script'], me)
+ log.debug("Authenticator: authenticate in %s" % me)
# Use nameless pipes to pass parameters.
# If they are passed as arguments they show up in the output
# of ps, etc. This way is secure.
@@ -185,6 +185,24 @@
_syslog.log("cumin: authentication via script failed for user %s" %
username)
return False
+ def find_user(self, username):
+ me = self.__class__.__name__
+ if 'f' in self.cap:
+ log.debug("Authenticator: executing find_user in %s" % me)
+ # Use nameless pipes to pass parameters.
+ # If they are passed as arguments they show up in the output
+ # of ps, etc. This way is secure.
+ # Note, the call to communicate causes an EOF on stdin
+ cmd = [self.params['script']]
+ res = subprocess.Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
+ out, err = res.communicate(input="%s %s" % \
+ (self.params['find'], username))
+ log.debug("Authenticator: find_user, script returned "\
+ "%s, %s, exitcode %s", out, err, res.returncode)
+ if res.returncode == 0:
+ return True
+ return False
+
def batch_import(self):
if 'l' in self.cap:
log.debug("Authenticator: batch import in %s",
@@ -204,7 +222,7 @@
def change_pw(self, user, newpass, oldpass):
if 'c' in self.cap:
me = self.__class__.__name__
- log.debug("Authenticator: change password in %s", me)
+ log.debug("Authenticator: change password in %s" % me)
# Use nameless pipes to pass parameters.
# If they are passed as arguments they show up in the output
@@ -309,22 +327,37 @@
cursor = self.app.database.get_read_cursor()
cls = self.app.model.com_redhat_cumin.User
user = cls.get_object(cursor, name=username)
- external = len(user.password) == 0
- log.info("Authenticator: authenticating external user %s" % external)
- if not external:
+ if user is None or len(user.password) == 0:
+ log.debug("Authenticator: authenticating external user")
+ for authenticator in self.authenticators:
+ log.debug("Authenticator: authenticate, try %s",
authenticator)
+ res = authenticator.authenticate(username, password)
+ if res:
+ break
+ else:
res = crypt(password, user.password) == user.password
- if res:
- msg = "cumin: authenticated user %s" % username
- else:
- msg = "cumin: authentication failed for user %s" % username
+ msg = "cumin: authentication %s for user %s" \
+ % (res and "succeeded" or "failed", username)
_syslog.log(msg)
- return res
- else:
+ return user, res
+
+ def find_user(self, username):
+ # Like authenticate, but find_user only confirms the
+ # existence of a user. We are not concerned with password here.
+ log.debug("Authenticator: calling find_user")
+ cursor = self.app.database.get_read_cursor()
+ cls = self.app.model.com_redhat_cumin.User
+ user = cls.get_object(cursor, name=username)
+ res = user and len(user.password) > 0
+ if not res:
+ # Try to find the user externally
+ log.debug("Authenticator: finding external user")
for authenticator in self.authenticators:
- log.debug("Authenticator: authenticate, try %s",
authenticator)
- if authenticator.authenticate(username, password):
- return True
- return False
+ log.debug("Authenticator: find_user, try %s", authenticator)
+ res = authenticator.find_user(username)
+ if res:
+ break
+ return user, res
def update_password(self, username, oldpassword, newpassword):
status = False
@@ -335,18 +368,21 @@
cursor = conn.cursor()
cls = self.app.model.com_redhat_cumin.User
user = cls.get_object(cursor, name=username)
- if user.password == "":
- for authenticator in self.authenticators:
- status = authenticator.authenticate(username, oldpassword)
- if status:
- status = authenticator.change_pw(username,
- oldpassword, newpassword)
- if not status:
- message = "Could not change password"
- break
+ if user is None or user.password == "":
+ # Disable change password for external users
+ message = "Change password is not allowed for external users"
- if not status and message == "":
- message = "The password is incorrect"
+ #for authenticator in self.authenticators:
+ # status = authenticator.authenticate(username, oldpassword)
+ # if status:
+ # status = authenticator.change_pw(username,
+ # oldpassword, newpassword)
+ # if not status:
+ # message = "Could not change password"
+ # break
+ #if not status and message == "":
+ # message = "The password is incorrect"
+
else:
status = crypt(oldpassword, user.password) == user.password
if status:
Modified: trunk/cumin/python/cumin/widgets.py
===================================================================
--- trunk/cumin/python/cumin/widgets.py 2012-04-19 17:08:41 UTC (rev 5315)
+++ trunk/cumin/python/cumin/widgets.py 2012-04-19 17:48:29 UTC (rev 5316)
@@ -1171,11 +1171,23 @@
class LoginSession(object):
def __init__(self, app, user, group):
self.app = app
- self.user = user
+
+ # If this is an external user, create
+ # an adapter here. Lots of things
+ # expect user.name to be defined.
+ if type(user) is str:
+ self.user = LoginSession.ExternalUser()
+ self.user.name = user
+ else:
+ self.user = user
+
self.group = group
self.created = datetime.now()
self.notifications = list()
+ class ExternalUser(object):
+ pass
+
class NotificationSet(Widget):
def __init__(self, app, name):
super(NotificationSet, self).__init__(app, name)
@@ -1309,24 +1321,25 @@
elif self.app.auth_proxy or self.app.user:
username = self.app.user
# proxy user overrides app defined user
- try:
- username = session.request_environment['HTTP_REMOTE_USER']
- except KeyError:
- log.debug("Proxy auth enabled but no remote user set")
- pass
+ if self.app.auth_proxy:
+ try:
+ username = session.request_environment['HTTP_REMOTE_USER']
+ except KeyError:
+ log.debug("Proxy auth enabled but no remote user set")
if username:
- cls = self.app.model.com_redhat_cumin.User
- users = cls.get_selection(session.cursor, name=username)
- if not users:
+ user, ok = self.app.authenticator.find_user(username)
+ if not ok:
+ # We couldn't find the user, internally or externally
if not self.app.auth_proxy:
log.info("User '%s' not found" % username)
+ return False
else:
log.info("User %s not found in db, "\
"using auth proxy", username )
#Hmmm, what is users here? For now just let it return
# to the login page
- return False
+ return False
#TODO prehodit do authenticatora
# ondemand -> authenticator.create_user
@@ -1334,15 +1347,22 @@
# Check for valid group
if self.app.authorizator.is_enforcing():
- cursor = self.app.database.get_read_cursor()
- roles = self.app.admin.get_roles_for_user(cursor, users[0])
+ if not user:
+ # This is an external user, default role is 'user'
+ roles = ['user']
+ else:
+ cursor = self.app.database.get_read_cursor()
+ roles = self.app.admin.get_roles_for_user(
+ cursor, user)
if not self.app.authorizator.contains_valid_group(roles):
log.info("No valid roles for '%s'" % username)
return False # go to login page
else:
roles = []
- login = LoginSession(self.app, users[0], roles)
+ if user is None:
+ user = username
+ login = LoginSession(self.app, user, roles)
session.client_session.attributes["login_session"] = login
return True
return False