summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael DeHaan <mdehaan@mdehaan.rdu.redhat.com>2007-09-17 14:58:05 -0400
committerMichael DeHaan <mdehaan@mdehaan.rdu.redhat.com>2007-09-17 14:58:05 -0400
commit43aedaa8cf3cbcb9b1d7d4815e5db3e223dac417 (patch)
tree0975e4e3f8edfeb93c9ac8bee47c743e59e1a778
parent4d391978ad2c5081a8d884c30c4269ca518824a7 (diff)
downloadthird_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.py78
-rw-r--r--cobbler/webui/master.py72
-rwxr-xr-xscripts/cobbler_webui.cgi85
-rw-r--r--setup.py1
-rw-r--r--webui_templates/master.tmpl16
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>&lt;Sync&gt;</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>&lt;Sync&gt;</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()
+
diff --git a/setup.py b/setup.py
index 29981a0..032a206 100644
--- a/setup.py
+++ b/setup.py
@@ -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>&lt;Sync&gt;</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>&lt;Sync&gt;</li>
</ul>
</div>