initial commit : Brie avec lecture LDAP

This commit is contained in:
Roven Gabriel 2012-09-29 15:38:19 +02:00
commit 3e95b9a9d6
94 changed files with 5564 additions and 0 deletions

6
.hgignore Normal file
View File

@ -0,0 +1,6 @@
syntax: glob
Brie/data/sessions/*
*.pyc
*.swp
*.swo

View File

@ -0,0 +1,10 @@
Metadata-Version: 1.0
Name: Brie
Version: 0.1dev
Summary: UNKNOWN
Home-page: UNKNOWN
Author: UNKNOWN
Author-email: UNKNOWN
License: UNKNOWN
Description: UNKNOWN
Platform: UNKNOWN

View File

@ -0,0 +1,68 @@
MANIFEST.in
README.txt
setup.cfg
setup.py
Brie.egg-info/PKG-INFO
Brie.egg-info/SOURCES.txt
Brie.egg-info/dependency_links.txt
Brie.egg-info/entry_points.txt
Brie.egg-info/paster_plugins.txt
Brie.egg-info/requires.txt
Brie.egg-info/top_level.txt
brie/__init__.py
brie/websetup.py
brie/config/__init__.py
brie/config/app_cfg.py
brie/config/environment.py
brie/config/middleware.py
brie/controllers/__init__.py
brie/controllers/error.py
brie/controllers/root.py
brie/controllers/secure.py
brie/controllers/template.py
brie/i18n/ru/LC_MESSAGES/brie.po
brie/lib/__init__.py
brie/lib/app_globals.py
brie/lib/base.py
brie/lib/helpers.py
brie/model/__init__.py
brie/model/auth.py
brie/public/favicon.ico
brie/public/css/style.css
brie/public/images/contentbg.png
brie/public/images/error.png
brie/public/images/header_inner2.png
brie/public/images/headerbg.png
brie/public/images/info.png
brie/public/images/inputbg.png
brie/public/images/loginbg.png
brie/public/images/loginbottombg.png
brie/public/images/loginheader-left.png
brie/public/images/loginheader-right.png
brie/public/images/menu-item-actibg-first.png
brie/public/images/menu-item-actibg.png
brie/public/images/menu-item-border.png
brie/public/images/menubg.png
brie/public/images/ok.png
brie/public/images/pagebg.png
brie/public/images/star.png
brie/public/images/strype2.png
brie/public/images/under_the_hood_blue.png
brie/public/images/warning.png
brie/templates/__init__.py
brie/templates/about.html
brie/templates/authentication.html
brie/templates/debug.html
brie/templates/error.html
brie/templates/footer.html
brie/templates/header.html
brie/templates/index.html
brie/templates/login.html
brie/templates/master.html
brie/templates/sidebars.html
brie/tests/__init__.py
brie/tests/functional/__init__.py
brie/tests/functional/test_authentication.py
brie/tests/functional/test_root.py
brie/tests/models/__init__.py
brie/tests/models/test_auth.py

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,7 @@
[paste.app_factory]
main = brie.config.middleware:make_app
[paste.app_install]
main = pylons.util:PylonsInstaller

View File

@ -0,0 +1,4 @@
PasteScript
Pylons
TurboGears2
tg.devtools

View File

@ -0,0 +1,7 @@
TurboGears2 >= 2.0b7
Catwalk >= 2.0.2
Babel >=0.9.4
toscawidgets >= 0.9.7.1
zope.sqlalchemy >= 0.4
repoze.tm2 >= 1.0a4
repoze.what-quickstart >= 1.0

View File

@ -0,0 +1 @@
brie

4
Brie/MANIFEST.in Normal file
View File

@ -0,0 +1,4 @@
recursive-include brie/public *
include brie/public/favicon.ico
recursive-include brie/i18n *
recursive-include brie/templates *

24
Brie/README.txt Normal file
View File

@ -0,0 +1,24 @@
This file is for you to describe the Brie application. Typically
you would include information such as the information below:
Installation and Setup
======================
Install ``Brie`` using the setup.py script::
$ cd Brie
$ python setup.py install
Create the project database for any model classes defined::
$ paster setup-app development.ini
Start the paste http server::
$ paster serve development.ini
While developing you may want the server to reload after changes in package files (or its dependencies) are saved. This can be achieved easily by adding the --reload option::
$ paster serve --reload development.ini
Then you are ready to go.

1
Brie/REAME Normal file
View File

@ -0,0 +1 @@
migrate_* sont des scripts temporaires d'entre deux

0
Brie/__init__.py Normal file
View File

2
Brie/brie/__init__.py Normal file
View File

@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
"""The Brie package"""

View File

@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-

View File

@ -0,0 +1,65 @@
# -*- coding: utf-8 -*-
"""
Global configuration file for TG2-specific settings in Brie.
This file complements development/deployment.ini.
Please note that **all the argument values are strings**. If you want to
convert them into boolean, for example, you should use the
:func:`paste.deploy.converters.asbool` function, as in::
from paste.deploy.converters import asbool
setting = asbool(global_conf.get('the_setting'))
"""
from tg.configuration import AppConfig
import brie
from brie import model
from brie.lib import app_globals, helpers
base_config = AppConfig()
base_config.renderers = []
base_config.package = brie
#Set the default renderer
base_config.default_renderer = 'genshi'
base_config.renderers.append('genshi')
# if you want raw speed and have installed chameleon.genshi
# you should try to use this renderer instead.
# warning: for the moment chameleon does not handle i18n translations
#base_config.renderers.append('chameleon_genshi')
#Configure the base SQLALchemy Setup
base_config.use_sqlalchemy = True
base_config.model = brie.model
base_config.DBSession = brie.model.DBSession
# YOU MUST CHANGE THIS VALUE IN PRODUCTION TO SECURE YOUR APP
base_config.sa_auth.cookie_secret = "ChangeME"
# Configure the authentication backend
base_config.auth_backend = 'sqlalchemy'
base_config.sa_auth.dbsession = model.DBSession
# what is the class you want to use to search for users in the database
base_config.sa_auth.user_class = model.User
# what is the class you want to use to search for groups in the database
base_config.sa_auth.group_class = model.Group
# what is the class you want to use to search for permissions in the database
base_config.sa_auth.permission_class = model.Permission
# override this if you would like to provide a different who plugin for
# managing login and logout of your application
base_config.sa_auth.form_plugin = None
# You may optionally define a page where you want users to be redirected to
# on login:
base_config.sa_auth.post_login_url = '/post_login'
# You may optionally define a page where you want users to be redirected to
# on logout:
base_config.sa_auth.post_logout_url = '/post_logout'

View File

@ -0,0 +1,95 @@
#
# Brie - TurboGears configuration
#
# The %(here)s variable will be replaced with the parent directory of this file
#
[DEFAULT]
# WARGING == If debug is not set to false, you'll get the interactive
# debugger on production, which is a huge security hole.
debug = false
email_to = you@yourdomain.com
smtp_server = localhost
error_email_from = paste@localhost
[server:main]
use = egg:Paste#http
host = 0.0.0.0
port = 8080
[app:main]
use = egg:Brie
full_stack = true
cache_dir = %(here)s/data
beaker.session.key = brie
beaker.session.secret = ${app_instance_secret}
app_instance_uuid = ${app_instance_uuid}
# If you'd like to fine-tune the individual locations of the cache data dirs
# for the Cache data, or the Session saves, un-comment the desired settings
# here:
#beaker.cache.data_dir = %(here)s/data/cache
#beaker.session.data_dir = %(here)s/data/sessions
# Specify the database for SQLAlchemy to use via
# turbogears.database
# %(here) may include a ':' character on Windows environments; this can
# invalidate the URI when specifying a SQLite db via path name
sqlalchemy.url = sqlite:///%(here)s/somedb.db
sqlalchemy.echo = False
# 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
# Add additional loggers, handlers, formatters here
# Uses python's logging config file format
# http://docs.python.org/lib/logging-config-fileformat.html
[loggers]
keys = root, brie, sqlalchemy, auth
[handlers]
keys = console
[formatters]
keys = generic
# If you create additional loggers, add them as a key to [loggers]
[logger_root]
level = INFO
handlers = console
[logger_brie]
level = INFO
handlers =
qualname = brie
[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine
# "level = INFO" logs SQL queries.
# "level = DEBUG" logs SQL queries and results.
# "level = WARN" logs neither. (Recommended for production systems.)
# A logger for authentication, identification and authorization -- this is
# repoze.who and repoze.what:
[logger_auth]
level = WARN
handlers =
qualname = auth
# If you create additional handlers, add them as a key to [handlers]
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
# If you create additional formatters, add them as a key to [formatters]
[formatter_generic]
format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S

View File

@ -0,0 +1,9 @@
# -*- coding: utf-8 -*-
"""WSGI environment setup for Brie."""
from brie.config.app_cfg import base_config
__all__ = ['load_environment']
#Use base_config to setup the environment loader function
load_environment = base_config.make_load_environment()

View File

@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
uri = "ldaps://ldap.pacaterie.u-psud.fr"
base_dn = "dc=pacaterie,dc=u-psud,dc=fr"
username_base_dn = "ou=membres," + base_dn
room_base_dn = "ou=chambres," + base_dn
area_filter = "(objectClass=pacateriearea)"
floor_filter = "(objectClass=pacateriefloor)"
room_filter = "(objectClass=pacaterieRoom)"

View File

@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
"""WSGI middleware initialization for the Brie application."""
from brie.config.app_cfg import base_config
from brie.config.environment import load_environment
__all__ = ['make_app']
# Use base_config to setup the necessary PasteDeploy application factory.
# make_base_app will wrap the TG2 app with all the middleware it needs.
make_base_app = base_config.setup_tg_wsgi_app(load_environment)
def make_app(global_conf, full_stack=True, **app_conf):
"""
Set Brie up with the settings found in the PasteDeploy configuration
file used.
:param global_conf: The global settings for Brie (those
defined under the ``[DEFAULT]`` section).
:type global_conf: dict
:param full_stack: Should the whole TG2 stack be set up?
:type full_stack: str or bool
:return: The Brie application with all the relevant middleware
loaded.
This is the PasteDeploy factory for the Brie application.
``app_conf`` contains all the application-specific settings (those defined
under ``[app:main]``.
"""
app = make_base_app(global_conf, full_stack=True, **app_conf)
# Wrap your base TurboGears 2 application with custom middleware here
return app

View File

@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
"""Controllers for the Brie application."""

View File

@ -0,0 +1,142 @@
# -*- coding: utf-8 -*-
from tg import session
from tg.controllers import RestController, redirect
from tg.decorators import expose, validate
from brie.lib.base import BaseController
from brie.config import ldap_config
from brie.lib.ldap_helper import *
class User(object):
ldap_bind = None
attrs = None
def __init__(self, ldap_bind, attrs):
self.ldap_bind = ldap_bind
self.attrs = attrs
#end def
#end class
class AuthHandler(object):
__users = dict()
__user_session_name = "user"
__anon_bind = None
def get_anon_bind(self):
if self.__anon_bind is not None:
return self.__anon_bind
else:
self.__anon_bind = Ldap.connect("", "")
return self.__anon_bind
#end if
#end def
def login(self, username, password):
if self.get_anon_bind() is None:
return False
actual_user = self.get_anon_bind().search_first(ldap_config.username_base_dn, "(uid=" + username + ")")
if actual_user is None:
return False
username_dn = actual_user.dn
bind = Ldap.connect(username_dn, password)
if bind is None:
return False
attributes = bind.search_first(username_dn, "(uid=" + username + ")")
user = User(bind, attributes)
AuthHandler.__users[username] = user
session[AuthHandler.__user_session_name] = username
session.save()
return True
#end def
def logout(self):
user = session[AuthHandler.__user_session_name]
if user in AuthHandler.__users:
stored_user = AuthHandler.__users[user]
stored_user.ldap_bind.close()
del AuthHandler.__users[user]
#end if
session[AuthHandler.__user_session_name] = None
session.save()
#end def
def get_user(self):
if not AuthHandler.__user_session_name in session:
return None
user = session[AuthHandler.__user_session_name]
if user in AuthHandler.__users:
return AuthHandler.__users[user]
return None
#end def
def get_user_or_redirect(self):
maybe_user = self.get_user()
if maybe_user is None:
redirect("/auth/login/") # TODO from config
#end if
return maybe_user
#end def
#end class
class AuthenticatedRestController(RestController):
user = None
def __before__(self, *args, **kwargs):
self.user = current.get_user_or_redirect()
#end def
#end def
class AuthenticatedBaseController(BaseController):
user = None
def __before__(self, *args, **kwargs):
self.user = current.get_user_or_redirect()
#end def
#end def
current = AuthHandler()
class LoginRestController(RestController):
@expose("brie.templates.auth.login")
def get(self):
return dict(login = "", error = "")
@expose("brie.templates.auth.login")
def post(self, username, password):
success = current.login(username, password)
if success:
redirect("/")
#end if
return dict(login = username, error = "erreur de connexion")
#end def
class AuthRestController(BaseController):
login = LoginRestController()
@expose()
def logout(self):
current.logout()
redirect("/")
#end class

View File

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
"""Sample controller module"""
# turbogears imports
from tg import expose
#from tg import redirect, validate, flash
# third party imports
#from pylons.i18n import ugettext as _
#from repoze.what import predicates
# project specific imports
from brie.lib.base import BaseController
#from brie.model import DBSession, metadata
class SampleController(BaseController):
#Uncomment this line if your controller requires an authenticated user
#allow_only = authorize.not_anonymous()
@expose('brie.templates.index')
def index(self):
return dict(page='index')

View File

@ -0,0 +1,83 @@
# -*- coding: utf-8 -*-
from tg import session
from tg.controllers import redirect
from tg.decorators import expose, validate
from brie.lib.camembert_helpers import *
from brie.config import ldap_config
from brie.lib.ldap_helper import *
from brie.model import DBSession
from brie.model.ldap import *
from brie.controllers import auth
from brie.controllers.auth import AuthenticatedBaseController, AuthenticatedRestController
from operator import itemgetter
#root = tg.config['application_root_module'].RootController
""" Controller d'affichage de details de membres, chambres et interfaces """
class EditController(AuthenticatedBaseController):
show = None
wifi = None
def __init__(self, new_show):
self.show = new_show
self.wifi = WifiRestController(new_show)
""" Affiche les détails du membre, de la chambre et de l'interface """
@expose("brie.templates.edit.member")
def member(self, uid):
return self.show.member(uid)
#end def
@expose("brie.templates.edit.room")
def room(self, room_number):
return self.show.room(room_number)
#end def
@expose("brie.templates.edit.interface")
def interface(self, interface_id):
return self.show.interface(interface_id)
#end def
#end class
class WifiRestController(AuthenticatedRestController):
show = None
def __init__(self, new_show):
self.show = new_show
@expose("brie.templates.edit.wifi")
def get(self, uid):
member = Member.get_by_uid(self.user, uid)
if member is None:
self.show.error_no_entry()
def post(self, uid, password):
member = Member.get_by_uid(self.user, uid)
if member is None:
self.show.error_no_entry()
wifi = Wifi.get_by_dn(self.user, member.dn)
if wifi is None:
wifi_dn = "cn=wifi," + member.dn
self.user.ldap_bind.add_entry(wifi_dn, Wifi.entry_attr(password))
else:
attr = {
"userPassword" : password
}
self.user.ldap_bind.replace_attr(wifi.dn, attr)
#end if
redirect("/show/member/" + uid)
#end def
#end class

View File

@ -0,0 +1,111 @@
from tg import session
from tg.controllers import redirect
from tg.decorators import expose, validate
from brie.lib.base import BaseController
from brie.lib.camembert_helpers import *
from brie.config import ldap_config
from brie.lib.ldap_helper import *
from brie.model import DBSession
from brie.model.camembert import *
from brie.model.ldap import *
from brie.controllers import auth
from brie.controllers.auth import AuthenticatedBaseController, AuthenticatedRestController
from operator import itemgetter
class RoomsController(AuthenticatedBaseController):
__default_color = "ok_color"
__error_colors = {
"PAS PAYE" : "non_paye_color",
"CERTIF" : "non_certif_color",
"VIDE" : "vide_color"
}
def color_picker(self, description):
for color in self.__error_colors.iteritems():
if color[0] in description:
return color[1]
#end if
#end for
return self.__default_color
#end def
def reverse_sort_name(self, name_items):
return sorted(name_items, key=itemgetter(0), reverse=True)
def sort_name(self, name_items):
return sorted(name_items, key=itemgetter(0))
@expose("brie.templates.rooms.index")
def index(self):
result = DBSession.query(Interface)
interfaces_dict = dict()
for interface in result:
interfaces_dict[str(interface.idinterface)] = interface
stats = dict()
areas = dict()
rooms = self.user.ldap_bind.search(ldap_config.room_base_dn, ldap_config.room_filter)
rooms = sorted(rooms, key = lambda a: a.cn.first())
for room in rooms:
interface = interfaces_dict[room.get("x-switchInterface").first()]
color = self.color_picker(interface.ifdescription)
if color in stats:
stats[color] = stats[color] + 1
else:
stats[color] = 0
#end if
room_id = int(room.uid.first())
floor = Translations.floor_of_room(room_id)
area = Translations.area_of_room(room_id)
if area not in areas:
areas[area] = dict()
#end if
if floor not in areas[area]:
areas[area][floor] = []
#end if
areas[area][floor].append((room, interface))
#end for
return { "areas" : areas, "color_picker" : self.color_picker, "reverse_sorted_name" : self.reverse_sort_name, "sorted_name" : self.sort_name, "stats" : stats}
#end def
@expose("brie.templates.rooms.index")
def preview(self, number):
if not number.isdigit(): redirect("/rooms/")
index_result = self.index()
room = Room.get_by_uid(self.user, number)
member = None
if room.has("x-memberIn"):
member = Member.get_by_dn(self.user, room.get("x-memberIn").first())
interface = (
DBSession.query(Interface)
.filter(Interface.idinterface == room.get("x-switchInterface").first())
.first()
)
preview = member, room, interface
index_result["preview"] = preview
return index_result
#end def

View File

@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
"""Main Controller"""
from tg import expose, flash, require, url, request, redirect
from pylons.i18n import ugettext as _, lazy_ugettext as l_
from catwalk.tg2 import Catwalk
from repoze.what import predicates
from brie.lib.base import BaseController
from brie.model import DBSession, metadata
from brie import model
#from brie.controllers.secure import SecureController
import brie.controllers.auth as auth_handler
from brie.controllers.auth import AuthRestController
from brie.controllers.rooms import RoomsController
from brie.controllers.show import ShowController
from brie.controllers.edit import EditController
from brie.model.camembert import Materiel
__all__ = ['RootController']
class RootController(BaseController):
"""
The root controller for the Brie application.
All the other controllers and WSGI applications should be mounted on this
controller. For example::
panel = ControlPanelController()
another_app = AnotherWSGIApplication()
Keep in mind that WSGI applications shouldn't be mounted directly: They
must be wrapped around with :class:`tg.controllers.WSGIAppController`.
"""
# admin = Catwalk(model, DBSession)
auth = AuthRestController()
rooms = RoomsController()
show = ShowController()
edit = EditController(show)
@expose('brie.templates.index')
def index(self):
"""Handle the front-page."""
materiel = DBSession.query(Materiel)
user = auth_handler.current.get_user()
return { "user" : user, "materiel" : materiel }
@expose()
def foobar(self):
redirect("http://172.17.22.10:9000/toto")
#end def
# @expose('brie.templates.index')
# @require(predicates.has_permission('manage', msg=l_('Only for managers')))
# def manage_permission_only(self, **kw):
# """Illustrate how a page for managers only works."""
# return dict(page='managers stuff')

View File

@ -0,0 +1,81 @@
# -*- coding: utf-8 -*-
from tg import session
from tg.controllers import redirect
from tg.decorators import expose, validate
from brie.lib.camembert_helpers import *
from brie.config import ldap_config
from brie.lib.ldap_helper import *
from brie.model import DBSession
from brie.model.camembert import Interface
from brie.model.ldap import *
from brie.controllers import auth
from brie.controllers.auth import AuthenticatedBaseController, AuthenticatedRestController
from operator import itemgetter
""" Controller d'affichage de details de membres, chambres et interfaces """
class ShowController(AuthenticatedBaseController):
@expose("brie.templates.show.error")
def error_no_entry(self):
return { "error" : "Entrée non existante" }
""" Affiche les détails du membre, de la chambre et de l'interface """
@expose("brie.templates.show.member")
def member(self, uid):
member = Member.get_by_uid(self.user, uid)
if member is None:
return self.error_no_entry()
room = Room.get_by_member_dn(self.user, member.dn)
interface = (
DBSession.query(Interface)
.filter(Interface.idinterface == room.get("x-switchInterface").first())
.first()
)
return { "member_ldap" : member, "interface" : interface, "room_ldap" : room }
#end def
@expose("brie.templates.show.room")
def room(self, room_id):
room = Room.get_by_uid(self.user, room_id)
if room is None:
return self.error_no_entry()
interface = (
DBSession.query(Interface)
.filter(Interface.idinterface == room.get("x-switchInterface").first())
.first()
)
member = None
if room.has("x-memberIn"):
member = Member.get_by_dn(self.user, room.get("x-memberIn").first())
return { "interface" : interface, "room_ldap" : room, "member_ldap" : member }
#end def
@expose("brie.templates.show.interface")
def interface(self, interface_id):
interface = (
DBSession.query(Interface)
.filter(Interface.idinterface == interface_id)
.first()
)
if interface is None:
return self.error_no_entry()
room = Room.get_by_interface(self.user, interface.idinterface)
return { "interface" : interface, "room_ldap" : room }
#end def
#end class

View File

@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
"""Fallback controller."""
from brie.lib.base import BaseController
__all__ = ['TemplateController']
class TemplateController(BaseController):
"""
The fallback controller for Brie.
By default, the final controller tried to fulfill the request
when no other routes match. It may be used to display a template
when all else fails, e.g.::
def view(self, url):
return render('/%s' % url)
Or if you're using Mako and want to explicitly send a 404 (Not
Found) response code when the requested template doesn't exist::
import mako.exceptions
def view(self, url):
try:
return render('/%s' % url)
except mako.exceptions.TopLevelLookupException:
abort(404)
"""
def view(self, url):
"""Abort the request with a 404 HTTP status code."""
abort(404)

View File

@ -0,0 +1,24 @@
# Russian translations for ${package}.
# Copyright (C) 2008 ORGANIZATION
# This file is distributed under the same license as the ${package} project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2008.
#
msgid ""
msgstr ""
"Project-Id-Version: ${package} 0.0.0\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2008-01-13 14:00+0200\n"
"PO-Revision-Date: 2008-01-13 14:00+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: ru <LL@li.org>\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.1\n"
#: ${package}/controllers/root.py:13
msgid "Your application is now running"
msgstr "Ваши приложение успешно запущено"

View File

@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-

View File

@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
"""The application's Globals object"""
__all__ = ['Globals']
class Globals(object):
"""Container for objects available throughout the life of the application.
One instance of Globals is created during application initialization and
is available during requests via the 'app_globals' variable.
"""
def __init__(self):
"""Do nothing, by default."""
pass

32
Brie/brie/lib/base.py Normal file
View File

@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
"""The base Controller API."""
from tg import TGController, tmpl_context
from tg.render import render
from tg import request
from pylons.i18n import _, ungettext, N_
from tw.api import WidgetBunch
import brie.model as model
__all__ = ['Controller', 'BaseController']
class BaseController(TGController):
"""
Base class for the controllers in the application.
Your web application should have one of these. The root of
your application is used to compute URLs used by your app.
"""
def __call__(self, environ, start_response):
"""Invoke the Controller"""
# TGController.__call__ dispatches to the Controller method
# the request is routed to. This routing information is
# available in environ['pylons.routes_dict']
request.identity = request.environ.get('repoze.who.identity')
tmpl_context.identity = request.identity
return TGController.__call__(self, environ, start_response)

View File

@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
import unicodedata
class Translations(object):
@staticmethod
def to_uid(name, surname):
clean_name = Translations.strip_accents(name.replace(" ", "")).lower()[:15]
clean_surname = Translations.strip_accents(surname.replace(" ", "")).lower()[:15]
return clean_name + "." + clean_surname
#end def
@staticmethod
def floor_of_room(room):
return room / 100
#end def
@staticmethod
def area_of_room(room):
if Translations.floor_of_room(room) == 5:
return "crous"
floor_number = room % 100
if floor_number <= 33:
return "sud"
else:
return "nord"
#end if
#end def
#end class
# http://stackoverflow.com/questions/517923/what-is-the-best-way-to-remove-accents-in-a-python-unicode-string
@staticmethod
def strip_accents(s):
return ''.join((c for c in unicodedata.normalize('NFD', s) if unicodedata.category(c) != 'Mn'))
#end def

5
Brie/brie/lib/helpers.py Normal file
View File

@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
"""WebHelpers used in Brie."""
from webhelpers import date, feedgenerator, html, number, misc, text

View File

@ -0,0 +1,180 @@
# -*- coding: utf-8 -*-
import ldap
#import ldap.modlist as modlist
from brie.config import ldap_config
class Ldap(object):
__connection = None
def __init__(self, connection):
self.__connection = connection
#end def
@staticmethod
def connect(dn, password):
connection = None
try:
connection = ldap.initialize(ldap_config.uri)
connection.simple_bind_s(dn, password)
except:
return None
#end try
if connection is not None:
return Ldap(connection)
#end
return None
#end def
def search(self, dn, filter, scope = ldap.SCOPE_SUBTREE):
try:
results = self.__connection.search_s(dn, scope, filter)
except:
return None
#end try
ldap_results = []
for result in results:
result_dn = result[0]
attributes = result[1]
val_dict = dict()
for attribute in attributes.iteritems():
name = attribute[0]
values = attribute[1]
ldap_value = LdapValue(name, values)
val_dict[name] = ldap_value
#end for
ldap_result = LdapResult(result_dn, val_dict)
ldap_results.append(ldap_result)
#end for
return ldap_results
#end def
def search_first(self, dn, filter, scope = ldap.SCOPE_SUBTREE):
results = self.search(dn, filter, scope)
if results is None: return None
for result in results:
return result
#end for
return None
#end def
def search_dn(self, dn):
return self.search_first(dn, "(objectClass=*)", ldap.SCOPE_BASE)
def replace_attr(self, dn, attributes):
modlist = []
for attribute in attributes.iteritems():
modlist.append((ldap.MOD_REPLACE, attribute[0], attribute[1]))
#end for
self.__connection.modify_s(dn, modlist)
#end def
def add_attr(self, dn, attributes):
modlist = []
for attribute in attributes.iteritems():
modlist.append((ldap.MOD_ADD, attribute[0], attribute[1]))
#end for
try:
self.__connection.modify_s(dn, modlist)
except ldap.TYPE_OR_VALUE_EXISTS:
pass
#end def
def delete_attr(self, dn, attributes):
modlist = []
for attribute in attributes.iteritems():
modlist.append((ldap.MOD_DELETE, attribute[0], attribute[1]))
#end for
#try:
self.__connection.modify_s(dn, modlist)
#except:
# pass
#end def
def add_entry(self, dn, attributes):
modlist = []
for attribute in attributes.iteritems():
modlist.append((attribute[0], attribute[1]))
#end for
##try:
self.__connection.add_s(dn, modlist)
##except:
## pass
#end def
def delete_entry(self, dn):
#try:
self.__connection.delete_s(dn)
#except:
# pass
#end def
def delete_entry_subtree(self, dn):
entries = self.search(dn, "(objectClass=*)")
for entry in reversed(entries):
self.delete_entry(entry.dn)
#end for
#end def
def rename_entry(self, dn, newdn, superior):
self.__connection.rename_s(dn, newdn, newsuperior= superior)
def close(self):
self.__connection.unbind()
#end class
class LdapResult(object):
dn = None
def __init__(self, dn, var_dict):
self.__dict__ = var_dict
self.dn = dn
#end def
def has(self, attribute_name):
return attribute_name in self.__dict__
#end def
def get(self, name):
if name in self.__dict__:
return self.__dict__[name]
else:
return self.__getattr__(name)
#end if
#end def
def __getattr__(self, name):
return None
#end def
#end class
class LdapValue(object):
name = None
values = []
def __init__(self, name, values):
self.values = [value.decode("utf-8") for value in values]
self.name = name
#end def
def first(self, default = None):
for value in self.values:
return value
#end for
return default
#end def
#end class

View File

@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
"""The application's model objects"""
from zope.sqlalchemy import ZopeTransactionExtension
from sqlalchemy.orm import scoped_session, sessionmaker
#from sqlalchemy import MetaData
from sqlalchemy.ext.declarative import declarative_base
# Global session manager: DBSession() returns the Thread-local
# session object appropriate for the current web request.
maker = sessionmaker(autoflush=True, autocommit=False,
extension=ZopeTransactionExtension())
DBSession = scoped_session(maker)
# Base class for all of our model classes: By default, the data model is
# defined with SQLAlchemy's declarative extension, but if you need more
# control, you can switch to the traditional method.
DeclarativeBase = declarative_base()
# There are two convenient ways for you to spare some typing.
# You can have a query property on all your model classes by doing this:
# DeclarativeBase.query = DBSession.query_property()
# Or you can use a session-aware mapper as it was used in TurboGears 1:
# DeclarativeBase = declarative_base(mapper=DBSession.mapper)
# Global metadata.
# The default metadata is the one from the declarative base.
metadata = DeclarativeBase.metadata
# If you have multiple databases with overlapping table names, you'll need a
# metadata for each database. Feel free to rename 'metadata2'.
#metadata2 = MetaData()
#####
# Generally you will not want to define your table's mappers, and data objects
# here in __init__ but will want to create modules them in the model directory
# and import them at the bottom of this file.
#
######
def init_model(engine):
"""Call me before using any of the tables or classes in the model."""
DBSession.configure(bind=engine)
# If you are using reflection to introspect your database and create
# table objects for you, your tables must be defined and mapped inside
# the init_model function, so that the engine is available if you
# use the model outside tg2, you need to make sure this is called before
# you use the model.
#
# See the following example:
#global t_reflected
#t_reflected = Table("Reflected", metadata,
# autoload=True, autoload_with=engine)
#mapper(Reflected, t_reflected)
# Import your model modules here.
from brie.model.auth import User, Group, Permission

232
Brie/brie/model/auth.py Normal file
View File

@ -0,0 +1,232 @@
# -*- coding: utf-8 -*-
"""
Auth* related model.
This is where the models used by :mod:`repoze.who` and :mod:`repoze.what` are
defined.
It's perfectly fine to re-use this definition in the Brie application,
though.
"""
import os
from datetime import datetime
import sys
try:
from hashlib import sha1
except ImportError:
sys.exit('ImportError: No module named hashlib\n'
'If you are on python2.4 this library is not part of python. '
'Please install it. Example: easy_install hashlib')
from sqlalchemy import Table, ForeignKey, Column
from sqlalchemy.types import Unicode, Integer, DateTime
from sqlalchemy.orm import relation, synonym
from brie.model import DeclarativeBase, metadata, DBSession
__all__ = ['User', 'Group', 'Permission']
#{ Association tables
# This is the association table for the many-to-many relationship between
# groups and permissions. This is required by repoze.what.
group_permission_table = Table('tg_group_permission', metadata,
Column('group_id', Integer, ForeignKey('tg_group.group_id',
onupdate="CASCADE", ondelete="CASCADE")),
Column('permission_id', Integer, ForeignKey('tg_permission.permission_id',
onupdate="CASCADE", ondelete="CASCADE"))
)
# This is the association table for the many-to-many relationship between
# groups and members - this is, the memberships. It's required by repoze.what.
user_group_table = Table('tg_user_group', metadata,
Column('user_id', Integer, ForeignKey('tg_user.user_id',
onupdate="CASCADE", ondelete="CASCADE")),
Column('group_id', Integer, ForeignKey('tg_group.group_id',
onupdate="CASCADE", ondelete="CASCADE"))
)
#{ The auth* model itself
class Group(DeclarativeBase):
"""
Group definition for :mod:`repoze.what`.
Only the ``group_name`` column is required by :mod:`repoze.what`.
"""
__tablename__ = 'tg_group'
#{ Columns
group_id = Column(Integer, autoincrement=True, primary_key=True)
group_name = Column(Unicode(16), unique=True, nullable=False)
display_name = Column(Unicode(255))
created = Column(DateTime, default=datetime.now)
#{ Relations
users = relation('User', secondary=user_group_table, backref='groups')
#{ Special methods
def __repr__(self):
return '<Group: name=%s>' % self.group_name
def __unicode__(self):
return self.group_name
#}
# The 'info' argument we're passing to the email_address and password columns
# contain metadata that Rum (http://python-rum.org/) can use generate an
# admin interface for your models.
class User(DeclarativeBase):
"""
User definition.
This is the user definition used by :mod:`repoze.who`, which requires at
least the ``user_name`` column.
"""
__tablename__ = 'tg_user'
#{ Columns
user_id = Column(Integer, autoincrement=True, primary_key=True)
user_name = Column(Unicode(16), unique=True, nullable=False)
email_address = Column(Unicode(255), unique=True, nullable=False,
info={'rum': {'field':'Email'}})
display_name = Column(Unicode(255))
_password = Column('password', Unicode(80),
info={'rum': {'field':'Password'}})
created = Column(DateTime, default=datetime.now)
#{ Special methods
def __repr__(self):
return '<User: email="%s", display name="%s">' % (
self.email_address, self.display_name)
def __unicode__(self):
return self.display_name or self.user_name
#{ Getters and setters
@property
def permissions(self):
"""Return a set of strings for the permissions granted."""
perms = set()
for g in self.groups:
perms = perms | set(g.permissions)
return perms
@classmethod
def by_email_address(cls, email):
"""Return the user object whose email address is ``email``."""
return DBSession.query(cls).filter(cls.email_address==email).first()
@classmethod
def by_user_name(cls, username):
"""Return the user object whose user name is ``username``."""
return DBSession.query(cls).filter(cls.user_name==username).first()
def _set_password(self, password):
"""Hash ``password`` on the fly and store its hashed version."""
hashed_password = password
if isinstance(password, unicode):
password_8bit = password.encode('UTF-8')
else:
password_8bit = password
salt = sha1()
salt.update(os.urandom(60))
hash = sha1()
hash.update(password_8bit + salt.hexdigest())
hashed_password = salt.hexdigest() + hash.hexdigest()
# Make sure the hashed password is an UTF-8 object at the end of the
# process because SQLAlchemy _wants_ a unicode object for Unicode
# columns
if not isinstance(hashed_password, unicode):
hashed_password = hashed_password.decode('UTF-8')
self._password = hashed_password
def _get_password(self):
"""Return the hashed version of the password."""
return self._password
password = synonym('_password', descriptor=property(_get_password,
_set_password))
#}
def validate_password(self, password):
"""
Check the password against existing credentials.
:param password: the password that was provided by the user to
try and authenticate. This is the clear text version that we will
need to match against the hashed one in the database.
:type password: unicode object.
:return: Whether the password is valid.
:rtype: bool
"""
hashed_pass = sha1()
hashed_pass.update(password + self.password[:40])
return self.password[40:] == hashed_pass.hexdigest()
class Permission(DeclarativeBase):
"""
Permission definition for :mod:`repoze.what`.
Only the ``permission_name`` column is required by :mod:`repoze.what`.
"""
__tablename__ = 'tg_permission'
#{ Columns
permission_id = Column(Integer, autoincrement=True, primary_key=True)
permission_name = Column(Unicode(16), unique=True, nullable=False)
description = Column(Unicode(255))
#{ Relations
groups = relation(Group, secondary=group_permission_table,
backref='permissions')
#{ Special methods
def __repr__(self):
return '<Permission: name=%s>' % self.permission_name
def __unicode__(self):
return self.permission_name
#}
#}

View File

@ -0,0 +1,144 @@
# -*- coding: utf-8 -*-
"""Sample model module."""
from sqlalchemy import *
from sqlalchemy.orm import mapper, relation
from sqlalchemy import Table, ForeignKey, Column
from sqlalchemy.types import Integer, Unicode
from sqlalchemy.dialects.postgresql import MACADDR, INET, BOOLEAN, DATE
#from sqlalchemy.orm import relation, backref
from brie.model import DeclarativeBase, metadata, DBSession
class Materiel(DeclarativeBase):
__tablename__ = 'materiel'
#{ Columns
idmateriel = Column(Integer, primary_key=True)
hostname = Column(Unicode(64))
manageable = Column(Integer)
snmpversion = Column(Integer)
type = Column(Unicode(64))
ostype = Column(Unicode(512))
capabilities = Column(Integer)
datecreation = Column(Integer)
datelast = Column(Integer)
#}
class Room(DeclarativeBase):
__tablename__ = "room"
idroom = Column(Integer, primary_key=True)
idinterface = Column(Integer)
name = Column(Unicode(32))
class Computer(DeclarativeBase):
__tablename__ = "computer"
idcomp = Column(Integer, primary_key = True)
name = Column(Unicode(32), nullable = False)
mac = Column(MACADDR, nullable = False)
iduser = Column(Integer)
ip = Column(INET)
#end class
class UserPacaterie(DeclarativeBase):
__tablename__ = "user_pac"
iduser = Column(Integer, primary_key = True)
nom = Column(Unicode(64), nullable = False)
prenom = Column(Unicode(64), nullable = False)
datedeco = Column(DATE, nullable = False)
mail = Column(Unicode(64))
certif = Column(BOOLEAN, nullable = False)
idroom = Column(Integer)
special_case = Column(BOOLEAN)
comment = Column(Unicode(64))
#end class
class Ip(DeclarativeBase):
__tablename__ = "ip"
ip = Column(INET, primary_key = True)
idmateriel = Column(Integer, primary_key = True)
main = Column(BOOLEAN)
datecreation = Column(Integer)
datelast = Column(Integer)
#end class
class IpUser(DeclarativeBase):
__tablename__ = "ip_user"
ip = Column(INET, primary_key = True)
free = Column(BOOLEAN)
#end class
class Fdb(DeclarativeBase):
__tablename__ = "fdb"
idinterface = Column(Integer, primary_key = True)
vlan = Column(Integer, primary_key = True)
mac = Column(MACADDR, primary_key = True)
datefirst = Column(Integer)
datelast = Column(Integer)
type = Column(Integer)
#end class
class Action(DeclarativeBase):
__tablename__ = "action"
idaction = Column(Integer, primary_key = True)
idinterface = Column(Integer)
numaction = Column(Integer, nullable = False)
option = Column(Unicode(256))
#end class
class ActionLog(DeclarativeBase):
__tablename__ = "action_log"
idlog = Column(Integer, primary_key = True)
loggeduser = Column(Unicode(64), nullable = False)
logdate = Column(Integer, nullable = False)
idinterface = Column(Integer)
numaction = Column(Integer, nullable = False)
oldoption = Column(Unicode(256))
newoption = Column(Unicode(256))
iduser = Column(Integer)
amount = Column(Unicode(64))
#end class
class Interface(DeclarativeBase):
__tablename__ = "interface"
idinterface = Column(Integer, primary_key = True)
idmateriel = Column(Integer, nullable = False)
ifnumber = Column(Integer, nullable = False)
ifname = Column(Unicode(64))
ifdescription = Column(Unicode(256))
ifaddress = Column(MACADDR)
ifspeed = Column(Integer)
ifadminstatus = Column(Integer)
ifoperstatus = Column(Integer)
iftype = Column(Integer)
ifvlan = Column(Integer)
ifvoicevlan = Column(Integer)
ifnativevlan = Column(Integer)
ifmodule = Column(Integer)
ifport = Column(Integer)
portdot1d = Column(Integer)
portfast = Column(BOOLEAN)
portsecenable = Column(BOOLEAN)
portsecstatus = Column(Integer)
portsecmaxmac = Column(Integer)
portseccurrmac = Column(Integer)
portsecviolation = Column(Integer)
portseclastsrcaddr = Column(MACADDR)
portsecsticky = Column(BOOLEAN)
#end class

59
Brie/brie/model/ldap.py Normal file
View File

@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
from brie.config import ldap_config
class Member(object):
@staticmethod
def get_by_dn(user_session, dn):
return user_session.ldap_bind.search_dn(dn)
#end def
@staticmethod
def get_by_uid(user_session, uid):
return user_session.ldap_bind.search_first(ldap_config.username_base_dn, "(uid=" + uid + ")")
#end def
#end class
class Room(object):
@staticmethod
def get_by_name(user_session, name):
return user_session.ldap_bind.search_first(ldap_config.room_base_dn, "(&" + ldap_config.room_filter + "(cn=" + name + "))")
#end def
@staticmethod
def get_by_uid(user_session, uid):
return user_session.ldap_bind.search_first(ldap_config.room_base_dn, "(&" + ldap_config.room_filter + "(uid=" + uid + "))")
#end def
@staticmethod
def get_by_member_dn(user_session, dn):
return user_session.ldap_bind.search_first(ldap_config.room_base_dn, "(&" + ldap_config.room_filter + "(x-memberIn=" + dn + "))")
#end def
@staticmethod
def get_by_interface(user_session, interface_id):
return user_session.ldap_bind.search_first(ldap_config.room_base_dn, "(&" + ldap_config.room_filter + "(x-switchInterface=" + str(interface_id) + "))")
#end class
class Wifi(object):
@staticmethod
def entry_attr(password):
return {
"objectClass" : ["top", "organizationalRole", "simpleSecurityObject"],
"cn" : "wifi",
"userPassword" : password
}
#end def
@staticmethod
def get_by_dn(user_session, dn):
return user_session.ldap_bind.search_first("cn=wifi," + dn)
#end def
#end class

View File

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
"""Sample model module."""
from sqlalchemy import *
from sqlalchemy.orm import mapper, relation
from sqlalchemy import Table, ForeignKey, Column
from sqlalchemy.types import Integer, Unicode
#from sqlalchemy.orm import relation, backref
from brie.model import DeclarativeBase, metadata, DBSession
class SampleModel(DeclarativeBase):
__tablename__ = 'sample_model'
#{ Columns
id = Column(Integer, primary_key=True)
data = Column(Unicode(255), nullable=False)
#}

View File

@ -0,0 +1,31 @@
html {
font-family: sans-serif;
color: #444444;
}
a {
color: #444444;
text-decoration: none;
}
a:hover {
color: white;
background-color: black;
}
.section {
font-family: sans-serif;
margin-bottom: 20px;
}
.section_name, .section_name_no_caps {
color: grey;
font-weight: bold;
margin-bottom: 6px;
display: inline-block;
}
.section_name {
text-transform: capitalize;
}

View File

@ -0,0 +1,68 @@
html {
font-family: sans-serif;
}
a {
color: #444444;
text-decoration: none;
}
a:hover {
color: white;
background-color: black;
}
.floor_name {
color: lightgrey;
}
.room_number, .interface_status {
display: inline-block;
color: #444444;
font-weight: bold;
padding: 3px;
margin: 2px;
}
.area {
width: 1300px
}
.preview_part {
display: inline-block;
}
.preview_name {
color: #444444;
font-weight: bold;
}
.ok_color {
background-color: #D1F2C4
}
.vide_color {
background-color: white;
}
.non_certif_color {
background-color: #F2E1C4;
}
.non_paye_color {
background-color: #F2C4C4;
}
.shut_color {
background-color: lightgray;
}
.violation_color {
background-color: #F2C4C4;
}
.rooms_legend {
float: right;
}

View File

@ -0,0 +1,12 @@
.show_section_name {
margin-left: 200px;
}
.item_name {
width: 196px;
color: #A0A0A0;
display: inline-block;
height: 22px;
text-align: right;
padding-right: 4px;
}

View File

@ -0,0 +1,303 @@
html {
background: #555555 url('../images/pagebg.png') top left repeat-x;
text-align: center;
margin: 0;
padding: 0;
}
body {
text-align: left;
width: 960px;
margin: 0 auto;
padding: 0;
font-size: 12px;
font-family:"Lucida Grande","Lucida Sans Unicode",geneva,verdana,sans-serif;
}
a {
color: #286571;
}
#header {
height: 132px;
margin: 10px 10px 0 10px;
background: url('../images/headerbg.png') top left no-repeat;
}
#header h1 {
padding: 0;
margin: 0;
padding-top: 30px;
padding-left: 180px;
color: #fff;
position: relative;
font-size: 36px;
}
#header h1 .subtitle {
font-size: 60%;
position: absolute;
left: 240px;
top: 70px;
}
ul#mainmenu {
margin: 0;
padding: 0 10px;
background: url('../images/menubg.png') top left no-repeat;
height: 38px;
}
ul#mainmenu li {
list-style-type: none;
margin: 0;
padding: 0;
position: relative;
display: inline;
float: left;
}
ul#mainmenu li a {
color: #fff;
float: left;
height: 31px;
display: block;
line-height: 30px;
vertical-align: middle;
padding: 0 10px;
font-size: 12px;
text-decoration: none;
background: url('../images/menu-item-border.png') left top no-repeat;
}
ul#mainmenu li a:hover, ul#mainmenu li a.active {
background: url('../images/menu-item-actibg.png') left top no-repeat;
}
ul#mainmenu li.first a {
background: none;
}
ul#mainmenu li.first a:hover, ul#mainmenu li.first a.active {
background: url('../images/menu-item-actibg-first.png') left top no-repeat;
}
ul#mainmenu li.loginlogout
{
float: right;
right: 10px;
}
ul#mainmenu li.loginlogout a:hover
{
background: url('../images/menu-item-border.png') left top no-repeat;
}
#content {
background: #fff url('../images/contentbg.png') left bottom no-repeat;
margin : 0 10px 10px 10px;
padding: 0 10px;
overflow: hidden;
}
#content .currentpage {
margin-top: 0;
}
/* Login form*/
#loginform
{
text-align: center;
}
form.loginfields
{
text-align: left;
width: 270px;
background: url('../images/loginbg.png') top left no-repeat;
padding: 0;
margin: 0 auto;
border: 0;
}
form.loginfields h2
{
font-size: 16px;
float: left;
padding: 0;
margin: 5px;
margin-top: 0;
background: url('../images/loginheader-left.png') top left no-repeat;
}
* html form.loginfields h2
{
margin-left: 3px;
width: 80px;
text-align: center;
}
form.loginfields h2 span
{
background: url('../images/loginheader-right.png') right top no-repeat;
height: 30px;
line-height: 30px;
display: block;
font-size: 100%;
font-weight: normal;
color: #fff;
padding: 0 10px;
}
*+html form.loginfields h2 span
{
padding: 0 20px;
}
form.loginfields label
{
clear: left;
float: left;
margin-top: 5px;
margin-left: 10px;
width: 65px;
line-height: 31px;
vertical-align: middle;
}
form.loginfields input.text
{
float: left;
margin-left: 10px;
margin-top: 5px;
width: 165px;
height: 21px;
padding: 5px;
border: 0;
background: url('../images/inputbg.png') top left no-repeat;
color: #fff;
}
form.loginfields input#submit
{
background: url('../images/loginbottombg.png') bottom left no-repeat;
width: 270px;
height: 61px;
border: 0;
margin-top: 10px;
color: #fff;
padding: 10px 140px 20px 10px;
}
* html form.loginfields input#submit
{
clear: both;
margin-left: -10px;
}
/* Sidebar */
.sidebar {
border: 1px solid #cce;
background-color: #eee;
margin: 0.5em;
margin-left: 1.5em;
padding: 1em;
float: right;
width: 200px;
font-size: 88%;
}
.sidebar h2 {
margin-top: 0;
color: black;
}
.sidebar ul {
margin-left: 1.5em;
padding-left: 0;
}
#sb_top {
clear: right;
}
#sb_bottom {
clear: right;
}
#getting_started {
margin-left: 20px;
}
#getting_started_steps a {
text-decoration: none;
}
#getting_started_steps a:hover {
text-decoration: underline;
}
#getting_started_steps li {
margin-bottom: 0.5em;
}
/* Other and footer */
#footer {
background: #fff;
padding: 10px;
color:#888888;
font-size:90%;
}
.flogo {
float:left;
margin-bottom:0px;
padding-left:20px;
padding-right:20px;
padding-top:0px;
}
.foottext p {
margin: 0; padding: 0;
}
.code {
font-family:monospace;
font-size:127%;
}
span.code {
background:#EEEEEE none repeat scroll 0% 0%;
font-weight:bold;
}
#flash, .notice {
font-size:120%;
font-weight:bolder;
margin:0pt auto 0.5em;
width:680px;
}
#flash div, .notice {
padding:20px 15px 20px 65px;
}
#flash .ok {
background:#d8ecd8 url(../images/ok.png) no-repeat scroll 10px center;
}
#flash .warning {
background:#fff483 url(../images/warning.png) no-repeat scroll 10px center;
}
#flash .error {
background:#f9c5c1 url(../images/error.png) no-repeat scroll 10px center;
}
#flash .alert,
#flash .info {
background:#EEEEFF url(../images/info.png) no-repeat scroll 10px center;
}
.notice {
background:#EEEEFF url(../images/info.png) no-repeat scroll 10px center;
}
.fielderror {
color:red;
font-weight:bold;
}
div.clearingdiv {
clear:both;
}

