From 182fbe30945a8de005f00b460968dca7973342fb Mon Sep 17 00:00:00 2001 From: rcritten Date: Mon, 10 Sep 2007 16:33:01 -0400 Subject: Enable mod_proxy to sit in front of TurboGears and pass along the kerberos principal name Add an identity an visit class to TurboGears that can handle the user without requiring a database Update the UI to show the user correctly. Note that this is currently disabled. It is hardcoded to always return the principal test@FREEIPA.ORG in proxyprovider.py It doesn't handle an unauthorized request because that can never happen. --- ipa-server/ipa-gui/dev.cfg | 13 ++- ipa-server/ipa-gui/ipa_gui.egg-info/SOURCES.txt | 7 ++ .../ipa-gui/ipa_gui.egg-info/entry_points.txt | 6 ++ ipa-server/ipa-gui/ipagui/controllers.py | 23 +++- ipa-server/ipa-gui/ipagui/proxyprovider.py | 118 +++++++++++++++++++++ ipa-server/ipa-gui/ipagui/proxyvisit.py | 25 +++++ ipa-server/ipa-gui/ipagui/templates/master.kid | 19 ++-- ipa-server/ipa-gui/setup.py | 6 ++ ipa-server/xmlrpc-server/ipa.conf | 36 ++++++- 9 files changed, 239 insertions(+), 14 deletions(-) create mode 100644 ipa-server/ipa-gui/ipa_gui.egg-info/entry_points.txt create mode 100644 ipa-server/ipa-gui/ipagui/proxyprovider.py create mode 100644 ipa-server/ipa-gui/ipagui/proxyvisit.py diff --git a/ipa-server/ipa-gui/dev.cfg b/ipa-server/ipa-gui/dev.cfg index 7bb0fd8c..7cc2441d 100644 --- a/ipa-server/ipa-gui/dev.cfg +++ b/ipa-server/ipa-gui/dev.cfg @@ -13,8 +13,19 @@ # If you have sqlite, here's a simple default to get you started # in development -sqlobject.dburi="sqlite://%(current_dir_uri)s/devdata.sqlite" +# sqlobject.dburi="sqlite://%(current_dir_uri)s/devdata.sqlite" +# Our our sqlobject-derived proxy provider +identity.provider='proxyprovider' + +# the first thing checked on any request. We want to short-circuit this +# as early as possible +identity.source = 'visit' + +# Turn on identity and visit (visit is required for identity) +identity.on=True +visit.on=True +visit.manager='proxyvisit' # if you are using a database or table type without transactions # (MySQL default, for example), you should turn off transactions diff --git a/ipa-server/ipa-gui/ipa_gui.egg-info/SOURCES.txt b/ipa-server/ipa-gui/ipa_gui.egg-info/SOURCES.txt index 49b7ce44..3bd84824 100644 --- a/ipa-server/ipa-gui/ipa_gui.egg-info/SOURCES.txt +++ b/ipa-server/ipa-gui/ipa_gui.egg-info/SOURCES.txt @@ -4,6 +4,7 @@ start-ipagui.py ipa_gui.egg-info/PKG-INFO ipa_gui.egg-info/SOURCES.txt ipa_gui.egg-info/dependency_links.txt +ipa_gui.egg-info/entry_points.txt ipa_gui.egg-info/not-zip-safe ipa_gui.egg-info/paster_plugins.txt ipa_gui.egg-info/requires.txt @@ -13,8 +14,14 @@ ipagui/__init__.py ipagui/controllers.py ipagui/json.py ipagui/model.py +ipagui/proxyprovider.py +ipagui/proxyvisit.py ipagui/release.py ipagui/config/__init__.py +ipagui/forms/__init__.py +ipagui/forms/user.py +ipagui/helpers/__init__.py +ipagui/helpers/userhelper.py ipagui/templates/__init__.py ipagui/tests/__init__.py ipagui/tests/test_controllers.py diff --git a/ipa-server/ipa-gui/ipa_gui.egg-info/entry_points.txt b/ipa-server/ipa-gui/ipa_gui.egg-info/entry_points.txt new file mode 100644 index 00000000..22c35bfd --- /dev/null +++ b/ipa-server/ipa-gui/ipa_gui.egg-info/entry_points.txt @@ -0,0 +1,6 @@ + + [turbogears.identity.provider] + proxyprovider = ipagui.proxyprovider:ProxyIdentityProvider + + [turbogears.visit.manager] + proxyvisit = ipagui.proxyvisit:ProxyVisitManager diff --git a/ipa-server/ipa-gui/ipagui/controllers.py b/ipa-server/ipa-gui/ipagui/controllers.py index acbc4818..a0755525 100644 --- a/ipa-server/ipa-gui/ipagui/controllers.py +++ b/ipa-server/ipa-gui/ipagui/controllers.py @@ -8,6 +8,7 @@ from turbogears import controllers, expose, flash from turbogears import validators, validate from turbogears import widgets, paginate from turbogears import error_handler +from turbogears import identity # from model import * # import logging # log = logging.getLogger("ipagui.controllers") @@ -27,7 +28,6 @@ user_edit_form = forms.user.UserEditForm() password_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" client = ipa.ipaclient.IPAClient(True) -client.set_principal("test@FREEIPA.ORG") user_fields = ['*', 'nsAccountLock'] @@ -45,10 +45,12 @@ def utf8_encode(value): class Root(controllers.RootController): @expose(template="ipagui.templates.welcome") + @identity.require(identity.not_anonymous()) def index(self): return dict() @expose() + @identity.require(identity.not_anonymous()) def topsearch(self, **kw): if kw.get('searchtype') == "Users": return self.userlist(uid=kw.get('searchvalue')) @@ -62,6 +64,7 @@ class Root(controllers.RootController): ######## @expose("ipagui.templates.usernew") + @identity.require(identity.not_anonymous()) def usernew(self, tg_errors=None): """Displays the new user form""" if tg_errors: @@ -70,9 +73,11 @@ class Root(controllers.RootController): return dict(form=user_new_form) @expose() + @identity.require(identity.not_anonymous()) def usercreate(self, **kw): """Creates a new user""" restrict_post() + client.set_principal(identity.current.user_name) if kw.get('submit') == 'Cancel': turbogears.flash("Add user cancelled") raise turbogears.redirect('/userlist') @@ -104,11 +109,13 @@ class Root(controllers.RootController): @expose("ipagui.templates.useredit") + @identity.require(identity.not_anonymous()) def useredit(self, uid, tg_errors=None): """Displays the edit user form""" if tg_errors: turbogears.flash("There was a problem with the form!") + client.set_principal(identity.current.user_name) user = client.get_user_by_uid(uid, user_fields) user_dict = user.toDict() # Edit shouldn't fill in the password field. @@ -121,9 +128,11 @@ class Root(controllers.RootController): return dict(form=user_edit_form, user=user_dict) @expose() + @identity.require(identity.not_anonymous()) def userupdate(self, **kw): """Updates an existing user""" restrict_post() + client.set_principal(identity.current.user_name) if kw.get('submit') == 'Cancel Edit': turbogears.flash("Edit user cancelled") raise turbogears.redirect('/usershow', uid=kw.get('uid')) @@ -169,8 +178,10 @@ class Root(controllers.RootController): @expose("ipagui.templates.userlist") + @identity.require(identity.not_anonymous()) def userlist(self, **kw): """Retrieve a list of all users and display them in one huge list""" + client.set_principal(identity.current.user_name) users = None counter = 0 uid = kw.get('uid') @@ -190,8 +201,10 @@ class Root(controllers.RootController): @expose("ipagui.templates.usershow") + @identity.require(identity.not_anonymous()) def usershow(self, uid): """Retrieve a single user for display""" + client.set_principal(identity.current.user_name) try: user = client.get_user_by_uid(uid, user_fields) return dict(user=user.toDict(), fields=forms.user.UserFields()) @@ -200,10 +213,12 @@ class Root(controllers.RootController): raise turbogears.redirect("/") @validate(form=user_new_form) + @identity.require(identity.not_anonymous()) def usercreatevalidate(self, tg_errors=None, **kw): return tg_errors, kw @validate(form=user_edit_form) + @identity.require(identity.not_anonymous()) def userupdatevalidate(self, tg_errors=None, **kw): return tg_errors, kw @@ -222,10 +237,12 @@ class Root(controllers.RootController): return password @expose() + @identity.require(identity.not_anonymous()) def suggest_uid(self, givenname, sn): if (len(givenname) == 0) or (len(sn) == 0): return "" + client.set_principal(identity.current.user_name) givenname = givenname.lower() sn = sn.lower() @@ -309,7 +326,9 @@ class Root(controllers.RootController): ######### @expose("ipagui.templates.groupindex") + @identity.require(identity.not_anonymous()) def groupindex(self, tg_errors=None): + client.set_principal(identity.current.user_name) return dict() @@ -318,5 +337,7 @@ class Root(controllers.RootController): ############ @expose("ipagui.templates.resindex") + @identity.require(identity.not_anonymous()) def resindex(self, tg_errors=None): + client.set_principal(identity.current.user_name) return dict() diff --git a/ipa-server/ipa-gui/ipagui/proxyprovider.py b/ipa-server/ipa-gui/ipagui/proxyprovider.py new file mode 100644 index 00000000..12519880 --- /dev/null +++ b/ipa-server/ipa-gui/ipagui/proxyprovider.py @@ -0,0 +1,118 @@ +from turbogears.identity.soprovider import * +from turbogears.identity.visitor import * +import logging + +log = logging.getLogger("turbogears.identity") + +class IPA_User(object): + ''' + Shell of a User definition. We don't really need much here. + ''' + + def __init__(self, user_name): + self.user_name = user_name + self.display_name = user_name + self.permissions = None + self.groups = None + return + +class ProxyIdentity(object): + def __init__(self, visit_key, user=None): + if user: + self._user= user + self.visit_key= visit_key + + def _get_user(self): + try: + return self._user + except AttributeError: + # User hasn't already been set + return None + user= property(_get_user) + + def _get_user_name(self): + if not self.user: + return None + return self.user.user_name + user_name= property(_get_user_name) + + def _get_name(self): + if not self.user: + return None + return self.user.name + user_name= property(_get_name) + + def _get_anonymous(self): + return not self.user + anonymous= property(_get_anonymous) + + def _get_permissions(self): + try: + return self._permissions + except AttributeError: + # Permissions haven't been computed yet + return None + permissions= property(_get_permissions) + + def _get_groups(self): + try: + return self._groups + except AttributeError: + # Groups haven't been computed yet + return None + groups= property(_get_groups) + + def logout(self): + ''' + Remove the link between this identity and the visit. + ''' + # Clear the current identity + anon= ProxyObjectIdentity(None,None) + #XXX if user is None anonymous will be true, no need to set attr. + #anon.anonymous= True + identity.set_current_identity( anon ) + +class ProxyIdentityProvider(SqlObjectIdentityProvider): + ''' + IdentityProvider that uses REMOTE_USER from Apache + ''' + def __init__(self): + super(ProxyIdentityProvider, self).__init__() + get = turbogears.config.get + # We can get any config variables here + log.info( "Proxy Identity starting" ) + + def create_provider_model(self): + pass + + def validate_identity(self, user_name, password, visit_key): + user = IPA_User(user_name) + log.debug( "validate_identity %s" % user_name) + + return ProxyIdentity(visit_key, user) + + def validate_password(self, user, user_name, password): + '''Validation has already occurred in the proxy''' + return True + + def load_identity(self, visit_key): + try: +# user_name= cherrypy.request.headers['X-FORWARDED-USER'] + user_name= "test@FREEIPA.ORG" + except KeyError: + return None + set_login_attempted( True ) + return self.validate_identity( user_name, None, visit_key ) + + def anonymous_identity( self ): + ''' + This shouldn't ever happen in IPA but including it to include the + entire identity API. + ''' + return ProxyIdentity( None ) + + def authenticated_identity(self, user): + ''' + Constructs Identity object for user that has no associated visit_key. + ''' + return ProxyIdentity(None, user) diff --git a/ipa-server/ipa-gui/ipagui/proxyvisit.py b/ipa-server/ipa-gui/ipagui/proxyvisit.py new file mode 100644 index 00000000..1fde3902 --- /dev/null +++ b/ipa-server/ipa-gui/ipagui/proxyvisit.py @@ -0,0 +1,25 @@ +from turbogears.visit.api import BaseVisitManager, Visit +from turbogears import config + +import logging + +log = logging.getLogger("turbogears.visit.proxyvisit") + +class ProxyVisitManager(BaseVisitManager): + """Virtually empty class just so can avoid saving this stuff in a + database.""" + def __init__(self, timeout): + super(ProxyVisitManager,self).__init__(timeout) + return + + def create_model(self): + return + + def new_visit_with_key(self, visit_key): + return Visit(visit_key, True) + + def visit_for_key(self, visit_key): + return Visit(visit_key, False) + + def update_queued_visits(self, queue): + return None diff --git a/ipa-server/ipa-gui/ipagui/templates/master.kid b/ipa-server/ipa-gui/ipagui/templates/master.kid index 8abe24ba..2d3a35f2 100644 --- a/ipa-server/ipa-gui/ipagui/templates/master.kid +++ b/ipa-server/ipa-gui/ipagui/templates/master.kid @@ -14,15 +14,6 @@ -
- - Login - - - Welcome ${tg.identity.user.display_name}. - Logout - -