Changeset - 9db7782727b3
[Not reviewed]
default
0 5 0
Marcin Kuzminski - 16 years ago 2010-04-07 19:39:31
marcin@python-blog.com
Static files for production fixed
Error handler for debug on, added
admin auth function authenticates only admins
changed creation of db
5 files changed with 33 insertions and 14 deletions:
0 comments (0 inline, 0 general)
development.ini
Show inline comments
 
################################################################################
 
################################################################################
 
# pylons_app - Pylons environment configuration                                #
 
#                                                                              # 
 
# The %(here)s variable will be replaced with the parent directory of this file#
 
################################################################################
 

	
 
[DEFAULT]
 
debug = true
 
############################################
 
## Uncomment and replace with the address ##
 
## which should receive any error reports ##
 
############################################
 
#email_to = marcin.kuzminski@etelko.pl
 
#smtp_server = mail.etelko.pl
 
#error_email_from = paste_error@localhost
 
#smtp_username = 
 
#smtp_password = 
 
#error_message = 'mercurial crash !'
 

	
 
[server:main]
 
use = egg:Paste#http
 
host = 127.0.0.1
 
port = 5000
 

	
 
[app:main]
 
use = egg:pylons_app
 
full_stack = true
 
static_files = true
 
lang=en
 
cache_dir = %(here)s/data
 
repos_name = etelko
 

	
 
################################################################################
 
## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT*  ##
 
## Debug mode will enable the interactive debugging tool, allowing ANYONE to  ##
 
## execute malicious code after an exception is raised.                       ##
 
################################################################################
 
#set debug = false
 

	
 

	
 
################################
 
### LOGGING CONFIGURATION   ####
 
################################
 
[loggers]
 
keys = root, routes, pylons_app, sqlalchemy
 

	
 
[handlers]
 
keys = console
 

	
 
[formatters]
 
keys = generic
 

	
 
#############
 
## LOGGERS ##
 
#############
 
[logger_root]
 
level = NOTSET
 
handlers = console
 

	
 
[logger_routes]
 
level = INFO
 
handlers = console
 
qualname = routes.middleware
 
# "level = DEBUG" logs the route matched and routing variables.
 

	
 
[logger_pylons_app]
 
level = DEBUG
 
handlers = console
 
qualname = pylons_app
 

	
 

	
 
[logger_sqlalchemy]
 
level = DEBUG
 
handlers = console
 
qualname = sqlalchemy.engine
 

	
 
##############
 
## HANDLERS ##
 
##############
 

	
 
[handler_console]
 
class = StreamHandler
 
args = (sys.stderr,)
 
level = NOTSET
 
formatter = generic
 

	
 
################
 
## FORMATTERS ##
 
################
 

	
 
[formatter_generic]
 
format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
 
datefmt = %H:%M:%S
 
datefmt = %Y-%m-%d %H:%M:%S
 

	
production.ini
Show inline comments
 
################################################################################
 
################################################################################
 
# pylons_app - Pylons environment configuration                                #
 
#                                                                              # 
 
# The %(here)s variable will be replaced with the parent directory of this file#
 
################################################################################
 

	
 
[DEFAULT]
 
debug = true
 
############################################
 
## Uncomment and replace with the address ##
 
## which should receive any error reports ##
 
############################################
 
#email_to = marcin.kuzminski@etelko.pl
 
#smtp_server = mail.etelko.pl
 
#error_email_from = paste_error@localhost
 
#smtp_username = 
 
#smtp_password = 
 
#error_message = 'mercurial crash !'
 

	
 
[server:main]
 
use = egg:Paste#http
 
host = 127.0.0.1
 
port = 8001
 

	
 
[app:main]
 
use = egg:pylons_app
 
full_stack = true
 
static_files = false
 
static_files = true
 
lang=en
 
cache_dir = %(here)s/data
 
repos_name = etelko
 

	
 
################################################################################
 
## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT*  ##
 
## Debug mode will enable the interactive debugging tool, allowing ANYONE to  ##
 
## execute malicious code after an exception is raised.                       ##
 
################################################################################
 
#set debug = false
 

	
 
################################
 
### LOGGING CONFIGURATION   ####
 
################################
 
[loggers]
 
keys = root, routes, pylons_app, sqlalchemy
 

	
 
[handlers]
 
keys = console
 

	
 
[formatters]
 
keys = generic
 

	
 
#############
 
## LOGGERS ##
 
#############
 
[logger_root]
 
