mirror of
https://github.com/kakwa/ldapcherry
synced 2024-11-22 09:24:21 +01:00
begin implementation of the main application
This commit is contained in:
parent
ff6e996912
commit
d6bb5c38ed
@ -96,7 +96,7 @@ auth.mode = 'or'
|
||||
# resources parameters
|
||||
[resources]
|
||||
# templates directory
|
||||
template_dir = '/usr/share/ldapcherry/templates/'
|
||||
templates.dir = '/usr/share/ldapcherry/templates/'
|
||||
|
||||
[/static]
|
||||
tools.staticdir.on = True
|
||||
|
@ -15,6 +15,8 @@ import logging.handlers
|
||||
from operator import itemgetter
|
||||
from socket import error as socket_error
|
||||
|
||||
from exceptions import *
|
||||
|
||||
#cherrypy http framework imports
|
||||
import cherrypy
|
||||
from cherrypy.lib.httputil import parse_query_string
|
||||
@ -25,8 +27,38 @@ from mako import lookup
|
||||
|
||||
SESSION_KEY = '_cp_username'
|
||||
|
||||
# Custom log function to overrige weird error.log function
|
||||
# of cherrypy
|
||||
def syslog_error(msg='', context='',
|
||||
severity=logging.INFO, traceback=False):
|
||||
if traceback:
|
||||
msg += cherrypy._cperror.format_exc()
|
||||
if context == '':
|
||||
cherrypy.log.error_log.log(severity, msg)
|
||||
else:
|
||||
cherrypy.log.error_log.log(severity,
|
||||
' '.join((context, msg)))
|
||||
|
||||
class LdapCherry(object):
|
||||
|
||||
def _handle_exception(self, e):
|
||||
if hasattr(e, 'log'):
|
||||
cherrypy.log.error(
|
||||
msg = e.log,
|
||||
severity = logging.ERROR
|
||||
)
|
||||
else:
|
||||
cherrypy.log.error(
|
||||
msg = "Unkwon exception <%(e)s>" % { 'e' : str(e) },
|
||||
severity = logging.ERROR
|
||||
)
|
||||
# log the traceback as 'debug'
|
||||
cherrypy.log.error(
|
||||
msg = '',
|
||||
severity = logging.DEBUG,
|
||||
traceback= True
|
||||
)
|
||||
|
||||
def _get_param(self, section, key, config, default=None):
|
||||
""" Get configuration parameter "key" from config
|
||||
@str section: the section of the config file
|
||||
@ -42,106 +74,99 @@ class LdapCherry(object):
|
||||
else:
|
||||
raise MissingParameter(section, key)
|
||||
|
||||
def _set_access_log(self, config, level):
|
||||
access_handler = self._get_param('global', 'log.access_handler', config, 'syslog')
|
||||
|
||||
# log format for syslog
|
||||
syslog_formatter = logging.Formatter(
|
||||
"ldapcherry[%(process)d]: %(message)s")
|
||||
|
||||
# replace access log handler by a syslog handler
|
||||
if access_handler == 'syslog':
|
||||
cherrypy.log.access_log.handlers = []
|
||||
handler = logging.handlers.SysLogHandler(address = '/dev/log',
|
||||
facility='user')
|
||||
handler.setFormatter(syslog_formatter)
|
||||
cherrypy.log.access_log.addHandler(handler)
|
||||
|
||||
# if file, we keep the default
|
||||
elif access_handler == 'file':
|
||||
pass
|
||||
|
||||
# replace access log handler by a null handler
|
||||
elif access_handler == 'none':
|
||||
cherrypy.log.access_log.handlers = []
|
||||
handler = logging.NullHandler()
|
||||
cherrypy.log.access_log.addHandler(handler)
|
||||
|
||||
# set log level
|
||||
cherrypy.log.access_log.setLevel(level)
|
||||
|
||||
def _set_error_log(self, config, level):
|
||||
error_handler = self._get_param('global', 'log.error_handler', config, 'syslog')
|
||||
|
||||
# log format for syslog
|
||||
syslog_formatter = logging.Formatter(
|
||||
"ldapcherry[%(process)d]: %(message)s")
|
||||
|
||||
# replacing the error handler by a syslog handler
|
||||
if error_handler == 'syslog':
|
||||
cherrypy.log.error_log.handlers = []
|
||||
|
||||
# redefining log.error method because cherrypy does weird
|
||||
# things like adding the date inside the message
|
||||
# or adding space even if context is empty
|
||||
# (by the way, what's the use of "context"?)
|
||||
cherrypy.log.error = syslog_error
|
||||
|
||||
handler = logging.handlers.SysLogHandler(address = '/dev/log',
|
||||
facility='user')
|
||||
handler.setFormatter(syslog_formatter)
|
||||
cherrypy.log.error_log.addHandler(handler)
|
||||
|
||||
# if file, we keep the default
|
||||
elif error_handler == 'file':
|
||||
pass
|
||||
|
||||
# replacing the error handler by a null handler
|
||||
elif error_handler == 'none':
|
||||
cherrypy.log.error_log.handlers = []
|
||||
handler = logging.NullHandler()
|
||||
cherrypy.log.error_log.addHandler(handler)
|
||||
|
||||
# set log level
|
||||
cherrypy.log.error_log.setLevel(level)
|
||||
|
||||
def reload(self, config = None):
|
||||
""" load/reload the configuration
|
||||
"""
|
||||
|
||||
try:
|
||||
# definition of the template directory
|
||||
self.template_dir = self._get_param('resources', 'template_dir', config)
|
||||
# log configuration handling
|
||||
# get log level
|
||||
# (if not in configuration file, log level is set to debug)
|
||||
level = self._get_loglevel(self._get_param('global', 'log.level', config, 'debug'))
|
||||
# configure access log
|
||||
self._set_access_log(config, level)
|
||||
# configure error log
|
||||
self._set_error_log(config, level)
|
||||
|
||||
# log format for syslog
|
||||
syslog_formatter = logging.Formatter(
|
||||
"ldapcherry[%(process)d]: %(message)s")
|
||||
|
||||
access_handler = self._get_param('global', 'log.access_handler', config, 'syslog')
|
||||
|
||||
# replace access log handler by a syslog handler
|
||||
if access_handler == 'syslog':
|
||||
cherrypy.log.access_log.handlers = []
|
||||
handler = logging.handlers.SysLogHandler(address = '/dev/log',
|
||||
facility='user')
|
||||
handler.setFormatter(syslog_formatter)
|
||||
cherrypy.log.access_log.addHandler(handler)
|
||||
|
||||
# if file, we keep the default
|
||||
elif access_handler == 'file':
|
||||
pass
|
||||
|
||||
# replace access log handler by a null handler
|
||||
elif access_handler == 'none':
|
||||
cherrypy.log.access_log.handlers = []
|
||||
handler = logging.NullHandler()
|
||||
cherrypy.log.access_log.addHandler(handler)
|
||||
|
||||
error_handler = self._get_param('global', 'log.error_handler', config, 'syslog')
|
||||
|
||||
# replacing the error handler by a syslog handler
|
||||
if error_handler == 'syslog':
|
||||
cherrypy.log.error_log.handlers = []
|
||||
|
||||
# redefining log.error method because cherrypy does weird
|
||||
# things like adding the date inside the message
|
||||
# or adding space even if context is empty
|
||||
# (by the way, what's the use of "context"?)
|
||||
def syslog_error(msg='', context='',
|
||||
severity=logging.INFO, traceback=False):
|
||||
if traceback:
|
||||
msg += cherrypy._cperror.format_exc()
|
||||
if context == '':
|
||||
cherrypy.log.error_log.log(severity, msg)
|
||||
else:
|
||||
cherrypy.log.error_log.log(severity,
|
||||
' '.join((context, msg)))
|
||||
cherrypy.log.error = syslog_error
|
||||
|
||||
handler = logging.handlers.SysLogHandler(address = '/dev/log',
|
||||
facility='user')
|
||||
handler.setFormatter(syslog_formatter)
|
||||
cherrypy.log.error_log.addHandler(handler)
|
||||
|
||||
# if file, we keep the default
|
||||
elif error_handler == 'file':
|
||||
pass
|
||||
|
||||
# replacing the error handler by a null handler
|
||||
elif error_handler == 'none':
|
||||
cherrypy.log.error_log.handlers = []
|
||||
handler = logging.NullHandler()
|
||||
cherrypy.log.error_log.addHandler(handler)
|
||||
|
||||
# set log level
|
||||
cherrypy.log.error_log.setLevel(level)
|
||||
cherrypy.log.access_log.setLevel(level)
|
||||
|
||||
# definition of the template directory
|
||||
self.template_dir = self._get_param('resources', 'templates.dir', config)
|
||||
# preload templates
|
||||
self.temp_lookup = lookup.TemplateLookup(
|
||||
directories=self.template_dir, input_encoding='utf-8'
|
||||
)
|
||||
self.temp_index = self.temp_lookup.get_template('index.tmpl')
|
||||
self.temp_result = self.temp_lookup.get_template('result.tmpl')
|
||||
self.temp_error = self.temp_lookup.get_template('error.tmpl')
|
||||
self.temp_login = self.temp_lookup.get_template('login.tmpl')
|
||||
|
||||
# loading the authentification module
|
||||
auth_module = self._get_param('auth', 'auth.module', config)
|
||||
auth = __import__(auth_module, globals(), locals(), ['Auth'], -1)
|
||||
self.auth = auth.Auth(config['auth'], cherrypy.log)
|
||||
#auth_module = self._get_param('auth', 'auth.module', config)
|
||||
#auth = __import__(auth_module, globals(), locals(), ['Auth'], -1)
|
||||
#self.auth = auth.Auth(config['auth'], cherrypy.log)
|
||||
|
||||
except MissingParameter as e:
|
||||
cherrypy.log.error(
|
||||
msg = "ldapcherry failure, "\
|
||||
"missing parameter '%(param)s' "\
|
||||
"in section '%(section)s'" % {
|
||||
'param': e.key,
|
||||
'section': e.section
|
||||
},
|
||||
severity = logging.ERROR
|
||||
)
|
||||
except Exception as e:
|
||||
self._handle_exception(e)
|
||||
exit(1)
|
||||
|
||||
def _get_loglevel(self, level):
|
||||
@ -177,13 +202,6 @@ class LdapCherry(object):
|
||||
and returns the right error page and emits a log
|
||||
"""
|
||||
|
||||
# log the traceback as 'debug'
|
||||
cherrypy.log.error(
|
||||
msg = '',
|
||||
severity = logging.DEBUG,
|
||||
traceback= True
|
||||
)
|
||||
|
||||
# log and error page handling
|
||||
def render_error(alert, message):
|
||||
if alert == 'danger':
|
||||
|
@ -39,6 +39,13 @@ class MissingRolesFile(Exception):
|
||||
self.rolefile = rolefile
|
||||
self.log = "fail to open role file <%(rolefile)s>" % { 'rolefile' : rolefile}
|
||||
|
||||
class MissingMainFile(Exception):
|
||||
def __init__(self, config):
|
||||
self.rolefile = rolefile
|
||||
self.log = "fail to open main file <%(config)s>" % { 'rolefile' : rolefile}
|
||||
|
||||
|
||||
|
||||
class MissingAttributesFile(Exception):
|
||||
def __init__(self, attributesfile):
|
||||
self.attributesfile = attributesfile
|
||||
|
15
resources/templates/error.tmpl
Normal file
15
resources/templates/error.tmpl
Normal file
@ -0,0 +1,15 @@
|
||||
<%inherit file="navbar.tmpl"/>
|
||||
<%block name="core">
|
||||
<div class="row clearfix">
|
||||
<div class="col-md-12 column">
|
||||
<div class="alert alert-dismissable alert-${alert}">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||
<h4>
|
||||
${message}
|
||||
</h4>
|
||||
</div>
|
||||
<a class="btn btn-default blue" href='/?zone=${current_zone}'><span class="glyphicon glyphicon-home"> Return</a>
|
||||
</div>
|
||||
</div>
|
||||
</%block>
|
||||
|
29
resources/templates/login.tmpl
Normal file
29
resources/templates/login.tmpl
Normal file
@ -0,0 +1,29 @@
|
||||
<%inherit file="base.tmpl"/>
|
||||
<%block name="core">
|
||||
<div class="row clearfix" style="margin-top:30px">
|
||||
<div class="col-md-4 column"></div>
|
||||
<div class="col-md-4 column well">
|
||||
<form method='POST' action='/login' role="form" class="form-signin">
|
||||
<div class="form-group">
|
||||
<h2 class="form-signin-heading">Please sign in</h2>
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon glyphicon glyphicon-user"></span>
|
||||
<input type="text" class="form-control" name="login" placeholder="Login" required autofocus>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon glyphicon glyphicon-lock"></span>
|
||||
<input type="password" class="form-control" name="password" placeholder="Password" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<button class="btn btn-default blue" type="submit"><span class="glyphicon glyphicon-off"> Sign in</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-4 column"></div>
|
||||
</div>
|
||||
</%block>
|
103
tests/cfg/ldapcherry.ini
Normal file
103
tests/cfg/ldapcherry.ini
Normal file
@ -0,0 +1,103 @@
|
||||
# global parameters
|
||||
[global]
|
||||
|
||||
# listing interface
|
||||
server.socket_host = '127.0.0.1'
|
||||
# port
|
||||
server.socket_port = 8080
|
||||
# number of threads
|
||||
server.thread_pool = 8
|
||||
#don't show traceback on error
|
||||
request.show_tracebacks = False
|
||||
|
||||
# log configuration
|
||||
# /!\ you can't have multiple log handlers
|
||||
#####################################
|
||||
# configuration to log in files #
|
||||
#####################################
|
||||
## logger 'file' for access log
|
||||
#log.access_handler = 'file'
|
||||
## logger syslog for error and ldapcherry log
|
||||
#log.error_handler = 'file'
|
||||
## access log file
|
||||
#log.access_file = '/tmp/ldapcherry_access.log'
|
||||
## error and ldapcherry log file
|
||||
#log.error_file = '/tmp/ldapcherry_error.log'
|
||||
|
||||
#####################################
|
||||
# configuration to log in syslog #
|
||||
#####################################
|
||||
# logger syslog for access log
|
||||
#log.access_handler = 'syslog'
|
||||
## logger syslog for error and ldapcherry log
|
||||
log.error_handler = 'syslog'
|
||||
|
||||
#####################################
|
||||
# configuration to not log at all #
|
||||
#####################################
|
||||
# logger none for access log
|
||||
log.access_handler = 'none'
|
||||
# logger none for error and ldapcherry log
|
||||
#log.error_handler = 'none'
|
||||
|
||||
# log level
|
||||
log.level = 'info'
|
||||
|
||||
# session configuration
|
||||
# activate session
|
||||
tools.sessions.on = True
|
||||
# session timeout
|
||||
tools.sessions.timeout = 10
|
||||
# file session storage(to use if multiple processes,
|
||||
# default is in RAM and per process)
|
||||
#tools.sessions.storage_type = "file"
|
||||
# session
|
||||
#tools.sessions.storage_path = "/var/lib/ldapcherry/sessions"
|
||||
|
||||
[attributes]
|
||||
|
||||
# file discribing form content
|
||||
attributes.file = '/etc/ldapcherry/attributes.yml'
|
||||
|
||||
[roles]
|
||||
|
||||
# file listing roles
|
||||
roles.file = '/etc/ldapcherry/roles.yml'
|
||||
|
||||
[backends]
|
||||
|
||||
ldap.module = 'ldapcherry.backends.ldap'
|
||||
ldap.groupdn = 'ou=group,dc=example,dc=com'
|
||||
ldap.people = 'ou=group,dc=example,dc=com'
|
||||
ldap.authdn = 'cn=ldapcherry,dc=example,dc=com'
|
||||
ldap.password = 'password'
|
||||
ldap.uri = 'ldaps://ldap.ldapcherry.org'
|
||||
ldap.ca = '/etc/dnscherry/TEST-cacert.pem'
|
||||
ldap.starttls = 'on'
|
||||
ldap.checkcert = 'off'
|
||||
|
||||
ad.module = 'ldapcherry.backends.ad'
|
||||
ad.auth = 'Administrator'
|
||||
ad.password = 'password'
|
||||
|
||||
# authentification parameters
|
||||
[auth]
|
||||
|
||||
# Auth mode
|
||||
# * and: user must authenticate on all backends
|
||||
# * or: user must authenticate on one of the backend
|
||||
# * none: disable authentification
|
||||
# * custom: custom authentification module (need auth.module param)
|
||||
auth.mode = 'or'
|
||||
|
||||
# custom auth module to load
|
||||
#auth.module = 'ldapcherry.auth.modNone'
|
||||
|
||||
# resources parameters
|
||||
[resources]
|
||||
# templates directory
|
||||
templates.dir = '/usr/share/ldapcherry/templates/'
|
||||
|
||||
[/static]
|
||||
tools.staticdir.on = True
|
||||
tools.staticdir.dir = '/usr/share/ldapcherry/static/'
|
51
tests/test_LdapCherry.py
Normal file
51
tests/test_LdapCherry.py
Normal file
@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import with_statement
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import pytest
|
||||
import sys
|
||||
from sets import Set
|
||||
from ldapcherry import LdapCherry
|
||||
from ldapcherry.exceptions import DumplicateRoleKey, MissingKey, DumplicateRoleContent, MissingRolesFile, MissingRole
|
||||
from ldapcherry.pyyamlwrapper import DumplicatedKey, RelationError
|
||||
import cherrypy
|
||||
from cherrypy.process import plugins, servers
|
||||
from cherrypy import Application
|
||||
|
||||
# monkey patching cherrypy to disable config interpolation
|
||||
def new_as_dict(self, raw=True, vars=None):
|
||||
"""Convert an INI file to a dictionary"""
|
||||
# Load INI file into a dict
|
||||
result = {}
|
||||
for section in self.sections():
|
||||
if section not in result:
|
||||
result[section] = {}
|
||||
for option in self.options(section):
|
||||
value = self.get(section, option, raw=raw, vars=vars)
|
||||
try:
|
||||
value = cherrypy.lib.reprconf.unrepr(value)
|
||||
except Exception:
|
||||
x = sys.exc_info()[1]
|
||||
msg = ("Config error in section: %r, option: %r, "
|
||||
"value: %r. Config values must be valid Python." %
|
||||
(section, option, value))
|
||||
raise ValueError(msg, x.__class__.__name__, x.args)
|
||||
result[section][option] = value
|
||||
return result
|
||||
cherrypy.lib.reprconf.Parser.as_dict = new_as_dict
|
||||
|
||||
|
||||
def loadconf(configfile, instance):
|
||||
app = cherrypy.tree.mount(instance, '/', configfile)
|
||||
cherrypy.config.update(configfile)
|
||||
instance.reload(app.config)
|
||||
|
||||
class TestError(object):
|
||||
|
||||
def testNominal(self):
|
||||
app = LdapCherry()
|
||||
loadconf('./tests/cfg/ldapcherry.ini', app)
|
||||
return True
|
||||
|
Loading…
Reference in New Issue
Block a user