Binary file not shown.

View File

@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
"""Templates package for the application."""

View File

View File

@ -0,0 +1,16 @@
<html>
<body>
<form action="/auth/login" method="post">
<p class="error">${error}</p>
<div>
<span>Nom d'utilisateur</span>
<input name="username" type="text" value="${login}" />
</div>
<div>
<span>Nom d'utilisateur</span>
<input name="password" type="password" />
</div>
<input type="submit" value="connexion"/>
</form>
</body>
</html>

View File

View File

@ -0,0 +1,6 @@
<html
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude"
>
<span class="section_name">${error}</span>
</html>

View File

@ -0,0 +1,23 @@
<html
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="member_room_interface_edit_views.html" />
<head>
<link type="text/css" rel="Stylesheet" href="/css/common.css" />
<link type="text/css" rel="Stylesheet" href="/css/show.css" />
</head>
<body>
<div py:choose="member_ldap">
<span class="section_name" py:when="None">Entrée inexistante</span>
<div py:otherwise="">${member_view(member_ldap)}</div>
</div>
<div py:choose="room">
<span class="section_name" py:when="None">Pas de chambre associée</span>
<div py:otherwise="">${room_view(room)}</div>
</div>
<div py:choose="interface">
<span class="section_name" py:when="None">Pas d'interface associée</span>
<div py:otherwise="">${interface_view(interface)}</div>
</div>
</body>
</html>

