diff options
author | Michael DeHaan <mdehaan@mdehaan.rdu.redhat.com> | 2007-09-17 14:58:05 -0400 |
---|---|---|
committer | Michael DeHaan <mdehaan@mdehaan.rdu.redhat.com> | 2007-09-17 14:58:05 -0400 |
commit | 43aedaa8cf3cbcb9b1d7d4815e5db3e223dac417 (patch) | |
tree | 0975e4e3f8edfeb93c9ac8bee47c743e59e1a778 | |
parent | 4d391978ad2c5081a8d884c30c4269ca518824a7 (diff) | |
download | third_party-cobbler-43aedaa8cf3cbcb9b1d7d4815e5db3e223dac417.tar.gz third_party-cobbler-43aedaa8cf3cbcb9b1d7d4815e5db3e223dac417.tar.xz third_party-cobbler-43aedaa8cf3cbcb9b1d7d4815e5db3e223dac417.zip |
Commit Al Tobey's auth patches to the Web UI.
-rw-r--r-- | cobbler/webui/CobblerWeb.py | 78 | ||||
-rw-r--r-- | cobbler/webui/master.py | 72 | ||||
-rwxr-xr-x | scripts/cobbler_webui.cgi | 85 | ||||
-rw-r--r-- | setup.py | 1 | ||||
-rw-r--r-- | webui_templates/master.tmpl | 16 |
5 files changed, 195 insertions, 57 deletions
diff --git a/cobbler/webui/CobblerWeb.py b/cobbler/webui/CobblerWeb.py index 268c911..3d5d446 100644 --- a/cobbler/webui/CobblerWeb.py +++ b/cobbler/webui/CobblerWeb.py @@ -18,6 +18,9 @@ import string from cobbler.utils import * import logging import sys +import Cookie + +# set up logging logger = logging.getLogger("cobbler.webui") logger.setLevel(logging.DEBUG) @@ -28,13 +31,14 @@ ch.setFormatter(formatter) logger.addHandler(ch) def log_exc(): + """ + Log active traceback to logfile. + """ (t, v, tb) = sys.exc_info() logger.info("Exception occured: %s" % t ) logger.info("Exception value: %s" % v) logger.info("Exception Info:\n%s" % string.join(traceback.format_list(traceback.extract_tb(tb)))) - - class CobblerWeb(object): """ The Cobbler web interface uses a MVC-style pattern. This is the model. @@ -42,17 +46,15 @@ class CobblerWeb(object): it all run either under cgi-bin or CherryPy. Supporting other Python frameworks should be trivial. """ - def __init__(self, server=None, base_url='/', username=None, password=None): + def __init__(self, server=None, base_url='/', username=None, password=None, token=None, token_cookie_name='cobbler_xmlrpc_token'): self.server = server self.base_url = base_url self.remote = None - self.token = None - - if username is None or password is None: - raise CobblerWebAuthException( "Must provide username and password for Cobbler r/w XMLRPC Interface!" ) - + self.token = token self.username = username self.password = password + self.token_cookie_name = token_cookie_name + self.logout = None def __xmlrpc_setup(self): """ @@ -62,8 +64,11 @@ class CobblerWeb(object): """ if self.remote is None: self.remote = xmlrpclib.Server(self.server, allow_none=True) + if self.token is None: self.token = self.remote.login( self.username, self.password ) + self.password = None # don't need it anymore, get rid of it + return self.remote def __render(self, template, data): @@ -73,7 +78,15 @@ class CobblerWeb(object): """ try: data['base_url'] = self.base_url - #filepath = "%s/%s" % (os.path.dirname(__file__), template) + + # used by master.tmpl to determine whether or not to show login/logout links + if self.token: + data['logged_in'] = 1 + elif self.username and self.password: + data['logged_in'] = 'configured' + else: + data['logged_in'] = None + filepath = os.path.join("/usr/share/cobbler/webui_templates/",template) tmpl = Template( file=filepath, searchList=data ) return str(tmpl) @@ -81,6 +94,23 @@ class CobblerWeb(object): log_exc() return self.error_page("Error while rendering page. See /var/log/cobbler/webui.log") + def cookies(self): + """ + Returns a Cookie.SimpleCookie object with all of CobblerWeb's cookies. + Mmmmm cookies! + """ + cookies = Cookie.SimpleCookie() + + if self.logout: + cookies[self.token_cookie_name] = "null" + cookies[self.token_cookie_name]['expires'] = 0 + self.logout = None + + elif self.token: + cookies[self.token_cookie_name] = self.token + + return cookies + def modes(self): """ Returns a list of methods in this object that can be run as web @@ -102,6 +132,31 @@ class CobblerWeb(object): return self.__render( 'index.tmpl', dict() ) # ------------------------------------------------------------------------ # + # Authentication + # ------------------------------------------------------------------------ # + + def login(self, message=None): + return self.__render( 'login.tmpl', {'message': message} ) + + def login_submit(self, username=None, password=None, submit=None): + if username is None: + return self.error_page( "No username supplied." ) + if password is None: + return self.error_page( "No password supplied." ) + + self.username = username + self.password = password + + self.__xmlrpc_setup() + + return self.index() + + def logout_submit(self): + self.token = None + self.logout = 1 + return self.login() + + # ------------------------------------------------------------------------ # # Settings # ------------------------------------------------------------------------ # @@ -448,6 +503,11 @@ class CobblerWeb(object): error_page.exposed = False index.exposed = True + login.exposed = True + login_submit.exposed = True + logout_submit.exposed = True + cookies.exposed = False + distro_edit.exposed = True distro_list.exposed = True distro_save.exposed = True diff --git a/cobbler/webui/master.py b/cobbler/webui/master.py index cec5a67..017da01 100644 --- a/cobbler/webui/master.py +++ b/cobbler/webui/master.py @@ -33,10 +33,10 @@ VFN=valueForName currentTime=time.time __CHEETAH_version__ = '2.0rc8' __CHEETAH_versionTuple__ = (2, 0, 0, 'candidate', 8) -__CHEETAH_genTime__ = 1189803313.957711 -__CHEETAH_genTimestamp__ = 'Fri Sep 14 16:55:13 2007' +__CHEETAH_genTime__ = 1189812484.1640699 +__CHEETAH_genTimestamp__ = 'Fri Sep 14 16:28:04 2007' __CHEETAH_src__ = 'webui_templates/master.tmpl' -__CHEETAH_srcLastModified__ = 'Fri Sep 14 14:19:18 2007' +__CHEETAH_srcLastModified__ = 'Fri Sep 14 16:27:47 2007' __CHEETAH_docstring__ = 'Autogenerated by CHEETAH: The Python-Powered Template Engine' if __CHEETAH_versionTuple__ < RequiredCheetahVersionTuple: @@ -69,7 +69,7 @@ class master(Template): - ## CHEETAH: generated from #block body at line 53, col 1. + ## CHEETAH: generated from #block body at line 55, col 1. trans = KWS.get("trans") if (not trans and not self._CHEETAH__isBuffering and not callable(self.transaction)): trans = self.transaction # is None unless self.awake() was called @@ -144,58 +144,68 @@ class master(Template): <div id="sidebar"> <ul id="nav"> - <li> - <select name="cobbler_server" style="text-transform: none;"> - <option value="http://localhost:25151">http://localhost:25151</option> - </select> - </li> - <li><hr/></li> <li><a href="''') - _v = VFFSL(SL,"base_url",True) # '$base_url' on line 33, col 22 - if _v is not None: write(_filter(_v, rawExpr='$base_url')) # from line 33, col 22. + _v = VFFSL(SL,"base_url",True) # '$base_url' on line 27, col 22 + if _v is not None: write(_filter(_v, rawExpr='$base_url')) # from line 27, col 22. write('''/settings_view" class="menu">Settings</a></li> <li><hr/></li> <li>LIST</li> <li><a href="''') - _v = VFFSL(SL,"base_url",True) # '$base_url' on line 36, col 22 - if _v is not None: write(_filter(_v, rawExpr='$base_url')) # from line 36, col 22. + _v = VFFSL(SL,"base_url",True) # '$base_url' on line 30, col 22 + if _v is not None: write(_filter(_v, rawExpr='$base_url')) # from line 30, col 22. write('''/distro_list" class="menu">Distros</a></li> <li><a href="''') - _v = VFFSL(SL,"base_url",True) # '$base_url' on line 37, col 22 - if _v is not None: write(_filter(_v, rawExpr='$base_url')) # from line 37, col 22. + _v = VFFSL(SL,"base_url",True) # '$base_url' on line 31, col 22 + if _v is not None: write(_filter(_v, rawExpr='$base_url')) # from line 31, col 22. write('''/profile_list" class="menu">Profiles</a></li> <li><a href="''') - _v = VFFSL(SL,"base_url",True) # '$base_url' on line 38, col 22 - if _v is not None: write(_filter(_v, rawExpr='$base_url')) # from line 38, col 22. + _v = VFFSL(SL,"base_url",True) # '$base_url' on line 32, col 22 + if _v is not None: write(_filter(_v, rawExpr='$base_url')) # from line 32, col 22. write('''/system_list" class="menu">Systems</a></li> <li><a href="''') - _v = VFFSL(SL,"base_url",True) # '$base_url' on line 39, col 22 - if _v is not None: write(_filter(_v, rawExpr='$base_url')) # from line 39, col 22. + _v = VFFSL(SL,"base_url",True) # '$base_url' on line 33, col 22 + if _v is not None: write(_filter(_v, rawExpr='$base_url')) # from line 33, col 22. write('''/ksfile_list" class="menu">Kickstarts</a></li> <li><a href="''') - _v = VFFSL(SL,"base_url",True) # '$base_url' on line 40, col 22 - if _v is not None: write(_filter(_v, rawExpr='$base_url')) # from line 40, col 22. + _v = VFFSL(SL,"base_url",True) # '$base_url' on line 34, col 22 + if _v is not None: write(_filter(_v, rawExpr='$base_url')) # from line 34, col 22. write('''/repo_list" class="menu">Repos</a></li> <li><hr/></li> <li>ADD</li> <li><a href="''') - _v = VFFSL(SL,"base_url",True) # '$base_url' on line 43, col 22 - if _v is not None: write(_filter(_v, rawExpr='$base_url')) # from line 43, col 22. + _v = VFFSL(SL,"base_url",True) # '$base_url' on line 37, col 22 + if _v is not None: write(_filter(_v, rawExpr='$base_url')) # from line 37, col 22. write('''/distro_edit" class="menu">Distro</a></li> <li><a href="''') - _v = VFFSL(SL,"base_url",True) # '$base_url' on line 44, col 22 - if _v is not None: write(_filter(_v, rawExpr='$base_url')) # from line 44, col 22. + _v = VFFSL(SL,"base_url",True) # '$base_url' on line 38, col 22 + if _v is not None: write(_filter(_v, rawExpr='$base_url')) # from line 38, col 22. write('''/profile_edit" class="menu">Profile</a></li> <li><a href="''') - _v = VFFSL(SL,"base_url",True) # '$base_url' on line 45, col 22 - if _v is not None: write(_filter(_v, rawExpr='$base_url')) # from line 45, col 22. + _v = VFFSL(SL,"base_url",True) # '$base_url' on line 39, col 22 + if _v is not None: write(_filter(_v, rawExpr='$base_url')) # from line 39, col 22. write('''/system_edit" class="menu">System</a></li> <li><a href="''') - _v = VFFSL(SL,"base_url",True) # '$base_url' on line 46, col 22 - if _v is not None: write(_filter(_v, rawExpr='$base_url')) # from line 46, col 22. + _v = VFFSL(SL,"base_url",True) # '$base_url' on line 40, col 22 + if _v is not None: write(_filter(_v, rawExpr='$base_url')) # from line 40, col 22. write('''/repo_edit" class="menu">Repo</a></li> <li><hr/></li> - <li><Sync></li> +''') + if VFFSL(SL,"logged_in",True) != 'configured': # generated from line 42, col 10 + if VFFSL(SL,"logged_in",True): # generated from line 43, col 13 + write(''' <li><a href="''') + _v = VFFSL(SL,"base_url",True) # '$base_url' on line 44, col 30 + if _v is not None: write(_filter(_v, rawExpr='$base_url')) # from line 44, col 30. + write('''/logout_submit" class="menu">Log Out</a></li> +''') + else: # generated from line 45, col 13 + write(''' <li><a href="''') + _v = VFFSL(SL,"base_url",True) # '$base_url' on line 46, col 30 + if _v is not None: write(_filter(_v, rawExpr='$base_url')) # from line 46, col 30. + write('''/login" class="menu">Log In</a></li> +''') + write(''' <li><hr/></li> +''') + write(''' <li><Sync></li> </ul> </div> diff --git a/scripts/cobbler_webui.cgi b/scripts/cobbler_webui.cgi index a4ee8db..6e93a27 100755 --- a/scripts/cobbler_webui.cgi +++ b/scripts/cobbler_webui.cgi @@ -13,6 +13,7 @@ import cgi import cgitb +import Cookie import os import sys from cobbler.webui.CobblerWeb import CobblerWeb @@ -33,17 +34,50 @@ def map_modes(): def base_url(): return os.environ.get('SCRIPT_NAME', '') -def main(): +def configure(): + # FIXME: read a config file ... + config = { + 'token': None, + 'server': None, + 'base_url': None, + 'token_cookie_name': None, + 'username': None, + 'password': None, + 'cgitb_enabled': 0 + } + #config.username = 'testuser', + #config.password = 'llamas2007' - cgitb.enable() + # defaults + if config['server'] is None: + config['server'] = "http://localhost/cobbler_api_rw" - print "Content-type: text/html" - print + if config['base_url'] is None: + config['base_url'] = base_url() + + if config['token_cookie_name'] is None: + config['token_cookie_name'] = 'cobbler_xmlrpc_token' + + return config + +def main(): + content = "Something went wrong and I couldn't generate any content for you!" + cw_conf = configure() + path = map_modes() + form = cgi.parse() + cookies = Cookie.SimpleCookie(os.environ.get("HTTP_COOKIE","")) - path = map_modes() - form = cgi.parse() + # make cgitb enablement configurable + if cw_conf['cgitb_enabled'] == 1: + cgitb.enable() + cw_conf.pop('cgitb_enabled') - # ditch single-element arrays in the 'form' dictionary + # look for the token cookie and put it in the config dict if found + if cookies.has_key( cw_conf['token_cookie_name'] ): + cw_conf['token'] = cookies[ cw_conf['token_cookie_name'] ].value + + # exchnage single-element arrays in the 'form' dictionary for just that item + # so there isn't a ton of 'foo[0]' craziness where 'foo' should suffice # - may be bad for form elements that are sometimes lists and sometimes # single items for key,val in form.items(): @@ -51,10 +85,41 @@ def main(): if len(val) == 1: form[key] = val[0] - cw = CobblerWeb( server="http://localhost/cobbler_api_rw", base_url=base_url(), username='testuser', password='llamas2007' ) + # instantiate a CobblerWeb object + cw = CobblerWeb( **cw_conf ) + + if not path.startswith('login') and (cw_conf['token'] is None and (cw_conf['username'] is None or cw_conf['password'] is None)): + func = getattr( cw, 'login' ) + content = func( message="Authentication Required." ) - if path in cw.modes(): + # check for a valid path/mode + elif path in cw.modes(): func = getattr( cw, path ) - print func( **form ) + try: + content = func( **form ) + # handle failed authentication gracefully + except Exception, e: + if str(e).find('login failed:') > 0: + func = getattr( cw, 'login' ) + content = func( message="Authentication failed." ) + # everything else is a bug? + else: + raise e + + # handle invalid paths gracefully + else: + func = getattr( cw, 'error_page' ) + content = func( "Invalid Mode: \"%s\"" % path ) + + # finally, get any cookies generated by the CobblerWeb object + cookie_header = cw.cookies().output() + if cookie_header: + print cookie_header + + # deliver content + print "Content-type: text/html" + print + print content main() + @@ -135,6 +135,7 @@ if __name__ == "__main__": (wwwtmpl, ['webui_templates/master.tmpl']), (wwwtmpl, ['webui_templates/item.tmpl']), (wwwtmpl, ['webui_templates/index.tmpl']), + (wwwtmpl, ['webui_templates/login.tmpl']), # Web UI kickstart file editing (wwwtmpl, ['webui_templates/ksfile_edit.tmpl']), diff --git a/webui_templates/master.tmpl b/webui_templates/master.tmpl index 0e73c03..54f7469 100644 --- a/webui_templates/master.tmpl +++ b/webui_templates/master.tmpl @@ -24,12 +24,6 @@ <div id="sidebar"> <ul id="nav"> - <li> - <select name="cobbler_server" style="text-transform: none;"> - <option value="http://localhost:25151">http://localhost:25151</option> - </select> - </li> - <li><hr/></li> <li><a href="$base_url/settings_view" class="menu">Settings</a></li> <li><hr/></li> <li>LIST</li> @@ -45,7 +39,15 @@ <li><a href="$base_url/system_edit" class="menu">System</a></li> <li><a href="$base_url/repo_edit" class="menu">Repo</a></li> <li><hr/></li> - <li><Sync></li> + #if $logged_in != 'configured' + #if $logged_in + <li><a href="$base_url/logout_submit" class="menu">Log Out</a></li> + #else + <li><a href="$base_url/login" class="menu">Log In</a></li> + #end if + <li><hr/></li> + #end if + <li><Sync></li> </ul> </div> |