Well, as far as the delay and the blocking of the IP, I put this together, 
which would go somewhere in the /admin/models/access.py file, but I'd like 
to get some comments, as I've never coded this type of thing before so I'd 
like to know if there's a better way to code it, and what problems it might 
cause:

import os, pickle, time 
deny_file = os.path.join(request.folder, 'private', 'hosts.deny') 
denied_hosts = cache.ram('admin_denied_hosts', lambda: read_hosts_deny(), 
time_expire=3600) 

if request.client in denied_hosts: 
    if denied_hosts[request.client] >= 5: 
        raise HTTP(200, T(
'admin disabled because too many invalid password attempts')) 

def read_hosts_deny(): 
    if os.path.exists(deny_file): 
        with open(deny_file, 'rb') as f: 
            d = pickle.load(f) 
         
        return d 
    else: 
        return {} 
         
def write_hosts_deny(): 
    with open(deny_file, 'wb') as f: 
        pickle.dump(denied_hosts, f) 

def failed_login(): 
    if not request.is_local: 
        times_denied = 0 
        if request.client in denied_hosts: 
            times_denied = denied_hosts[request.client] + 1 
         
        denied_hosts[request.client] = times_denied 
        write_hosts_deny() 
             
        time.sleep(5) 
         
def successful_login(): 
    if not request.is_local: 
        if request.client in denied_hosts: 
            del denied_hosts[request.client] 
            write_hosts_deny() 
             
             
#################################################### 
# Then in /admin/controllers/default.py -> index() # 
#################################################### 

def index(): 
    """ Index handler """ 

    send = request.vars.send 
    if DEMO_MODE: 
        session.authorized = True 
        session.last_time = t0 
    if not send: 
        send = URL('site') 
    if session.authorized: 
        redirect(send) 
    elif request.vars.password: 
        if verify_password(request.vars.password): 
            session.authorized = True 
            ### ADDED THE FOLLOWING LINE ### 
            successful_login() 
            ################################ 

            if CHECK_VERSION: 
                session.check_version = True 
            else: 
                session.check_version = False 

            session.last_time = t0 
            if isinstance(send, list):  # ## why does this happen? 
                send = str(send[0]) 

            redirect(send) 
        else: 
            response.flash = T('invalid password') 
            #### ADDED THE FOLLOWING LINE ### 
            failed_login() 
            ################################# 

    return dict(send=send)

This adds a 5 second delay to a failed login attempt, adds the IP address to 
the denied_hosts dictionary, along with the number of failed attempts. This 
dictionary is cached in RAM and is written to /admin/private/hosts.deny to 
maintain the list after a restart. Once a login is successful, the failed 
attempt counter is reset to zero.

Reply via email to