View File

@ -0,0 +1,133 @@
<html
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude"
py:strip="">
<py:def function="member_view(member_ldap)">
<div class="section">
<span class="section_name show_section_name">${member_ldap.cn.first()}</span>
<div>
<div>
<span class="item_name">Fin de connexion</span>
<span py:choose="member_ldap.get('x-connectionEnd')">
<span py:when="None">Pas de fin de connexion</span>
<span py:otherwise="">${member_ldap.get("x-connectionEnd").first()}</span>
</span>
</div>
<div>
<span class="item_name">E-mail</span>
<input type="text" value="${member_ldap.mail.first('')}" />
</div>
<div>
<span class="item_name">Certificat</span>
<py:with vars="certif = member_ldap.get('x-certificatGiven')">
<span py:choose="">
<span py:when="certif is not None and certif.first() == 'TRUE'">oui</span>
<span py:otherwise="">non</span>
</span>
</py:with>
</div>
<div>
<span class="item_name">Commentaire</span>
<span py:choose="member_ldap.get('x-comment')">
<span py:when="None"></span>
<span py:otherwise="">${member_ldap.get("x-comment").first()}</span>
</span>
</div>
<div>
<span class="item_name">Cas spécial</span>
<py:with vars="cas_spec = member_ldap.get('x-specialCase')">
<span py:choose="">
<span py:when="cas_spec is not None and cas_spec.first() == 'TRUE'">oui</span>
<span py:otherwise="">non</span>
</span>
</py:with>
</div>
<div>
<span class="item_name">Wifi</span>
</div>
</div>
</div>
</py:def>
<py:def function="room_view(room, member_in = None)">
<div class="section">
<span class="section_name show_section_name">CHAMBRE ${room.cn.first()}</span>
<div>
<div py:if="member_in is not None">
<span class="item_name">Membre</span>
<span>
<a href="/show/member/${member_in.uid.first()}">${member_in.cn.first()}</a>
</span>
</div>
</div>
</div>
</py:def>
<py:def function="interface_view(interface, room_attached = None)">
<div class="section">
<span class="section_name show_section_name">INFOS INTERFACE</span>
<div>
<div>
<span class="item_name">Nombre de mac max</span>
<span>${interface.portsecmaxmac}</span>
</div>
<div>
<span class="item_name">Violation</span>
<span>${interface.portsecviolation}</span>
</div>
</div>
</div>
<div class="section">
<span class="section_name show_section_name">INFOS AVANCÉES</span>
<div>
<div>
<span class="item_name">ifNumber</span>
<span>${interface.ifnumber}</span>
</div>
<div>
<span class="item_name">ifAddress</span>
<span>${interface.ifaddress}</span>
</div>
<div>
<span class="item_name">ifType</span>
<span>${interface.iftype}</span>
</div>
<div>
<span class="item_name">Description</span>
<span>${interface.ifdescription}</span>
</div>
<div>
<span class="item_name">Etat</span>
<span py:choose="interface.ifoperstatus">
<span py:when="1">Up</span>
<span py:when="0">Down</span>
<span py:when="2">Error</span>
<span py:otherwise="">Inconnu</span>
</span>
</div>
<div>
<span class="item_name">Vitesse</span>
<span py:choose="interface.ifspeed">
<span py:when="10000000">10 Mb/s</span>
<span py:when="100000000">100 Mb/s</span>
<span py:when="1000000000">1 Gb/s</span>
<span py:otherwise="">interface.ifspeed</span>
</span>
</div>
<div>
<span class="item_name">Vlan</span>
<span>${interface.ifvlan}</span>
</div>
<div>
<span class="item_name">Native Vlan</span>
<span>${interface.ifnativevlan}</span>
</div>
<div>
<span class="item_name">SpanningTree Portfast</span>
<span>${interface.portfast}</span>
</div>
</div>
</div>
</py:def>
</html>