level = INFO
 
handlers = console
 

	
 
[logger_routes]
 
level = INFO
 
handlers = console
 
qualname = routes.middleware
 
# "level = DEBUG" logs the route matched and routing variables.
 

	
 
[logger_pylons_app]
 
level = DEBUG
 
handlers = console
 
qualname = pylons_app
 

	
 

	
 
[logger_sqlalchemy]
 
level = DEBUG
 
handlers = console
 
qualname = sqlalchemy.engine
 

	
 
##############
 
## HANDLERS ##
 
##############
 

	
 
[handler_console]
 
class = StreamHandler
 
args = (sys.stderr,)
 
level = NOTSET
 
formatter = generic
 

	
 
################
 
## FORMATTERS ##
 
################
 

	
 
[formatter_generic]
 
format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
 
datefmt = %H:%M:%S
 
datefmt = %Y-%m-%d %H:%M:%S
 

	
pylons_app/config/middleware.py
Show inline comments
 
"""Pylons middleware initialization"""
 
from beaker.middleware import SessionMiddleware
 
from paste.cascade import Cascade
 
from paste.registry import RegistryManager
 
from paste.urlparser import StaticURLParser
 
from paste.deploy.converters import asbool
 
from pylons.middleware import ErrorHandler, StatusCodeRedirect
 
from pylons.wsgiapp import PylonsApp
 
from routes.middleware import RoutesMiddleware
 
from paste.auth.basic import AuthBasicHandler
 
from pylons_app.config.environment import load_environment
 
from pylons_app.lib.auth import authfunc 
 

	
 
def make_app(global_conf, full_stack=True, static_files=True, **app_conf):
 
    """Create a Pylons WSGI application and return it
 

	
 
    ``global_conf``
 
        The inherited configuration for this application. Normally from
 
        the [DEFAULT] section of the Paste ini file.
 

	
 
    ``full_stack``
 
        Whether or not this application provides a full WSGI stack (by
 
        default, meaning it handles its own exceptions and errors).
 
        Disable full_stack when this application is "managed" by
 
        another WSGI middleware.
 

	
 
    ``app_conf``
 
        The application's local configuration. Normally specified in
 
        the [app:<name>] section of the Paste ini file (where <name>
 
        defaults to main).
 

	
 
    """
 
    # Configure the Pylons environment
 
    config = load_environment(global_conf, app_conf)
 

	
 

	
 
    # The Pylons WSGI app
 
    app = PylonsApp(config=config)
 

	
 
    # CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)
 

	
 
    # Routing/Session/Cache Middleware
 
    app = RoutesMiddleware(app, config['routes.map'])
 
    app = SessionMiddleware(app, config)
 
    app = AuthBasicHandler(app, config['repos_name'] + ' mercurial repository', authfunc)
 
    
 
    if asbool(full_stack):
 
        # Handle Python exceptions
 
        app = ErrorHandler(app, global_conf, **config['pylons.errorware'])
 

	
 
        # Display error documents for 401, 403, 404 status codes (and
 
        # 500 when debug is disabled)
 
        if asbool(config['debug']):
 
            #don't handle 404, since mercurial does it for us.
 
            app = StatusCodeRedirect(app, [400, 401, 403])
 
            app = StatusCodeRedirect(app, [400, 401, 403, 500])
 
        else:
 
            app = StatusCodeRedirect(app, [400, 401, 403, 500])
 
    
 
    # Establish the Registry for this application
 
    app = RegistryManager(app)
 

	
 
    if asbool(static_files):
 
        # Serve static files
 
        static_app = StaticURLParser(config['pylons.paths']['static_files'])
 
        app = Cascade([static_app, app])
 
    
 
        app.config = config
 

	
 
    return app
 

	
pylons_app/controllers/admin.py
Show inline comments
 
import logging
 

	
 
from pylons import request, response, session, tmpl_context as c, url, app_globals as g
 
from pylons.controllers.util import abort, redirect
 

	
 
from pylons_app.lib.base import BaseController, render
 
import os
 
from mercurial import ui, hg
 
from mercurial.error import RepoError
 
from ConfigParser import ConfigParser
 
from pylons_app.lib import auth
 
from pylons_app.model.forms import LoginForm
 
import formencode
 
import formencode.htmlfill as htmlfill
 
log = logging.getLogger(__name__)
 

	
 
