Ir al contenido principal

Ralsina.Me — El sitio web de Roberto Alsina

Authenticated Pages in CherryPy

Cher­ryPy is a cool, python­ic, sim­ple, quick, fun way to write web ap­pli­ca­tion­s.

I of­ten use Cher­ryPy to write cus­tom web ad­min tools for cus­tomer­s. Sup­pose you want to pro­vide them with a sim­ple way for pass­word man­age­men­t. Usu­al­ly I have the fol­low­ing re­quire­ments:

  • It must be sim­­ple ( not we­b­min )

  • It must not be a ter­mi­­nal ses­­sion (bye ssh :-( )

  • It must not be a graph­i­­cal ses­­sion (sad­­ly, that leaves out PyQt :-( )

  • It needs to do cus­­tom stuff: set the sam­­ba pass­­word at the same time, send a mail warn­ing about the next forced change, what­ev­er.

Some­day I may be able to use a sin­gle-app freeNX ses­sion, but right now that's a bit too much prob­lem for dif­fer­ent rea­son­s.

So, I wrote a Cher­ryPy page. Over time, I have be­come quite fond of it, and wrote a bunch of small tools around it. One of them was a way to lo­gin the us­er in­to the site us­ing the sys­tem's users and pass­word­s. Now I got to throw it away :-)

The new Cher­ryPy 2.1 has a mech­a­nism for im­ple­ment­ing pass­word-pro­tect­ed pages, called the Ses­sion au­then­ti­cate fil­ter which is sad­ly not doc­u­ment­ed yet any­where I can find.

So, here is my at­temp­t, so peo­ple googling it up can use it. Ex­cuse me:

cher­rypy ses­sio­n­au­then­ti­cate­filter cher­rypy ses­sio­n­au­then­ti­cate­filter cher­rypy ses­sio­n­au­then­ti­cate­filter cher­rypy ses­sio­n­au­then­ti­cate­filter cher­rypy ses­sio­n­au­then­ti­cate­filter cher­rypy ses­sio­n­au­then­ti­cate­filter cher­rypy ses­sio­n­au­then­ti­cate­filter cher­rypy ses­sio­n­au­then­ti­cate­filter cher­rypy ses­sio­n­au­then­ti­cate­filter cher­rypy ses­sio­n­au­then­ti­cate­filter

That should do it :-)

What you need first is a func­tion that takes a user­name and pass­word, and re­turns None on suc­cess, or an er­ror mes­sage for the fail­ure.

For example, I can adapt something I wrote earlier using check­pass­word-­pam

def validPass(name,password):
                cmd='/usr/bin/checkpassword-pam -s xdm -- /bin/true 3<&0'
                p=os.popen(cmd,'w')
                s='%s\000%s\000xxx\000'%(name,password)
                print cmd,s
                p.write(s)
                r=p.close()
                if r==None: #Success
                        return None
                else:
        return "Login Incorrect"

Al­so, you may want a func­tion that re­turns the lo­gin screen. If you do, re­mem­ber the fol­low­ing:

  1. It must set the form ac­tion to doLo­gin

  2. The us­er field should be called lo­gin

  3. The pass­word field should be called pass­word

  4. You will take a fromPage ar­gu­ment that you should pass through, so the us­er will end on the page he wants.

  5. You will take a er­rorMsg ar­gu­ment which is prob­a­bly the re­sult of a failed pre­vi­ous lo­gin. Dis­play it red or some­thing like it. Un­less it's emp­ty, in which case it should not be vis­i­ble.

Here's mine.

def loginScreen(fromPage, login = '', errorMsg = ''):
content="""
<form method="post" action="doLogin">
<div align=center>
        <span class=errormsg>%s</span><p>
        <table >
        <tr>
        <td>
                Login:
        <td>
                <input type="text" name="login" value="%s" size="40"/>
        <tr>
        <td>
                Password:
        <td>
        <input type="password" name="password" size="40"/>
        <input type="hidden" name="fromPage" value="%s"/>
        <tr>
        <td colspan=2 align=right>
        <input type="submit" value="Login" />
        </table>
        </div>
</form>
""" % (errorMsg, login, fromPage)
title='Login'
return renderTemplate(file='logintemplate.html')

Al­though I am us­ing a tem­plate to dis­play it nice­ly and with the right style, it should be pret­ty ob­vi­ous how it work­s.

You could use the de­fault lo­gin screen pro­vid­ed by the fil­ter. While it work­s, it's just ug­ly.

Then you need to ap­ply the fil­ter to the set of your pass-pro­tect­ed pages. Sup­pose you want the whole site to be pro­tect­ed, ex­cept for your /stat­ic di­rec­to­ry, which con­tains the stylesheet, im­ages and such. Then you put this in your con­fig­u­ra­tion file:

[/]
sessionAuthenticateFilter.on=True

[/static]
sessionAuthenticateFilter.on=False

Next thing is to hook the ses­sion au­then­ti­cate fil­ter to your cus­tom auth code. In your ap­p, do the fol­low­ing. It seems that you can't do this in the con­fig file, though, so do it in code.

    settings={
            '/': {
                                    'sessionAuthenticateFilter.checkLoginAndPassword': validPass,
                'sessionAuthenticateFilter.loginScreen':loginScreen
        }
}
cherrypy.config.update(settings)

And that's it. Now your site is pass­word pro­tect­ed. You can even have dif­fer­ent au­then­ti­ca­tion schemes for dif­fer­ent pieces of the site, by re­set­ting the hooks for the fold­er you pre­fer to a dif­fer­ent func­tion.

Al­so, I think this is a good ex­am­ple of why I like Cher­ryPy. This mech­a­nism is both flex­i­ble, pow­er­ful and sim­ple.

Kurt Pfeifle / 2006-04-04 01:26:

"Someday I may be able to use a single-app freeNX session, but right now that's a bit too much problem for different reasons."

----



Hey, can you imagine that I'd be curious about details on the different reasons for this stand? ;-)



Either mail me, or respond here, as you like...



Cheers,

Kurt

Roberto Alsina / 2006-04-04 01:27:

Well, I tried again, and right now there are only two problems:



1. The kind of users who get to use this are scary in their computer-using habits, and may destroy any app that's not a web page. ;-)



2. I can't make freenx work with password-based auth.



I don't even know if 2 is supposed to work ;-)


Contents © 2000-2024 Roberto Alsina