View File

@ -0,0 +1,19 @@
<html
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="member_room_interface_views.html" />
<head>
<link type="text/css" rel="Stylesheet" href="/css/common.css" />
<link type="text/css" rel="Stylesheet" href="/css/show.css" />
</head>
<body>
<div py:choose="room">
<span class="section_name" py:when="None">Pas de chambre associée</span>
<div py:otherwise="">${room_view(room, member_ldap)}</div>
</div>
<div py:choose="interface">
<span class="section_name" py:when="None">Pas d'interface associée</span>
<div py:otherwise="">${interface_view(interface)}</div>
</div>
</body>
</html>

View File

@ -0,0 +1,10 @@
<html xmlns:py="http://genshi.edgewall.org/" xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="login_widget.html" />
<body>
<div>${login_widget(user)}</div>
<h1>Test</h1>
<div py:for="item in materiel">
<span>${item.hostname}</span>
</div>
</body>
</html>

View File

@ -0,0 +1,17 @@
<html
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude"
py:strip=""
>
<py:def function="login_widget(user)">
<div py:choose="">
<div py:when="user is None">
<a href="${tg.url('/auth/login')}">Connection</a>
</div>
<div py:otherwise="">
<span>${user.attrs.cn.first()}</span>
<a href="${tg.url('/auth/logout')}">Deconnection</a>
</div>
</div>
</py:def>
</html>

View File

@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
"""Templates package for the application."""

View File