class AdminController(BaseController):
 

	
 

	
 
    def __before__(self):
 
        c.staticurl = g.statics
 
        c.admin_user = session.get('admin_user')
 
        c.admin_username = session.get('admin_username')
 
        
 
    def index(self):
 
        # Return a rendered template
 
        if request.POST:
 
            #import Login Form validator class
 
            login_form = LoginForm()
 

	
 
            try:
 
                c.form_result = login_form.to_python(dict(request.params))
 
                if auth.authfunc(None, c.form_result['username'], c.form_result['password']) and\
 
                    c.form_result['username'] == 'admin':
 
                if auth.admin_auth(c.form_result['username'], c.form_result['password']):
 
                    session['admin_user'] = True
 
                    session['admin_username'] = c.form_result['username']
 
                    session.save()
 
                    return redirect(url('admin_home'))
 
                else:
 
                    raise formencode.Invalid('Login Error', None, None,
 
                                             error_dict={'username':'invalid login',
 
                                                         'password':'invalid password'})
 
                                      
 
            except formencode.Invalid, error:
 
                c.form_result = error.value
 
                c.form_errors = error.error_dict or {}
 
                html = render('/admin.html')
 

	
 
                return htmlfill.render(
 
                    html,
 
                    defaults=c.form_result,
 
                    encoding="UTF-8"
 
                )
 
        return render('/admin.html')
 

	
 
    def repos_manage(self):
 
        return render('/repos_manage.html')
 
    
 
    def users_manage(self):
 
        conn, cur = auth.get_sqlite_conn_cur()
 
        cur.execute('SELECT * FROM users')
 
        c.users_list = cur.fetchall()        
 
        return render('/users_manage.html')
 
                
 
    def manage_hgrc(self):
 
        pass
 

	
 
    def hgrc(self, dirname):
 
        filename = os.path.join(dirname, '.hg', 'hgrc')
 
        return filename
 

	
 
    def add_repo(self, new_repo):
 
        
 

	
 
        #extra check it can be add since it's the command
 
        if new_repo == '_admin':
 
            c.msg = 'DENIED'
 
            c.new_repo = ''
 
            return render('add.html')
 

	
 
        new_repo = new_repo.replace(" ", "_")
 
        new_repo = new_repo.replace("-", "_")
 

	
 
        try:
 
            self._create_repo(new_repo)
 
            c.new_repo = new_repo
 
            c.msg = 'added repo'
 
        except Exception as e:
 
            c.new_repo = 'Exception when adding: %s' % new_repo
 
            c.msg = str(e)
 

	
 
        return render('add.html')
 

	
 
    def _check_repo(self, repo_name):
 
        p = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
 
        config_path = os.path.join(p, 'hgwebdir.config')
 

	
 
        cp = ConfigParser()
 

	
 
        cp.read(config_path)
 
        repos_path = cp.get('paths', '/').replace("**", '')
 

	
 
        if not repos_path:
 
            raise Exception('Could not read config !')
 

	
 
        self.repo_path = os.path.join(repos_path, repo_name)
 

	
 
        try:
 
            r = hg.repository(ui.ui(), self.repo_path)
 
            hg.verify(r)
 
            #here we hnow that repo exists it was verified
 
            log.info('%s repo is already created', repo_name)
 
            raise Exception('Repo exists')
 
        except RepoError:
 
            log.info('%s repo is free for creation', repo_name)
 
            #it means that there is no valid repo there...
 
            return True
 

	
 

	
 
    def _create_repo(self, repo_name):
 
        if repo_name in [None, '', 'add']:
 
            raise Exception('undefined repo_name of repo')
 

	
 
        if self._check_repo(repo_name):
 
            log.info('creating repo %s in %s', repo_name, self.repo_path)
 
            cmd = """mkdir %s && hg init %s""" \
 
                    % (self.repo_path, self.repo_path)
 
            os.popen(cmd)
pylons_app/lib/auth.py
Show inline comments
 
import sqlite3
 
import os
 
import logging
 
from os.path import dirname as dn
 
from datetime import datetime
 
import crypt
 

	
 
log = logging.getLogger(__name__)
 
ROOT = dn(dn(dn(os.path.realpath(__file__))))
 

	
 
def get_sqlite_conn_cur():
 
    conn = sqlite3.connect(os.path.join(ROOT, 'auth.sqlite'))
 
    cur = conn.cursor()
 
    return conn, cur
 

	
 

	
 