@ -0,0 +1,132 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="master.html" />
<head>
<meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
<title>Learning TurboGears 2.0: Quick guide to the Quickstart pages.</title>
</head>
<body>
${sidebar_top()}
${sidebar_bottom()}
<div id="getting_started">
<h2>Architectural basics of a quickstart TG2 site.</h2>
<p>The TG2 quickstart command produces this basic TG site. Here's how it works.</p>
<ol id="getting_started_steps">
<li class="getting_started">
<h3>Code my data model</h3>
<p> When you want a model for storing favorite links or wiki content,
the <strong>/model</strong> folder in your site is ready to go.</p>
<p> You can build a dynamic site without any data model at all. There
still be a default data-model template for you if you didn't enable
authentication and authorization in quickstart. If you enabled
it, you got auth data-model made for you.</p>
</li>
<li class="getting_started">
<h3>Design my URL structure</h3>
<p> The "<span class="code">root.py</span>" file under the
<strong>/controllers</strong> folder has your URLs. When you
called this url (<span class="code"><a href="${tg.url('/about')}">about</a></span>),
the command went through the RootController class to the
<span class="code">about</span><span class="code">()</span> method.</p>
<p> Those Python methods are responsible to create the dictionary of
variables that will be used in your web views (template).</p>
</li>
<li class="getting_started">
<h3>Reuse the web page elements</h3>
<p> A web page viewed by user could be constructed by single or
several reusable templates under <strong>/templates</strong>.
Take 'about' page for example, each reusable templates generating
a part of the page. We'll cover them in the order of where they are
found, listed near the top of the about.html template</p>
<p> <strong><span class="code">header.html</span></strong> - The
"header.html" template contains the HTML code to display the
'header': The blue gradient, TG2 logo, and some site text at the
top of every page it is included on. When the "about.html" template
is called, it includes this "header.html" template (and the others)
with a <span class="code">&lt;xi:include /&gt;</span> tag, part of
the Genshi templating system. The "header.html" template is not a
completely static HTML -- it also dynamically displays the current
page name with a Genshi template method called "replace" with the
code: <span class="code">&lt;span py:replace="page"/&gt;</span>.
It means replace this <span class="code">&lt;span /&gt;</span>
region with the contents found in the variable 'page' that has
been sent in the dictionary to this "about.html" template, and is
available through that namespace for use by this "header.html"
template. That's how it changes in the header depending on what
page you are visiting.
</p>
<p> <strong><span class="code">sidebars.html</span></strong> - The
sidebars (navigation areas on the right side of the page) are
generated as two separate <span class="code">py:def</span> blocks
in the "sidebars.html" template. The <span class="code">py:def</span>
construct is best thought of as a "macro" code... a simple way to
separate and reuse common code snippets. All it takes to include
these on the "about.html" page template is to write
<span class="code">
<br/><br/>
$${sidebar_top()}
<br/>
$${sidebar_bottom()}
<br/><br/>
</span> in the page where they are wanted. CSS styling (in
"/public/css/style.css") floats them off to the right side. You can
remove a sidebar or add more of them, and the CSS will place them one
atop the other.</p>
<p>This is, of course, also exactly how the header and footer
templates are also displayed in their proper places, but we'll
cover that in the "master.html" template below.</p>
<p>Oh, and in sidebar_top we've added a dynamic menu that shows the
link to this page at the top when you're at the "index" page, and
shows a link to the Home (index) page when you're here. Study the
"sidebars.html" template to see how we used
<span class="code">py:choose</span> for that.</p>
<p> <strong><span class="code">footer.html</span></strong> - The
"footer.html" block is simple, but also utilizes a special
"replace" method to set the current YEAR in the footer copyright
message. The code is:
<span class="code">&lt;span py:replace="now.strftime('%Y')"&gt;
</span> and it uses the variable "now" that was passed
in with the dictionary of variables. But because "now" is a
datetime object, we can use the Python
<span class="code">"strftime()"</span> method with the "replace"
call to say "Just Display The Year Here". Simple, elegant; we
format the date display in the template (the View in the
Model/View/Controller architecture) rather than formatting it in
the Controller method and sending it to the template as a string
variable.</p>
<p> <strong><span class="code">master.html</span></strong> - The
"master.html" template is called last, by design. The "master.html"
template controls the overall design of the page we're looking at,
calling first the "header" py:def macro, then the putting everything
from this "about.html" template into the "content" div, and
then calling the "footer" macro at the end. Thus the "master.html"
template provides the overall architecture for each page in this
site.</p>
<p>But why then shouldn't we call it first? Isn't it the most
important? Perhaps, but that's precisely why we call it LAST.
The "master.html" template needs to know where to find everything
else, everything that it will use in py:def macros to build the
page. So that means we call the other templates first, and then
call "master.html". </p>
<p>There's more to the "master.html" template... study it to see how
the &lt;title&gt; tags and static JS and CSS files are brought into
the page. Templating with Genshi is a powerful tool and we've only
scratched the surface. There are also a few little CSS tricks
hidden in these pages, like the use of a "clearingdiv" to make
sure that your footer stays below the sidebars and always looks
right. That's not TG2 at work, just CSS. You'll need all your
skills to build a fine web app, but TG2 will make the hard parts
easier so that you can concentrate more on good design and content
rather than struggling with mechanics.</p>
</li>
</ol>
<p>Good luck with TurboGears 2!</p>
</div>
</body>
</html>

View File

@ -0,0 +1,67 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="master.html" />
<head>
<meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
<title>Learning TurboGears 2.0: Quick guide to authentication.</title>
</head>
<body>
${sidebar_top()}
${sidebar_bottom()}
<div id="getting_started">
<h2>Authentication &amp; Authorization in a TG2 site.</h2>
<p>If you have access to this page, this means you have enabled authentication and authorization
in the quickstart to create your project.</p>
<p>
The paster command will have created a few specific controllers for you. But before you
go to play with those controllers you'll need to make sure your application has been
properly bootstapped.
This is dead easy, here is how to do this:
</p>
<span class="code">
paster setup-app development.ini
</span>
<p>
inside your application's folder and you'll get a database setup (using the preferences you have
set in your development.ini file). This database will also have been prepopulated with some
default logins/passwords so that you can test the secured controllers and methods.
</p>
<p>
To change the comportement of this setup-app command you just need to edit the <span class="code">websetup.py</span> file.
</p>
<p>
Now try to visiting the <a href="${tg.url('/manage_permission_only')}">manage_permission_only</a> URL. You will be challenged with a login/password form.
</p>
<p>
Only managers are authorized to visit this method. You will need to log-in using:
<p>
<span class="code">
login: manager
</span>
</p>
<p>
<span class="code">
password: managepass
</span>
</p>
</p>
<p>
Another protected resource is <a href="${tg.url('/editor_user_only')}">editor_user_only</a>. This one is protected by a different set of permissions.
You will need to be <span class="code">editor</span> with a password of <span class="code">editpass</span> to be able to access it.
</p>
<p>
The last kind of protected resource in this quickstarted app is a full so called <a href="${tg.url('/secc')}">secure controller</a>. This controller is protected globally.
Instead of having a @require decorator on each method, we have set an allow_only attribute at the class level. All the methods in this controller will
require the same level of access. You need to be manager to access <a href="${tg.url('/secc')}">secc</a> or <a href="${tg.url('/secc/some_where')}">secc/some_where</a>.
</p>
</div>
</body>
</html>

View File

@ -0,0 +1,20 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="master.html" />
<head>
<meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
<title>Sample Template, for looking at template locals</title>
</head>
<body>
<h1>All objects from locals():</h1>
<div py:for="item in sorted(locals()['data'].keys())">
${item}: ${repr(locals()['data'][item])}</div>
</body>
</html>

View File

@ -0,0 +1,19 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="master.html" />
<head>
<meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
<title>A ${code} Error has Occurred </title>
</head>
<body>
<h1>Error ${code}</h1>
<div>${XML(message)}</div>
</body>
</html>

View File

@ -0,0 +1,17 @@
<html xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude"
py:strip="">
<py:def function="footer">
<div id="footer">
<div class="flogo">
<img src="${tg.url('/images/under_the_hood_blue.png')}" alt="TurboGears" />
<p><a href="http://www.turbogears.org/2.0/">Powered by TurboGears 2</a></p>
</div>
<div class="foottext">
<p>TurboGears is a open source front-to-back web development
framework written in Python. Copyright (c) 2005-2008 </p>
</div>
<div class="clearingdiv"></div>
</div>
</py:def>
</html>

View File

@ -0,0 +1,12 @@
<html xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude"
py:strip="">
<py:def function="header">
<div id="header">
<h1>
Welcome to TurboGears 2
<span class="subtitle">The Python web metaframework</span>
</h1>
</div>
</py:def>
</html>

View File

@ -0,0 +1,40 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="master.html" />
<head>
<meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
<title>Welcome to TurboGears 2.0, standing on the
shoulders of giants, since 2007</title>
</head>
<body>
${sidebar_top()}
<div id="getting_started">
<h2>Presentation</h2>
<p>TurboGears 2 is rapid web application development toolkit designed to make your life easier.</p>
<ol id="getting_started_steps">
<li class="getting_started">
<h3>Code your data model</h3>
<p> Design your data model, Create the database, and Add some bootstrap data.</p>
</li>
<li class="getting_started">
<h3>Design your URL architecture</h3>
<p> Decide your URLs, Program your controller methods, Design your
templates, and place some static files (CSS and/or JavaScript). </p>
</li>
<li class="getting_started">
<h3>Distribute your app</h3>
<p> Test your source, Generate project documents, Build a distribution.</p>
</li>
</ol>
</div>
<div class="clearingdiv" />
<div class="notice"> Thank you for choosing TurboGears.
</div>
</body>
</html>

View File

@ -0,0 +1,24 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="master.html" />
<head>
<meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
<title>Login Form</title>
</head>
<body>
<div id="loginform">
<form action="${tg.url('/login_handler', came_from = came_from.encode('utf-8'), __logins = login_counter.encode('utf-8'))}" method="POST" class="loginfields">
<h2><span>Login</span></h2>
<label for="login">Username:</label><input type="text" id="login" name="login" class="text"></input><br/>
<label for="password">Password:</label><input type="password" id="password" name="password" class="text"></input>
<input type="submit" id="submit" value="Login" />
</form>
</div>
</body>
</html>

View File

@ -0,0 +1,44 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude"
py:strip="">
<xi:include href="header.html" />
<xi:include href="sidebars.html" />
<xi:include href="footer.html" />
<head py:match="head" py:attrs="select('@*')">
<meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
<title py:replace="''">Your title goes here</title>
<meta py:replace="select('*')"/>
<link rel="stylesheet" type="text/css" media="screen" href="${tg.url('/css/style.css')}" />
</head>
<body py:match="body" py:attrs="select('@*')">
${header()}
<ul id="mainmenu">
<li class="first"><a href="${tg.url('/')}" class="${('', 'active')[defined('page') and page==page=='index']}">Welcome</a></li>
<li><a href="${tg.url('/about')}" class="${('', 'active')[defined('page') and page==page=='about']}">About</a></li>
<li py:if="tg.auth_stack_enabled"><a href="${tg.url('/auth')}" class="${('', 'active')[defined('page') and page==page=='auth']}">Authentication</a></li>
<li><a href="http://groups.google.com/group/turbogears">Contact</a></li>
<span py:if="tg.auth_stack_enabled" py:strip="True">
<li py:if="not request.identity" id="login" class="loginlogout"><a href="${tg.url('/login')}">Login</a></li>
<li py:if="request.identity" id="login" class="loginlogout"><a href="${tg.url('/logout_handler')}">Logout</a></li>
<li py:if="request.identity" id="admin" class="loginlogout"><a href="${tg.url('/admin')}">Admin</a></li>
</span>
</ul>
<div id="content">
<py:if test="defined('page')">
<div class="currentpage">
Now Viewing: <span py:replace="page"/>
</div>
</py:if>
<py:with vars="flash=tg.flash_obj.render('flash', use_js=False)">
<div py:if="flash" py:content="XML(flash)" />
</py:with>
<div py:replace="select('*|text()')"/>
<!-- End of content -->
${footer()}
</div>
</body>
</html>

View File

@ -0,0 +1,38 @@
<html xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude"
py:strip="">
<py:def function="sidebar_top">
<div id="sb_top" class="sidebar">
<h2>Get Started with TG2</h2>
<ul class="links">
<li py:choose="">
<span py:when="defined('page') and page=='index'">
<a href="${tg.url('/about')}">About this page</a>
A quick guide to this TG2 site
</span>
<span py:otherwise=""><a href="${tg.url('/')}">Home</a> Back to your Quickstart Home page </span>
</li>
<li><a href="http://www.turbogears.org/2.0/docs/">TG2 Documents</a> - Read everything in the Getting Started section</li>
<li><a href="http://docs.turbogears.org/1.0">TG1 docs</a> (still useful, although a lot has changed for TG2) </li>
<li><a href="http://groups.google.com/group/turbogears"> Join the TG Mail List</a> for general TG use/topics </li>
</ul>
</div>
</py:def>
<py:def function="sidebar_bottom">
<div id="sb_bottom" class="sidebar">
<h2>Developing TG2</h2>
<ul class="links">
<li><a href="http://docs.turbogears.org/2.0/RoughDocs/">More TG2 Documents</a> in progress</li>
<li><a href="http://trac.turbogears.org/query?status=new&amp;status=assigned&amp;status=reopened&amp;group=type&amp;milestone=2.0&amp;order=priority">TG2 Trac tickets</a> What's happening now in TG2 development</li>
<li><a href="http://trac.turbogears.org/timeline">TG Dev timeline</a> (recent ticket updates, svn checkins, wiki changes)</li>
<li><a href="http://svn.turbogears.org/trunk">TG2 SVN repository</a> For checking out a copy</li>
<li><a href="http://turbogears.org/2.0/docs/main/Contributing.html#installing-the-development-version-of-turbogears-2-from-source">Follow these instructions</a> For installing your copy</li>
<li><a href="http://trac.turbogears.org/browser/trunk">TG2 Trac's svn view</a> In case you need a quick look</li>
<li><a href="http://groups.google.com/group/turbogears-trunk"> Join the TG-Trunk Mail List</a> for TG2 discuss/dev </li>
</ul>
</div>
</py:def>
</html>

View File

View File

@ -0,0 +1,61 @@
<html
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude"
>
<head>
<link rel="stylesheet" type="text/css" href="/css/rooms.css" />
<link rel="stylesheet" type="text/css" href="/css/common.css" />
</head>
<div class="rooms_legend">
<py:def function="display_stat(name)">
<div class="room_number ${name}">${"%03d" % stats[name]}</div>
</py:def>
<div>
${display_stat("ok_color")}
<span>ordinateurs connectés</span>
</div>
<div>
${display_stat("non_certif_color")}
<span>certifs manquants</span>
</div>
<div>
${display_stat("non_paye_color")}
<span>fins de connexions passées</span>
</div>
<div>
${display_stat("vide_color")}
<span>chambres vides</span>
</div>
</div>
<div class="area section">
<span class="section_name">PREVIEW</span>
<div py:if="defined('preview')">
<py:with vars="member_ldap, room_preview, interface_preview = preview">
<div class="room_number ${color_picker(interface_preview.ifdescription)}">
<span><a href="/show/room/${room_preview.uid.first()}">${room_preview.cn.first()}</a></span>
</div>
<div class="preview_part preview_name" py:choose="member_ldap is not None">
<span py:when="True"><a href="/show/member/${member_ldap.uid.first()}">${member_ldap.cn.first()}</a></span>
<span py:otherwise=""><a href="/show/room/${room_preview.uid.first()}">Chambre Vide</a></span>
</div>
<div class="preview_part" py:choose="">
<span py:when="interface_preview.ifoperstatus == 1" class="interface_status ok_color">Ordinateur branché</span>
<span py:when="interface_preview.portsecviolation == 1" class="interface_status violation_color">Violation mac</span>
<span py:when="interface_preview.ifoperstatus == 0" class="interface_status shut_color">Ordinateur débranché</span>
<span py:when="interface_preview.portsecstatus == 3" class="interface_status violation_color">Inteface éteinte</span>
</div>
</py:with>
</div>
</div>
<div py:for="area_name, floors in sorted_name(areas.iteritems())" class="area section">
<div>
<span class="section_name">${("Aile " + area_name).upper()}</span>
<div py:for="floor_name, rooms in reverse_sorted_name(floors.iteritems())" class="floor">
<span class="floor_name">étage ${floor_name}</span>
<div py:for="room, interface in rooms" class="room_number ${color_picker(interface.ifdescription)}">
<a href="/rooms/preview/${room.uid.first()}"><span>${room.cn.first()}</span></a>
</div>
</div>
</div>
</div>
</html>

View File

View File

@ -0,0 +1,6 @@
<html
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude"
>
<span class="section_name">${error}</span>
</html>

View File

@ -0,0 +1,23 @@
<html
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="member_room_interface_views.html" />
<head>
<link type="text/css" rel="Stylesheet" href="/css/common.css" />
<link type="text/css" rel="Stylesheet" href="/css/show.css" />
</head>
<body>
<div py:choose="member_ldap">
<span class="section_name" py:when="None">Entrée inexistante</span>
<div py:otherwise="">${member_view(member_ldap)}</div>
</div>
<div py:choose="room_ldap">
<span class="section_name" py:when="None">Pas de chambre associée</span>
<div py:otherwise="">${room_view(room_ldap)}</div>
</div>
<div py:choose="interface">
<span class="section_name" py:when="None">Pas d'interface associée</span>
<div py:otherwise="">${interface_view(interface)}</div>
</div>
</body>
</html>

View File

@ -0,0 +1,133 @@
<html
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude"
py:strip="">
<py:def function="member_view(member_ldap)">
<div class="section">
<span class="section_name show_section_name">${member_ldap.cn.first()}</span>
<div>
<div>
<span class="item_name">Fin de connexion</span>
<span py:choose="member_ldap.get('x-connectionEnd')">
<span py:when="None">Pas de fin de connexion</span>
<span py:otherwise="">${member_ldap.get("x-connectionEnd").first()}</span>
</span>
</div>
<div>
<span class="item_name">E-mail</span>
<span>${member_ldap.mail.first('')}</span>
</div>
<div>
<span class="item_name">Certificat</span>
<py:with vars="certif = member_ldap.get('x-certificatGiven')">
<span py:choose="">
<span py:when="certif is not None and certif.first() == 'TRUE'">oui</span>
<span py:otherwise="">non</span>
</span>
</py:with>
</div>
<div>
<span class="item_name">Commentaire</span>
<span py:choose="member_ldap.get('x-comment')">
<span py:when="None"></span>
<span py:otherwise="">${member_ldap.get("x-comment").first()}</span>
</span>
</div>
<div>
<span class="item_name">Cas spécial</span>
<py:with vars="cas_spec = member_ldap.get('x-specialCase')">
<span py:choose="">
<span py:when="cas_spec is not None and cas_spec.first() == 'TRUE'">oui</span>
<span py:otherwise="">non</span>
</span>
</py:with>
</div>
<div>
<span class="item_name">Wifi</span>
</div>
</div>
</div>
</py:def>
<py:def function="room_view(room, member_in = None)">
<div class="section">
<span class="section_name show_section_name">CHAMBRE ${room.cn.first()}</span>
<div>
<div py:if="member_in is not None">
<span class="item_name">Membre</span>
<span>
<a href="/show/member/${member_in.uid.first()}">${member_in.cn.first()}</a>
</span>
</div>
</div>
</div>
</py:def>
<py:def function="interface_view(interface, room_attached = None)">
<div class="section">
<span class="section_name show_section_name">INFOS INTERFACE</span>
<div>
<div>
<span class="item_name">Nombre de mac max</span>
<span>${interface.portsecmaxmac}</span>
</div>
<div>
<span class="item_name">Violation</span>
<span>${interface.portsecviolation}</span>
</div>
</div>
</div>
<div class="section">
<span class="section_name show_section_name">INFOS AVANCÉES</span>
<div>
<div>
<span class="item_name">ifNumber</span>
<span>${interface.ifnumber}</span>
</div>
<div>
<span class="item_name">ifAddress</span>
<span>${interface.ifaddress}</span>
</div>
<div>
<span class="item_name">ifType</span>
<span>${interface.iftype}</span>
</div>
<div>
<span class="item_name">Description</span>
<span>${interface.ifdescription}</span>
</div>
<div>
<span class="item_name">Etat</span>
<span py:choose="interface.ifoperstatus">
<span py:when="1">Up</span>
<span py:when="0">Down</span>
<span py:when="2">Error</span>
<span py:otherwise="">Inconnu</span>
</span>
</div>
<div>
<span class="item_name">Vitesse</span>
<span py:choose="interface.ifspeed">
<span py:when="10000000">10 Mb/s</span>
<span py:when="100000000">100 Mb/s</span>
<span py:when="1000000000">1 Gb/s</span>
<span py:otherwise="">interface.ifspeed</span>
</span>
</div>
<div>
<span class="item_name">Vlan</span>
<span>${interface.ifvlan}</span>
</div>
<div>
<span class="item_name">Native Vlan</span>
<span>${interface.ifnativevlan}</span>
</div>
<div>
<span class="item_name">SpanningTree Portfast</span>
<span>${interface.portfast}</span>
</div>
</div>
</div>
</py:def>
</html>

View File

@ -0,0 +1,19 @@
<html
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="member_room_interface_views.html" />
<head>
<link type="text/css" rel="Stylesheet" href="/css/common.css" />
<link type="text/css" rel="Stylesheet" href="/css/show.css" />
</head>
<body>
<div py:choose="room_ldap">
<span class="section_name" py:when="None">Pas de chambre associée</span>
<div py:otherwise="">${room_view(room_ldap, member_ldap)}</div>
</div>
<div py:choose="interface">
<span class="section_name" py:when="None">Pas d'interface associée</span>
<div py:otherwise="">${interface_view(interface)}</div>
</div>
</body>
</html>

View File

@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
"""Unit and functional test suite for Brie."""
from os import path
import sys
from tg import config
from paste.deploy import loadapp
from paste.script.appinstall import SetupCommand
from routes import url_for
from webtest import TestApp
from nose.tools import eq_
from brie import model
__all__ = ['setup_db', 'teardown_db', 'TestController', 'url_for']
def setup_db():
"""Method used to build a database"""
engine = config['pylons.app_globals'].sa_engine
model.init_model(engine)
model.metadata.create_all(engine)
def teardown_db():
"""Method used to destroy a database"""
engine = config['pylons.app_globals'].sa_engine
model.metadata.drop_all(engine)
class TestController(object):
"""
Base functional test case for the controllers.
The Brie application instance (``self.app``) set up in this test
case (and descendants) has authentication disabled, so that developers can
test the protected areas independently of the :mod:`repoze.who` plugins
used initially. This way, authentication can be tested once and separately.
Check brie.tests.functional.test_authentication for the repoze.who
integration tests.
This is the officially supported way to test protected areas with
repoze.who-testutil (http://code.gustavonarea.net/repoze.who-testutil/).
"""
application_under_test = 'main_without_authn'
def setUp(self):
"""Method called by nose before running each test"""
# Loading the application:
conf_dir = config.here
wsgiapp = loadapp('config:test.ini#%s' % self.application_under_test,
relative_to=conf_dir)
self.app = TestApp(wsgiapp)
# Setting it up:
test_file = path.join(conf_dir, 'test.ini')
cmd = SetupCommand('setup-app')
cmd.run([test_file])
def tearDown(self):
"""Method called by nose after running each test"""
# Cleaning up the database:
teardown_db()

View File

@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
"""Functional test suite for the controllers of the application."""

View File

@ -0,0 +1,86 @@
# -*- coding: utf-8 -*-
"""
Integration tests for the :mod:`repoze.who`-powered authentication sub-system.
As Brie grows and the authentication method changes, only these tests
should be updated.
"""
from brie.tests import TestController
class TestAuthentication(TestController):
"""
Tests for the default authentication setup.
By default in TurboGears 2, :mod:`repoze.who` is configured with the same
plugins specified by repoze.what-quickstart (which are listed in
http://code.gustavonarea.net/repoze.what-quickstart/#repoze.what.plugins.quickstart.setup_sql_auth).
As the settings for those plugins change, or the plugins are replaced,
these tests should be updated.
"""
application_under_test = 'main'
def test_forced_login(self):
"""
Anonymous users must be redirected to the login form when authorization
is denied.
Next, upon successful login they should be redirected to the initially
requested page.
"""
# Requesting a protected area
resp = self.app.get('/secc/', status=302)
assert resp.location.startswith('http://localhost/login')
# Getting the login form:
resp = resp.follow(status=200)
form = resp.form
# Submitting the login form:
form['login'] = u'manager'
form['password'] = 'managepass'
post_login = form.submit(status=302)
# Being redirected to the initially requested page:
assert post_login.location.startswith('http://localhost/post_login')
initial_page = post_login.follow(status=302)
assert 'authtkt' in initial_page.request.cookies, \
"Session cookie wasn't defined: %s" % initial_page.request.cookies
assert initial_page.location.startswith('http://localhost/secc/'), \
initial_page.location
def test_voluntary_login(self):
"""Voluntary logins must work correctly"""
# Going to the login form voluntarily:
resp = self.app.get('/login', status=200)
form = resp.form
# Submitting the login form:
form['login'] = u'manager'
form['password'] = 'managepass'
post_login = form.submit(status=302)
# Being redirected to the home page:
assert post_login.location.startswith('http://localhost/post_login')
home_page = post_login.follow(status=302)
assert 'authtkt' in home_page.request.cookies, \
'Session cookie was not defined: %s' % home_page.request.cookies
assert home_page.location == 'http://localhost/'
def test_logout(self):
"""Logouts must work correctly"""
# Logging in voluntarily the quick way:
resp = self.app.get('/login_handler?login=manager&password=managepass',
status=302)
resp = resp.follow(status=302)
assert 'authtkt' in resp.request.cookies, \
'Session cookie was not defined: %s' % resp.request.cookies
# Logging out:
resp = self.app.get('/logout_handler', status=302)
assert resp.location.startswith('http://localhost/post_logout')
# Finally, redirected to the home page:
home_page = resp.follow(status=302)
assert home_page.request.cookies.get('authtkt') == '', \
'Session cookie was not deleted: %s' % home_page.request.cookies
assert home_page.location == 'http://localhost/', home_page.location

View File

@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-
"""
Functional test suite for the root controller.
This is an example of how functional tests can be written for controllers.
As opposed to a unit-test, which test a small unit of functionality,
functional tests exercise the whole application and its WSGI stack.
Please read http://pythonpaste.org/webtest/ for more information.
"""
from nose.tools import assert_true
from brie.tests import TestController
class TestRootController(TestController):
def test_index(self):
response = self.app.get('/')
msg = 'TurboGears 2 is rapid web application development toolkit '\
'designed to make your life easier.'
# You can look for specific strings:
assert_true(msg in response)
# You can also access a BeautifulSoup'ed response in your tests
# (First run $ easy_install BeautifulSoup
# and then uncomment the next two lines)
#links = response.html.findAll('a')
#print links
#assert_true(links, "Mummy, there are no links here!")
def test_secc_with_manager(self):
"""Only the manager can access the secure controller"""
# Note how authentication is forged:
environ = {'REMOTE_USER': 'manager'}
resp = self.app.get('/secc', extra_environ=environ, status=200)
assert 'Secure Controller here' in resp.body, resp.body
def test_secc_with_editor(self):
"""The editor shouldn't access the secure controller"""
environ = {'REMOTE_USER': 'editor'}
self.app.get('/secc', extra_environ=environ, status=403)
# It's enough to know that authorization was denied with a 403 status
def test_secc_with_anonymous(self):
"""Anonymous users must not access the secure controller"""
self.app.get('/secc', status=401)
# It's enough to know that authorization was denied with a 401 status

View File

@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
"""Unit test suite for the models of the application."""
from nose.tools import assert_equals
from brie.model import DBSession
from brie.tests import setup_db, teardown_db
__all__ = ['ModelTest']
#Create an empty database before we start our tests for this module
def setup():
"""Function called by nose on module load"""
setup_db()
#Teardown that database
def teardown():
"""Function called by nose after all tests in this module ran"""
teardown_db()
class ModelTest(object):
"""Base unit test case for the models."""
klass = None
attrs = {}
def setup(self):
try:
new_attrs = {}
new_attrs.update(self.attrs)
new_attrs.update(self.do_get_dependencies())
self.obj = self.klass(**new_attrs)
DBSession.add(self.obj)
DBSession.flush()
return self.obj
except:
DBSession.rollback()
raise
def tearDown(self):
DBSession.rollback()
def do_get_dependencies(self):
"""Use this method to pull in other objects that need to be created for this object to be build properly"""
return {}
def test_create_obj(self):
pass
def test_query_obj(self):
obj = DBSession.query(self.klass).one()
for key, value in self.attrs.iteritems():
assert_equals(getattr(obj, key), value)

View File

@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
"""Test suite for the TG app's models"""
from nose.tools import eq_
from brie import model
from brie.tests.models import ModelTest
class TestGroup(ModelTest):
"""Unit test case for the ``Group`` model."""
klass = model.Group
attrs = dict(
group_name = u"test_group",
display_name = u"Test Group"
)
class TestUser(ModelTest):
"""Unit test case for the ``User`` model."""
klass = model.User
attrs = dict(
user_name = u"ignucius",
email_address = u"ignucius@example.org"
)
def test_obj_creation_username(self):
"""The obj constructor must set the user name right"""
eq_(self.obj.user_name, u"ignucius")
def test_obj_creation_email(self):
"""The obj constructor must set the email right"""
eq_(self.obj.email_address, u"ignucius@example.org")
def test_no_permissions_by_default(self):
"""User objects should have no permission by default."""
eq_(len(self.obj.permissions), 0)
def test_getting_by_email(self):
"""Users should be fetcheable by their email addresses"""
him = model.User.by_email_address(u"ignucius@example.org")
eq_(him, self.obj)
class TestPermission(ModelTest):
"""Unit test case for the ``Permission`` model."""
klass = model.Permission
attrs = dict(
permission_name = u"test_permission",
description = u"This is a test Description"
)