def admin_auth(username, password):
 
    conn, cur = get_sqlite_conn_cur()
 
    password_crypt = crypt.crypt(password, '6a')
 

	
 
    try:
 
        cur.execute("SELECT * FROM users WHERE username=?", (username,))
 
        data = cur.fetchone()
 
    except sqlite3.OperationalError as e:
 
        data = None
 
        log.error(e)
 
        
 
    if data:
 
        if data[3]:
 
            if data[1] == username and data[2] == password_crypt and data[4]:
 
                log.info('user %s authenticated correctly', username)
 
                return True
 
        else:
 
            log.error('user %s is disabled', username)
 
            
 
    return False
 

	
 
def authfunc(environ, username, password):
 
    conn, cur = get_sqlite_conn_cur()
 
    password_crypt = crypt.crypt(password, '6a')
 

	
 
    try:
 
        cur.execute("SELECT * FROM users WHERE username=?", (username,))
 
        data = cur.fetchone()
 
    except sqlite3.OperationalError as e:
 
        data = None
 
        log.error(e)
 
    if data:
 
        if data[3]:
 
            if data[1] == username and data[2] == password_crypt:
 
                log.info('user %s authenticated correctly', username)
 
                if environ:
 
                    http_accept = environ.get('HTTP_ACCEPT')
 
            
 
                    if http_accept.startswith('application/mercurial') or \
 
                        environ['PATH_INFO'].find('raw-file') != -1:
 
                        cmd = environ['PATH_INFO']
 
                        for qry in environ['QUERY_STRING'].split('&'):
 
                            if qry.startswith('cmd'):
 
                                cmd += "|" + qry
 
                                
 
                                try:
 
                                    cur.execute('''INSERT INTO 
 
                                                        user_logs 
 
                                                   VALUES(?,?,?,?)''',
 
                                                    (None, data[0], cmd, datetime.now()))
 
                                    conn.commit()
 
                                except Exception as e:
 
                                    conn.rollback()
 
                                    log.error(e)
 
                                  
 
                return True
 
        else:
 
            log.error('user %s is disabled', username)
 
            
 
    return False
 

	
 
def create_user_table():
 
    '''
 
    Create a auth database
 
    '''
 
    conn, cur = get_sqlite_conn_cur()
 
    try:
 
        log.info('creating table %s', 'users')
 
        cur.execute('''DROP TABLE IF EXISTS users ''')
 
        cur.execute('''CREATE TABLE users
 
                        (id INTEGER PRIMARY KEY AUTOINCREMENT, 
 
                         username TEXT, 
 
                         password TEXT,
 
                         active INTEGER)''')
 
                         active INTEGER,
 
                         admin INTEGER)''')
 
        log.info('creating table %s', 'user_logs')
 
        cur.execute('''DROP TABLE IF EXISTS user_logs ''')
 
        cur.execute('''CREATE TABLE user_logs
 
                        (id INTEGER PRIMARY KEY AUTOINCREMENT,
 
                            user_id INTEGER,
 
                            last_action TEXT, 
 
                            last_action_date DATETIME)''')
 
        conn.commit()
 
    except:
 
        conn.rollback()
 
        raise
 
    
 
    cur.close()
 
    
 
def create_user(username, password):
 
def create_user(username, password, admin=False):
 
    conn, cur = get_sqlite_conn_cur()    
 
    password_crypt = crypt.crypt(password, '6a')
 
    cur_date = datetime.now()
 
    log.info('creating user %s', username)
 
    try:
 
        cur.execute('''INSERT INTO users values (?,?,?,?) ''',
 
                    (None, username, password_crypt, 1,))     
 
        cur.execute('''INSERT INTO users values (?,?,?,?,?) ''',
 
                    (None, username, password_crypt, 1, admin))     
 
        conn.commit()
 
    except:
 
        conn.rollback()
 
        raise
 
    
 
if __name__ == "__main__":
 
    create_user_table()
 
    create_user('marcink', 'qweqwe')
 
    create_user('marcink', 'qweqwe', True)
 
    create_user('lukaszd', 'qweqwe')
 
    create_user('adriand', 'qweqwe')
 
    create_user('radek', 'qweqwe')
 
    create_user('skrzeka', 'qweqwe')
 
    create_user('bart', 'qweqwe')
 
    create_user('maho', 'qweqwe')
 
    create_user('michalg', 'qweqwe')
 
    create_user('admin', 'qwe123qwe')
 
    
 
    #authfunc('', 'marcink', 'qweqwe')
0 comments (0 inline, 0 general)