57
Brie/brie/websetup.py Normal file
View File

@ -0,0 +1,57 @@
# -*- coding: utf-8 -*-
"""Setup the Brie application"""
import logging
import transaction
from tg import config
from brie.config.environment import load_environment
__all__ = ['setup_app']
log = logging.getLogger(__name__)
def setup_app(command, conf, vars):
"""Place any commands to setup brie here"""
load_environment(conf.global_conf, conf.local_conf)
# Load the models
from brie import model
print "Creating tables"
model.metadata.create_all(bind=config['pylons.app_globals'].sa_engine)
manager = model.User()
manager.user_name = u'manager'
manager.display_name = u'Example manager'
manager.email_address = u'manager@somedomain.com'
manager.password = u'managepass'
model.DBSession.add(manager)
group = model.Group()
group.group_name = u'managers'
group.display_name = u'Managers Group'
group.users.append(manager)
model.DBSession.add(group)
permission = model.Permission()
permission.permission_name = u'manage'
permission.description = u'This permission give an administrative right to the bearer'
permission.groups.append(group)
model.DBSession.add(permission)
editor = model.User()
editor.user_name = u'editor'
editor.display_name = u'Example editor'
editor.email_address = u'editor@somedomain.com'
editor.password = u'editpass'
model.DBSession.add(editor)
model.DBSession.flush()
transaction.commit()
print "Successfully setup"

View File

@ -0,0 +1,63 @@
#from paste.deploy import appconfig
#from pylons import config
#from brie.config.environment import load_environment
#conf = appconfig('config:development.ini', relative_to='.')
#load_environment(conf.global_conf, conf.local_conf)
import os
import sys
from brie.config import ldap_config
from brie.lib.ldap_helper import *
from brie.lib.camembert_helpers import *
from brie.model import DBSession
from brie.model.camembert import *
#interface_name = os.environ["NAS_PORT_ID"]
#switch_ip = os.environ["NAS_IP_ADDRESS"]
#mac = os.environ["USERNAME"]
#bind = Ldap.connect("uid=roven.gabriel," + ldap_config.username_base_dn, "foobar")
bind = Ldap.connect("uid=admin,ou=Administrators,ou=TopologyManagement,o=netscaperoot", "t734DSSL61")
rooms_base_dn = "ou=chambres,dc=pacaterie,dc=u-psud,dc=fr"
interface_name = "FastEthernet0/43"
switch_ip = "172.17.24.2"
mac = "000f1f867189"
rooms = bind.search(rooms_base_dn, "(&(&(objectClass=pacaterieRoom)(x-switchInterface=" + interface_name + ")(x-switch=" + switch_ip + ")))")
def roomOrNone(member):
if member.has("roomNumber"):
return member.roomNumber.first()
return None
#end def
def isOk():
for room in rooms:
print room.get("x-switchInterface").first()
if room.has("x-memberIn"):
print room.get("x-memberIn").first()
associated_member_machine = bind.search_first(room.get("x-memberIn").first(), "(uid=" + mac + ")")
print associated_member_machine.uid.first()
return associated_member_machine is not None
#end if
#end for
return False
#end def
ok = isOk()
if ok:
sys.exit(0)
else:
sys.exit(1)

0
Brie/devdata.db Normal file
View File

129
Brie/development.ini Normal file
View File

@ -0,0 +1,129 @@
#
# Brie - Pylons development environment configuration
#
# The %(here)s variable will be replaced with the parent directory of this file
#
# This file is for deployment specific config options -- other configuration
# that is always required for the app is done in the config directory,
# and generally should not be modified by end users.
[DEFAULT]
debug = true
# Uncomment and replace with the address which should receive any error reports
#email_to = you@yourdomain.com
smtp_server = localhost
error_email_from = paste@localhost
[server:main]
use = egg:Paste#http
host = 0.0.0.0
port = 9000
[app:main]
use = egg:Brie
full_stack = true
#lang = ru
cache_dir = %(here)s/data
beaker.session.key = brie
beaker.session.secret = somesecret
# If you'd like to fine-tune the individual locations of the cache data dirs
# for the Cache data, or the Session saves, un-comment the desired settings
# here:
#beaker.cache.data_dir = %(here)s/data/cache
#beaker.session.data_dir = %(here)s/data/sessions
# pick the form for your database
# %(here) may include a ':' character on Windows environments; this can
# invalidate the URI when specifying a SQLite db via path name
sqlalchemy.url=postgresql://camembert:CamembertDB%40Pacat@archange.pacaterie.u-psud.fr:5432/camembert
# sqlalchemy.url=mysql://username:password@hostname:port/databasename
# If you have sqlite, here's a simple default to get you started
# in development
#sqlalchemy.url = sqlite:///%(here)s/devdata.db
#echo shouldn't be used together with the logging module.
#sqlalchemy.echo = false
#sqlalchemy.echo_pool = false
#sqlalchemy.pool_recycle = 3600
# if you are using Mako and want to be able to reload
# the mako template from disk during the development phase
# you should say 'true' here
# This option is only used for mako templating engine
# WARNING: if you want to deploy your application using a zipped egg
# (ie: if your application's setup.py defines zip-safe=True, then you
# MUST put "false" for the production environment because there will
# be no disk and real files to compare time with.
# On the contrary if your application defines zip-safe=False and is
# deployed in an unzipped manner, then you can leave this option to true
templating.mako.reloadfromdisk = true
# the compiled template dir is a directory that must be readable by your
# webserver. It will be used to store the resulting templates once compiled
# by the TemplateLookup system.
# During development you generally don't need this option since paste's HTTP
# server will have access to you development directories, but in production
# you'll most certainly want to have apache or nginx to write in a directory
# that does not contain any source code in any form for obvious security reasons.
#
#templating.mako.compiled_templates_dir = /some/dir/where/webserver/has/access
# 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
# Add additional loggers, handlers, formatters here
# Uses python's logging config file format
# http://docs.python.org/lib/logging-config-fileformat.html
[loggers]
keys = root, brie, sqlalchemy, auth
[handlers]
keys = console
[formatters]
keys = generic
# If you create additional loggers, add them as a key to [loggers]
[logger_root]
level = INFO
handlers = console
[logger_brie]
level = DEBUG
handlers =
qualname = brie
[logger_sqlalchemy]
level = INFO
handlers =
qualname = sqlalchemy.engine
# "level = INFO" logs SQL queries.
# "level = DEBUG" logs SQL queries and results.
# "level = WARN" logs neither. (Recommended for production systems.)
# A logger for authentication, identification and authorization -- this is
# repoze.who and repoze.what:
[logger_auth]
level = WARN
handlers =
qualname = auth
# If you create additional handlers, add them as a key to [handlers]
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
# If you create additional formatters, add them as a key to [formatters]
[formatter_generic]
format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S

14
Brie/ez_setup/README.txt Normal file
View File

@ -0,0 +1,14 @@
This directory exists so that Subversion-based projects can share a single
copy of the ``ez_setup`` bootstrap module for ``setuptools``, and have it
automatically updated in their projects when ``setuptools`` is updated.
For your convenience, you may use the following svn:externals definition::
ez_setup svn://svn.eby-sarna.com/svnroot/ez_setup
You can set this by executing this command in your project directory::
svn propedit svn:externals .
And then adding the line shown above to the file that comes up for editing.
Then, whenever you update your project, ``ez_setup`` will be updated as well.

229
Brie/ez_setup/__init__.py Normal file
View File

@ -0,0 +1,229 @@
#!/usr/bin/python
"""Bootstrap setuptools installation
If you want to use setuptools in your package's setup.py, just include this
file in the same directory with it, and add this to the top of your setup.py::
from ez_setup import use_setuptools
use_setuptools()
If you want to require a specific version of setuptools, set a download
mirror, or use an alternate download directory, you can do so by supplying
the appropriate options to ``use_setuptools()``.
This file can also be run as a script to install or upgrade setuptools.
"""
import sys
DEFAULT_VERSION = "0.6c7"
DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
md5_data = {
'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2',
'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e',
'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372',
}
import sys, os
def _validate_md5(egg_name, data):
if egg_name in md5_data:
from md5 import md5
digest = md5(data).hexdigest()
if digest != md5_data[egg_name]:
print >>sys.stderr, (
"md5 validation of %s failed! (Possible download problem?)"
% egg_name
)
sys.exit(2)
return data
def use_setuptools(
version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
download_delay=15
):
"""Automatically find/download setuptools and make it available on sys.path
`version` should be a valid setuptools version number that is available
as an egg for download under the `download_base` URL (which should end with
a '/'). `to_dir` is the directory where setuptools will be downloaded, if
it is not already available. If `download_delay` is specified, it should
be the number of seconds that will be paused before initiating a download,
should one be required. If an older version of setuptools is installed,
this routine will print a message to ``sys.stderr`` and raise SystemExit in
an attempt to abort the calling script.
"""
try:
import setuptools
if setuptools.__version__ == '0.0.1':
print >>sys.stderr, (
"You have an obsolete version of setuptools installed. Please\n"
"remove it from your system entirely before rerunning this script."
)
sys.exit(2)
except ImportError:
egg = download_setuptools(version, download_base, to_dir, download_delay)
sys.path.insert(0, egg)
import setuptools; setuptools.bootstrap_install_from = egg
import pkg_resources
try:
pkg_resources.require("setuptools>="+version)
except pkg_resources.VersionConflict, e:
# XXX could we install in a subprocess here?
print >>sys.stderr, (
"The required version of setuptools (>=%s) is not available, and\n"
"can't be installed while this script is running. Please install\n"
" a more recent version first.\n\n(Currently using %r)"
) % (version, e.args[0])
sys.exit(2)
def download_setuptools(
version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
delay = 15
):
"""Download setuptools from a specified location and return its filename
`version` should be a valid setuptools version number that is available
as an egg for download under the `download_base` URL (which should end
with a '/'). `to_dir` is the directory where the egg will be downloaded.
`delay` is the number of seconds to pause before an actual download attempt.
"""
import urllib2, shutil
egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
url = download_base + egg_name
saveto = os.path.join(to_dir, egg_name)
src = dst = None
if not os.path.exists(saveto): # Avoid repeated downloads
try:
from distutils import log
if delay:
log.warn("""
---------------------------------------------------------------------------
This script requires setuptools version %s to run (even to display
help). I will attempt to download it for you (from
%s), but
you may need to enable firewall access for this script first.
I will start the download in %d seconds.
(Note: if this machine does not have network access, please obtain the file
%s
and place it in this directory before rerunning this script.)
---------------------------------------------------------------------------""",
version, download_base, delay, url
); from time import sleep; sleep(delay)
log.warn("Downloading %s", url)
src = urllib2.urlopen(url)
# Read/write all in one block, so we don't create a corrupt file
# if the download is interrupted.
data = _validate_md5(egg_name, src.read())
dst = open(saveto,"wb"); dst.write(data)
finally:
if src: src.close()
if dst: dst.close()
return os.path.realpath(saveto)
def main(argv, version=DEFAULT_VERSION):
"""Install or upgrade setuptools and EasyInstall"""
try:
import setuptools
except ImportError:
egg = None
try:
egg = download_setuptools(version, delay=0)
sys.path.insert(0,egg)
from setuptools.command.easy_install import main
return main(list(argv)+[egg]) # we're done here
finally:
if egg and os.path.exists(egg):
os.unlink(egg)
else:
if setuptools.__version__ == '0.0.1':
# tell the user to uninstall obsolete version
use_setuptools(version)
req = "setuptools>="+version
import pkg_resources
try:
pkg_resources.require(req)
except pkg_resources.VersionConflict:
try:
from setuptools.command.easy_install import main
except ImportError:
from easy_install import main
main(list(argv)+[download_setuptools(delay=0)])
sys.exit(0) # try to force an exit
else:
if argv:
from setuptools.command.easy_install import main
main(argv)
else:
print "Setuptools version",version,"or greater has been installed."
print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
def update_md5(filenames):
"""Update our built-in md5 registry"""
import re
from md5 import md5
for name in filenames:
base = os.path.basename(name)
f = open(name,'rb')
md5_data[base] = md5(f.read()).hexdigest()
f.close()
data = [" %r: %r,\n" % it for it in md5_data.items()]
data.sort()
repl = "".join(data)
import inspect
srcfile = inspect.getsourcefile(sys.modules[__name__])
f = open(srcfile, 'rb'); src = f.read(); f.close()
match = re.search("\nmd5_data = {\n([^}]+)}", src)
if not match:
print >>sys.stderr, "Internal error!"
sys.exit(2)
src = src[:match.start(1)] + repl + src[match.end(1):]
f = open(srcfile,'w')
f.write(src)
f.close()
if __name__=='__main__':
if len(sys.argv)>2 and sys.argv[1]=='--md5update':
update_md5(sys.argv[2:])
else:
main(sys.argv[1:])

1168
Brie/fo Normal file

File diff suppressed because it is too large Load Diff

32
Brie/foo Normal file
View File

@ -0,0 +1,32 @@
Boubakar OUATTARA
boubakar.ouattara
chambre 456 etage 4 aile nord
Boubakar OUATTARA
Tocha ZAABAY
tocha.zaabay
chambre 465 etage 4 aile nord
Tocha ZAABAY
Claude ALGOURDIN
claude.algourdin
chambre 418 etage 4 aile sud
Claude ALGOURDIN
Rachid CHEDRA
rachid.chedra
chambre 217 etage 2 aile sud
Rachid CHEDRA
Guillaume LETOUPIN
guillaume.letoupin
chambre 232 etage 2 aile sud
Guillaume LETOUPIN
erica MINUZ
erica.minuz
chambre 374 etage 3 aile nord
Erica MINUZ
FLAVIA MACHADO DOMINGUES
flavia.machadodomingue
chambre 452 etage 4 aile nord
flavia.machadodomingue not found
SOPHIA ID SALAH
sophia.idsalah
chambre 360 etage 3 aile nord
Sophia ID SALAH

130
Brie/migrate_machines.py Normal file
View File

@ -0,0 +1,130 @@
from paste.deploy import appconfig
from pylons import config
from brie.config.environment import load_environment
conf = appconfig('config:development.ini', relative_to='.')
load_environment(conf.global_conf, conf.local_conf)
from brie.config import ldap_config
from brie.lib.ldap_helper import *
from brie.lib.camembert_helpers import *
from brie.model import DBSession
from brie.model.camembert import *
#bind = Ldap.connect("uid=roven.gabriel," + ldap_config.username_base_dn, "foobar")
bind = Ldap.connect("uid=admin,ou=Administrators,ou=TopologyManagement,o=netscaperoot", "t734DSSL61")
members = bind.search(ldap_config.username_base_dn, "(objectClass=pacatnetMember)")
def roomOrNone(member):
if member.has("roomNumber"):
return member.roomNumber.first()
return None
#end def
def first(items):
for item in items:
return item
return None
#end def
nextuid = 30000
base_chambres_dn = "ou=chambres," + ldap_config.base_dn
results = DBSession.query(UserPacaterie)
for user in results:
uid = Translations.to_uid(user.prenom, user.nom)
print uid
member = bind.search_first(ldap_config.username_base_dn, "(uid=" + uid + ")")
print member.dn
machines = DBSession.query(Computer).filter(Computer.iduser == user.iduser)
machine_id = 1
for machine in machines:
print machine_id
print machine.name
print machine.mac
print machine.ip
machine_dn = "cn=" + str(machine_id) + "," + member.dn
existant = bind.search_first(machine_dn, "(objectClass=*)")
if existant is not None:
bind.delete_entry_subtree(existant.dn)
print "deleted : " + existant.dn
print machine_dn
machine_attributes = {
"objectClass" : ["top", "organizationalRole"],
"cn" : str(machine_id)
}
bind.add_entry(machine_dn, machine_attributes)
dhcp_dn = "cn=" + str(machine.name) + "," + machine_dn
print dhcp_dn
dhcp_attributes = {
"objectClass" : ["top", "uidObject", "dhcpHost"],
"cn" : str(machine.name),
"uid" : "machine_membre",
"dhcpHWAddress" : str("ethernet " + machine.mac),
"dhcpStatements" : str("fixed-address " + machine.name)
}
bind.add_entry(dhcp_dn, dhcp_attributes)
mac_auth_dn = "cn=mac," + machine_dn
print mac_auth_dn
flat_mac = str(machine.mac).replace(":", "")
print flat_mac
mac_auth_attributes = {
"objectClass" : ["top", "organizationalRole", "simpleSecurityObject", "uidObject"],
"cn" : "mac",
"uid" : flat_mac,
"userPassword" : flat_mac
}
bind.add_entry(mac_auth_dn, mac_auth_attributes)
dns_dn = "dlzHostName=" + machine.name + "," + machine_dn
print dns_dn
dns_attributes = {
"objectClass" : ["top", "dlzAbstractRecord", "dlzGenericRecord"],
"dlzData" : str(machine.ip),
"dlzHostName" : str(machine.name),
"dlzRecordId" : "1",
"dlzTTL" : "3600",
"dlzType" : "A"
}
bind.add_entry(dns_dn, dns_attributes)
machine_id = 1 + machine_id
#end for machine
#end for

View File

@ -0,0 +1,107 @@
from paste.deploy import appconfig
from pylons import config
from brie.config.environment import load_environment
conf = appconfig('config:development.ini', relative_to='.')
load_environment(conf.global_conf, conf.local_conf)
from brie.config import ldap_config
from brie.lib.ldap_helper import *
from brie.lib.camembert_helpers import *
from brie.model import DBSession
from brie.model.camembert import *
#bind = Ldap.connect("uid=roven.gabriel," + ldap_config.username_base_dn, "foobar")
bind = Ldap.connect("uid=admin,ou=Administrators,ou=TopologyManagement,o=netscaperoot", "t734DSSL61")
members = bind.search(ldap_config.username_base_dn, "(objectClass=pacatnetMember)")
def roomOrNone(member):
if member.has("roomNumber"):
return member.roomNumber.first()
return None
#end def
def first(items):
for item in items:
return item
return None
#end def
nextuid = 30000
base_chambres_dn = "ou=chambres," + ldap_config.base_dn
results = DBSession.query(Room)
for room in results:
ldap_room = bind.search_first(base_chambres_dn, "(cn=" + room.name + ")")
member_of = {
"x-memberIn" : None
}
try:
bind.delete_attr(ldap_room.dn, member_of)
print "deleted " + ldap_room.dn
except:
pass
#end try
#end for
results = DBSession.query(UserPacaterie, Room).filter(UserPacaterie.idroom == Room.idroom)
for (user, room) in results:
uid = Translations.to_uid(user.prenom, user.nom)
print uid
member = bind.search_first(ldap_config.username_base_dn, "(uid=" + uid + ")")
print room.name
member_dn = ""
if member is None:
member_dn = "uid=" + uid + ",ou=2012," + ldap_config.username_base_dn
mail = user.mail
if mail is None:
mail = ""
attributes = {
"objectClass" : ["top", "person", "organizationalPerson", "inetOrgPerson", "pacatnetMember", "pykotaAccount", "posixAccount"],
"uid" :(uid).encode("utf-8"),
"cn" : (user.prenom + " " + user.nom.upper()).encode("utf-8"),
"sn" : (user.nom.upper()).encode("utf-8"),
"givenName" : (user.prenom).encode("utf-8"),
"uidNumber" : str(-1),
"gidNumber" : "10000",
"homeDirectory" : ("/net/home/" + uid).encode("utf-8"),
"mail" : (mail).encode("utf-8"),
"loginShell" : "/usr/bin/zsh".encode("utf-8")
}
bind.add_entry(member_dn, attributes)
else:
member_dn = member.dn
#end if
ldap_room = bind.search_first(base_chambres_dn, "(cn=" + room.name + ")")
print ldap_room.dn
print member_dn
new_member_of = {
"x-memberIn" : str(member_dn)
}
bind.replace_attr(ldap_room.dn, new_member_of)
#end for

View File

@ -0,0 +1,70 @@
from paste.deploy import appconfig
from pylons import config
from brie.config.environment import load_environment
conf = appconfig('config:development.ini', relative_to='.')
load_environment(conf.global_conf, conf.local_conf)
from brie.config import ldap_config
from brie.lib.ldap_helper import *
from brie.lib.camembert_helpers import *
from brie.model import DBSession
from brie.model.camembert import *
#bind = Ldap.connect("uid=roven.gabriel,ou=2011," + ldap_config.username_base_dn, "foobar")
bind = Ldap.connect("uid=admin,ou=Administrators,ou=TopologyManagement,o=netscaperoot", "t734DSSL61")
members = bind.search(ldap_config.username_base_dn, "(&(ou:dn:=2011)(objectClass=pacatnetMember))")
def roomOrNone(member):
if member.has("roomNumber"):
return member.roomNumber.first()
return None
#end def
def first(items):
for item in items:
return item
return None
#end def
nextuid = 30000
def add(uid, user, room_dn, nextuid):
user_dn = "uid=" + uid + "," + ldap_config.username_base_dn
mail = user.mail
if mail is None:
mail = ""
attributes = {
"objectClass" : ["top", "person", "organizationalPerson", "inetOrgPerson", "pacatnetMember", "pykotaAccount", "posixAccount"],
"uid" :(uid).encode("utf-8"),
"cn" : (user.prenom + " " + user.nom.upper()).encode("utf-8"),
"sn" : (user.nom.upper()).encode("utf-8"),
"givenName" : (user.prenom).encode("utf-8"),
"uidNumber" : str(nextuid),
"gidNumber" : "10000",
"homeDirectory" : ("/net/home/" + uid).encode("utf-8"),
"mail" : (mail).encode("utf-8"),
"x-room" : (room_dn).encode("utf-8"),
"loginShell" : "/usr/bin/zsh".encode("utf-8")
}
bind.add_entry(user_dn, attributes)
#end def
filestream = open("people_to_change.txt", "r")
for line in filestream:
uid = line.strip()
print "++" + uid + "++"
print "ou=2010," + ldap_config.username_base_dn
result = bind.search_first(ldap_config.username_base_dn, "(uid=" + uid + ")")
bind.rename_entry(result.dn, "uid=" + result.uid.first(), "ou=2010," + ldap_config.username_base_dn)
#end for

80
Brie/migrate_rooms.py Normal file
View File

@ -0,0 +1,80 @@
from paste.deploy import appconfig
from pylons import config
from brie.config.environment import load_environment
conf = appconfig('config:development.ini', relative_to='.')
load_environment(conf.global_conf, conf.local_conf)
from brie.config import ldap_config
from brie.lib.ldap_helper import *
from brie.model import DBSession
from brie.model.camembert import *
import socket
#bind = Ldap.connect("uid=roven.gabriel,ou=2010," + ldap_config.username_base_dn, "foobar")
bind = Ldap.connect("uid=admin,ou=Administrators,ou=TopologyManagement,o=netscaperoot", "t734DSSL61")
members = bind.search(ldap_config.username_base_dn, "(objectClass=pacatnetMember)")
def roomOrNone(member):
if member.has("roomNumber"):
return member.roomNumber.first()
return None
#end def
def first(items):
for item in items:
return item
return None
#end def
def area(room_number):
floor_number = room_number % 100
if floor_number <= 33:
return "sud"
else:
return "nord"
#end if
#end def
results = DBSession.query(Room, Materiel, Interface).filter(Room.idinterface == Interface.idinterface).filter(Interface.idmateriel == Materiel.idmateriel)
for room, materiel, interface in results:
aile = area(room.idroom)
etage = str(room.idroom / 100)
base_chambres_dn = "ou=chambres," + ldap_config.base_dn
other_dn = "cn=" + etage + ",cn=" + aile + "," + base_chambres_dn
full_dn = "cn=" + room.name + "," + other_dn
print room
switch_ip = socket.gethostbyname(str(materiel.hostname))
attributes = {
"objectClass" : "pacaterieRoom",
"cn" : str(room.name),
"x-switch" : str(switch_ip),
"x-switchInterface" : str(interface.ifname)
}
print str(room.idinterface)
print full_dn
try:
bind.replace_attr(full_dn, attributes)
except ldap.NO_SUCH_OBJECT:
bind.add_entry(full_dn, attributes)
# print attributes
#end for

59
Brie/migrate_switch.py Normal file
View File

@ -0,0 +1,59 @@
from paste.deploy import appconfig
from pylons import config
from brie.config.environment import load_environment
conf = appconfig('config:development.ini', relative_to='.')
load_environment(conf.global_conf, conf.local_conf)
from brie.config import ldap_config
from brie.lib.ldap_helper import *
from brie.lib.camembert_helpers import *
from brie.model import DBSession
from brie.model.camembert import *
#bind = Ldap.connect("uid=roven.gabriel," + ldap_config.username_base_dn, "foobar")
bind = Ldap.connect("uid=admin,ou=Administrators,ou=TopologyManagement,o=netscaperoot", "t734DSSL61")
members = bind.search(ldap_config.username_base_dn, "(objectClass=pacatnetMember)")
def roomOrNone(member):
if member.has("roomNumber"):
return member.roomNumber.first()
return None
#end def
def first(items):
for item in items:
return item
return None
#end def
nextuid = 30000
base_chambres_dn = "ou=chambres," + ldap_config.base_dn
results = DBSession.query(Room, Interface).filter(Room.idinterface == Interface.idinterface).order_by(Room.name)
for (room, interface) in results:
ldap_room = bind.search_first(base_chambres_dn, "(cn=" + room.name + ")")
print ldap_room.dn
switch_id = {
"x-switchInterface" : str(interface.idinterface)
}
bind.replace_attr(ldap_room.dn, switch_id)
uid_attr = {
"objectClass" : "uidObject",
"uid" : str(room.idroom)
}
bind.add_attr(ldap_room.dn, uid_attr)
#end for

32
Brie/setup.cfg Normal file
View File

@ -0,0 +1,32 @@
[egg_info]
tag_build = dev
tag_svn_revision = true
[easy_install]
find_links = http://www.pylonshq.com/download/
[nosetests]
with-pylons=test.ini
# Babel configuration
[compile_catalog]
domain = brie
directory = brie/i18n
statistics = true
[extract_messages]
add_comments = TRANSLATORS:
output_file = brie/i18n/brie.pot
width = 80
keywords = l_
[init_catalog]
domain = brie
input_file = brie/i18n/brie.pot
output_dir = brie/i18n
[update_catalog]
domain = brie
input_file = brie/i18n/brie.pot
output_dir = brie/i18n
previous = true

49
Brie/setup.py Normal file
View File

@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-
try:
from setuptools import setup, find_packages
except ImportError:
from ez_setup import use_setuptools
use_setuptools()
from setuptools import setup, find_packages
setup(
name='Brie',
version='0.1',
description='',
author='',
author_email='',
#url='',
install_requires=[
"TurboGears2 >= 2.0b7",
"Catwalk >= 2.0.2",
"Babel >=0.9.4",
#can be removed iif use_toscawidgets = False
"toscawidgets >= 0.9.7.1",
"zope.sqlalchemy >= 0.4 ",
"repoze.tm2 >= 1.0a4",
"repoze.what-quickstart >= 1.0",
],
setup_requires=["PasteScript >= 1.7"],
paster_plugins=['PasteScript', 'Pylons', 'TurboGears2', 'tg.devtools'],
packages=find_packages(exclude=['ez_setup']),
include_package_data=True,
test_suite='nose.collector',
tests_require=['WebTest', 'BeautifulSoup'],
package_data={'brie': ['i18n/*/LC_MESSAGES/*.mo',
'templates/*/*',
'public/*/*']},
message_extractors={'brie': [
('**.py', 'python', None),
('templates/**.mako', 'mako', None),
('templates/**.html', 'genshi', None),
('public/**', 'ignore', None)]},
entry_points="""
[paste.app_factory]
main = brie.config.middleware:make_app
[paste.app_install]
main = pylons.util:PylonsInstaller
""",
)

26
Brie/test.ini Normal file
View File

@ -0,0 +1,26 @@
#
# Brie - TurboGears 2 testing 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 = you@yourdomain.com
smtp_server = localhost
error_email_from = paste@localhost
[server:main]
use = egg:Paste#http
host = 0.0.0.0
port = 5000
[app:main]
sqlalchemy.url = sqlite:///:memory:
use = config:development.ini
[app:main_without_authn]
use = main
skip_authentication = True
# Add additional test specific configuration options as necessary.