Просмотр исходного кода

* infrastructure for authkit

* menu generation code
* improved templates
* database and basic role setup in websetup.py
master
Jan Dittberner 12 лет назад
Родитель
Сommit
55124e861f
20 измененных файлов: 434 добавлений и 71 удалений
  1. +8
    -1
      development.ini
  2. +3
    -0
      gnuviechadminweb/config/middleware.py
  3. +22
    -0
      gnuviechadminweb/controllers/login.py
  4. +13
    -0
      gnuviechadminweb/controllers/menu.py
  5. +4
    -2
      gnuviechadminweb/lib/base.py
  6. +200
    -0
      gnuviechadminweb/model/GVAUsers.py
  7. +3
    -2
      gnuviechadminweb/model/__init__.py
  8. +33
    -61
      gnuviechadminweb/model/menu.py
  9. +34
    -0
      gnuviechadminweb/model/tables.py
  10. +30
    -0
      gnuviechadminweb/model/user.py
  11. +10
    -0
      gnuviechadminweb/public/stylesheets/gva.css
  12. +11
    -0
      gnuviechadminweb/templates/loginform.mako
  13. +3
    -1
      gnuviechadminweb/templates/main.mako
  14. +6
    -0
      gnuviechadminweb/templates/menuindex.mako
  15. +19
    -2
      gnuviechadminweb/templates/site.mako
  16. +7
    -0
      gnuviechadminweb/tests/functional/test_auth.py
  17. +7
    -0
      gnuviechadminweb/tests/functional/test_login.py
  18. +7
    -0
      gnuviechadminweb/tests/functional/test_menu.py
  19. +13
    -1
      gnuviechadminweb/websetup.py
  20. +1
    -1
      setup.py

+ 8
- 1
development.ini Просмотреть файл

@@ -20,7 +20,7 @@ use = egg:gnuviechadminweb
full_stack = true
cache_dir = %(here)s/data
beaker.session.key = gnuviechadminweb
beaker.session.secret = somesecret
beaker.session.secret = realy secret data

# 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
@@ -35,6 +35,13 @@ beaker.session.secret = somesecret

sqlalchemy.url = sqlite:///%(here)s/gvaweb.sqlite

authkit.setup.method = form, cookie
authkit.form.authenticate.user.type = gnuviechadminweb.model:GVAUsers
authkit.form.authenticate.user.data = gnuviechadminweb.model.meta
authkit.form.authenticate.user.encrypt = authkit.users:md5
authkit.cookie.secret = really secret data
authkit.cookie.signoutpath = /login/logout


# Logging configuration
[loggers]


+ 3
- 0
gnuviechadminweb/config/middleware.py Просмотреть файл

@@ -10,6 +10,8 @@ from pylons.middleware import error_mapper, ErrorDocuments, ErrorHandler, \
StaticJavascripts
from pylons.wsgiapp import PylonsApp

import authkit.authenticate

from gnuviechadminweb.config.environment import load_environment

def make_app(global_conf, full_stack=True, **app_conf):
@@ -43,6 +45,7 @@ def make_app(global_conf, full_stack=True, **app_conf):
app = ErrorHandler(app, global_conf, error_template=error_template,
**config['pylons.errorware'])

app = authkit.authenticate.middleware(app, app_conf)
# Display error documents for 401, 403, 404 status codes (and
# 500 when debug is disabled)
app = ErrorDocuments(app, global_conf, mapper=error_mapper, **app_conf)


+ 22
- 0
gnuviechadminweb/controllers/login.py Просмотреть файл

@@ -0,0 +1,22 @@
import logging

from gnuviechadminweb.lib.base import *
from authkit.permissions import ValidAuthKitUser
from authkit.authorize.pylons_adaptors import authorize

log = logging.getLogger(__name__)

class LoginController(BaseController):

def index(self):
# Return a rendered template
return render('/loginform.mako')

@authorize(ValidAuthKitUser())
def login(self):
c.messages['messages'].append('Successfully logged in.')
return render('/main.mako')

def logout(self):
c.messages['messages'].append('You are logged out.')
return render('/main.mako')

+ 13
- 0
gnuviechadminweb/controllers/menu.py Просмотреть файл

@@ -0,0 +1,13 @@
import logging

from gnuviechadminweb.lib.base import *
from authkit.permissions import HasAuthKitRole
from authkit.authorize.pylons_adaptors import authorize

log = logging.getLogger(__name__)

class MenuController(BaseController):
@authorize(HasAuthKitRole(["menuedit"]))
def index(self):
# Return a rendered template
return render('/menuindex.mako')

+ 4
- 2
gnuviechadminweb/lib/base.py Просмотреть файл

@@ -20,8 +20,10 @@ class BaseController(WSGIController):
"""Invoke the Controller"""
conn = meta.engine.connect()
meta.Session.configure(bind=conn)
c.menu = model.Menu.allowed(session['user'] if 'user' in session \
else None)
c.menu = model.Menu.allowed(environ['authkit.users'].user_roles(
environ['REMOTE_USER']) if 'REMOTE_USER' in environ else [])
c.messages = {'errors' : [],
'messages' : []}
try:
# WSGIController.__call__ dispatches to the Controller method
# the request is routed to. This routing information is


+ 200
- 0
gnuviechadminweb/model/GVAUsers.py Просмотреть файл

@@ -0,0 +1,200 @@
# -*- coding: utf-8 -*-
from authkit.users import Users
from gnuviechadminweb.model.user import Group, Role, User
from paste.util.import_string import eval_import
import logging

log = logging.getLogger(__name__)

def needsConnection(func):
def wrapper(*__args, **__kw):
from sqlalchemy.orm import create_session
engine = __args[0].meta.engine
conn = engine.contextual_connect()
if conn.closed:
conn = engine.connect()
__args[0].session = create_session(bind=conn)
try:
return func(*__args, **__kw)
finally:
conn.close()
else:
__args[0].session = create_session(bind=conn)
return func(*__args, **__kw)
return wrapper

class GVAUsers(Users):
def __init__(self, data, encrypt = None):
Users.__init__(self, data, encrypt)
log.debug("in __init__")
self.meta = eval_import(self.data)
self.session = self.meta.Session

@needsConnection
def _getSession(self):
return self.session

def _get_group(self, groupname):
return self._getSession().query(Group).filter_by(name=groupname).one()

def _get_user(self, username):
return self._getSession().query(User).filter_by(name=username).one()

def _get_role(self, rolename):
return self._getSession().query(Role).filter_by(name=rolename).one()

# Create Methods
def user_create(self, username, password, group=None):
n_user = User()
n_user.name = username
n_user.password = self.encrypt(password)
if group:
n_user.group = self._get_group(group)
self._getSession().save(n_user)
self._getSession().commit()

def role_create(self, role):
n_role = Role()
n_role.name = role
self._getSession().save(n_role)
self._getSession().commit()
def group_create(self, group):
n_group = Group()
n_group.name = group
self._getSession().save(n_group)
self._getSession().commit()

# Delete Methods
def user_delete(self, username):
self._getSession().delete(self._get_user(username))
self._getSession().commit()
def role_delete(self, role):
self._getSession().delete(self._get_role(role))
self._getSession().commit()
def group_delete(self, group):
self._getSession().delete(self._get_group())
self._getSession().commit()
# Delete Cascade Methods
def role_delete_cascade(self, role):
n_role = self._get_role(role)
for user in self._getSession().query(User).roles.any(name=role).all():
del user.roles[n_role.id]
self._getSession().delete(n_role)
self._getSession().commit()
def group_delete_cascade(self, group):
n_group = self._get_group(group)
self._getSession().delete(self._getSession().query(User).filter_by(
User.group==n_group))
self._getSession().delete(n_group)
self._getSession().commit()

# Existence Methods
def user_exists(self, username):
return self._getSession().query(User).filter_by(
name=username).count() == 1

def role_exists(self, role):
return self._getSession().query(Role).filter_by(
name=role).count() == 1

def group_exists(self, group):
return self._getSession().query(Group).filter_by(
name=group).count() == 1

# List Methods
def list_roles(self):
return [role.name.lower() for role in self._getSession().query(
Role).all()]
def list_users(self):
return [user.name.lower() for user in self._getSession().query(
User).all()]

def list_groups(self):
return [group.name.lower() for group in self._getSession().query(
Group).all()]

# User Methods
def user(self, username):
user = self._get_user(username)
roles = [role.name.lower() for role in user.roles]
roles.sort()
return {
'username' : user.name,
'group' : None if user.group is None else user.group.name,
'password' : user.password,
'roles' : roles
}

def user_roles(self, username):
user = self._get_user(username)
roles = [role.name.lower() for role in user.roles]
roles.sort()
return roles

def user_group(self, username):
user = self._get_user(username)
return None if user.group is None else user.group.name

def user_password(self, username):
user = self._get_user(username)
return user.password

def user_has_role(self, username, role):
user = self._get_user(username)
return role in [role.name for role in user.roles]

def user_has_group(self, username, group):
user = self._get_user(username)
return (group is None and user.group is None) or \
(group is not None and user.group is not None and \
group == user.group.name)

def user_has_password(self, username, password):
user = self._get_user(username)
return user.password == self.encrypt(password)

def user_set_username(self, username, new_username):
user = self._get_user(username)
user.name = new_username
self._getSession().update(user)
self._getSession().commit()

def user_set_group(self, username, group, add_if_necessary=False):
if add_if_necessary and self._getSession().query(Group).filter_by(
name=group).count() == 0:
self.group_create(group)
groupobj = self._get_group(group)
user = self._get_user(user)
user.group = groupobj
self._getSession().update(user)
self._getSession().commit()
def user_add_role(self, username, role, add_if_necessary=False):
if add_if_necessary and self._getSession().query(Role).filter_by(
name=role).count() == 0:
self.role_create(role)
roleobj = self._get_role(role)
user = self._get_user(username)
if not roleobj in user.roles:
user.roles.append(roleobj)
self._getSession().update(user)
self._getSession().commit()

def user_remove_role(self, username, role):
roleobj = self._get_role(role)
user = self._get_user(username)
if roleobj in user.roles:
del user.roles[roleobj.id]
self._getSession().commit()

def user_remove_group(self, username):
user = self._get_user(username)
user.group = None
self._getSession().update(user)
self._getSession().commit()

+ 3
- 2
gnuviechadminweb/model/__init__.py Просмотреть файл

@@ -11,5 +11,6 @@ def init_model(engine):
meta.engine = engine
meta.Session = orm.scoped_session(sm)

from gnuviechadminweb.model import menu
from gnuviechadminweb.model.menu import Menu, User, Role
from gnuviechadminweb.model.user import Group, Role, User
from gnuviechadminweb.model.GVAUsers import GVAUsers
from gnuviechadminweb.model.menu import Menu

+ 33
- 61
gnuviechadminweb/model/menu.py Просмотреть файл

@@ -1,64 +1,36 @@
# -*- python -*-
# -*- coding: utf-8 -*-
import sqlalchemy as sa
from sqlalchemy import orm

from gnuviechadminweb.model import meta

t_menu = \
sa.Table("menu", meta.metadata,
sa.Column("id", sa.types.Integer, primary_key=True),
sa.Column("title", sa.types.String(40), nullable=False),
sa.Column("controller", sa.types.String(40), nullable=False),
sa.Column("action", sa.types.String(40), nullable=False)
)

t_user = \
sa.Table("user", meta.metadata,
sa.Column("id", sa.types.Integer, primary_key=True),
sa.Column("name", sa.types.String(40), nullable=False),
sa.Column("password", sa.types.String(128), nullable=False)
)

t_role = \
sa.Table("role", meta.metadata,
sa.Column("id", sa.types.Integer, primary_key=True),
sa.Column("name", sa.types.String(40), nullable=False)
)

t_menu_role = \
sa.Table("menu_role", meta.metadata,
sa.Column("id", sa.types.Integer, primary_key=True),
sa.Column("menu_id", sa.types.Integer, sa.ForeignKey(t_menu.c.id)),
sa.Column("role_id", sa.types.Integer, sa.ForeignKey(t_role.c.id))
)

t_user_role = \
sa.Table("user_role", meta.metadata,
sa.Column("id", sa.types.Integer, primary_key=True),
sa.Column("user_id", sa.types.Integer, sa.ForeignKey(t_user.c.id)),
sa.Column("role_id", sa.types.Integer, sa.ForeignKey(t_role.c.id))
)

class Menu(object):
@classmethod
def allowed(cls, user=None):
menu_q = meta.Session.query(cls)
return menu_q.all()

class User(object):
pass

class Role(object):
pass

orm.mapper(Menu, t_menu, {
'roles' : orm.relation(Role, secondary = t_menu_role),
})
orm.mapper(Role, t_role, properties = {
'users' : orm.relation(User, secondary = t_user_role),
'menus' : orm.relation(Menu, secondary = t_menu_role),
})
orm.mapper(User, t_role, properties = {
'roles' : orm.relation(User, secondary = t_user_role)
})
menuitems = [
('Main page', 'gva', 'index', None),
('Login', 'login', 'login', []),
('Logout', 'login', 'logout', ['*']),
('Admin', 'gva', 'index', ['admin'])
]

@classmethod
def allowed(cls, roles=[]):
items = []
for item in cls.menuitems:
additem = False
if item[3] is None:
additem = True
elif len(item[3]) == 0 and len(roles) == 0:
additem = True
elif len(roles) > 0:
for role in item[3]:
if role in roles or role == '*':
additem = True
break
if additem:
items.append(Menu(item[0], item[1], item[2]))
return items

def __init__(self, title, controller, action):
self.title = title
self.controller = controller
self.action = action


+ 34
- 0
gnuviechadminweb/model/tables.py Просмотреть файл

@@ -0,0 +1,34 @@
# -*- python -*-
# -*- coding: utf-8 -*-
import sqlalchemy as sa

from gnuviechadminweb.model import meta

t_group = \
sa.Table("group", meta.metadata,
sa.Column("id", sa.types.Integer, primary_key=True),
sa.Column("name", sa.types.String(40), nullable=False,
unique=True),
)

t_role = \
sa.Table("role", meta.metadata,
sa.Column("id", sa.types.Integer, primary_key=True),
sa.Column("name", sa.types.String(40), nullable=False)
)

t_user = \
sa.Table("user", meta.metadata,
sa.Column("id", sa.types.Integer, primary_key=True),
sa.Column("name", sa.types.String(40), nullable=False),
sa.Column("password", sa.types.String(128), nullable=False),
sa.Column("group_id", sa.types.Integer,
sa.ForeignKey(t_group.c.id))
)

t_user_role = \
sa.Table("user_role", meta.metadata,
sa.Column("id", sa.types.Integer, primary_key=True),
sa.Column("user_id", sa.types.Integer, sa.ForeignKey(t_user.c.id)),
sa.Column("role_id", sa.types.Integer, sa.ForeignKey(t_role.c.id))
)

+ 30
- 0
gnuviechadminweb/model/user.py Просмотреть файл

@@ -0,0 +1,30 @@
# -*- python -*-
# -*- coding: utf-8 -*-
from sqlalchemy import orm

from gnuviechadminweb.model.tables import *
import logging

log = logging.getLogger(__name__)

class Group(object):
pass

class Role(object):
pass

class User(object):
pass

orm.mapper(Group, t_group, {
'users' : orm.relation(User),
})

orm.mapper(Role, t_role, properties = {
'users' : orm.relation(User, secondary = t_user_role),
})

orm.mapper(User, t_user, properties = {
'roles' : orm.relation(Role, secondary = t_user_role),
'group' : orm.relation(Group)
})

+ 10
- 0
gnuviechadminweb/public/stylesheets/gva.css Просмотреть файл

@@ -0,0 +1,10 @@
html {
font-family:sans;
}

#menu {
float:left;
padding-right:10px;
border-right:1px solid black;
margin-right:10px;
}

+ 11
- 0
gnuviechadminweb/templates/loginform.mako Просмотреть файл

@@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
<%inherit file="site.mako" />
<h1>Login</h1>
${h.form(h.url(action='login'), method='post')}
User name: ${h.text_field('username')}
Password: ${h.password_field('password')}
${h.submit('Login')}
${h.end_form()}
<%def name="headlines()">
<title>Login</title>
</%def>

+ 3
- 1
gnuviechadminweb/templates/main.mako Просмотреть файл

@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
<%inherit file="site.mako" />
<h1>Main page</h1>
<%def name="title()">Main page</%def>
<%def name="headlines()">
<title>Main page</title>
</%def>

+ 6
- 0
gnuviechadminweb/templates/menuindex.mako Просмотреть файл

@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
<%inherit file="site.mako" />
<h1>Menu editor</h1>
<%def name="headlines()">
<title>Menu editor</title>
</%def>

+ 19
- 2
gnuviechadminweb/templates/site.mako Просмотреть файл

@@ -1,8 +1,9 @@
# -*- coding: utf-8 -*-
<html>
<head>
<title>${self.title()}</title>
${self.headlines()}
${h.javascript_include_tag('jquery.js')}
${h.stylesheet_link_tag('gva.css')}
</head>
<body>
<ul id="menu">
@@ -10,9 +11,25 @@ ${h.javascript_include_tag('jquery.js')}
<li class="${h.cssclasses(item)}">${h.menulink(item)}</li>
% endfor
</ul>
% if c.messages['errors']:
<ul id="errors">
% for msg in c.messages['errors']:
<li>${msg}</li>
% endfor
</ul>
% endif
% if c.messages['messages']:
<ul id="messages">
% for msg in c.messages['messages']:
<li>${msg}</li>
% endfor
</ul>
% endif
<div id="content">
${next.body()}
</div>
</body>
</html>
<%def name="title()">Site</%def>
<%def name="headlines()">
<title>Site</title>
</%def>

+ 7
- 0
gnuviechadminweb/tests/functional/test_auth.py Просмотреть файл

@@ -0,0 +1,7 @@
from gnuviechadminweb.tests import *

class TestAuthController(TestController):

def test_index(self):
response = self.app.get(url_for(controller='auth'))
# Test response...

+ 7
- 0
gnuviechadminweb/tests/functional/test_login.py Просмотреть файл

@@ -0,0 +1,7 @@
from gnuviechadminweb.tests import *

class TestLoginController(TestController):

def test_index(self):
response = self.app.get(url_for(controller='login'))
# Test response...

+ 7
- 0
gnuviechadminweb/tests/functional/test_menu.py Просмотреть файл

@@ -0,0 +1,7 @@
from gnuviechadminweb.tests import *

class TestMenuController(TestController):

def test_index(self):
response = self.app.get(url_for(controller='menu'))
# Test response...

+ 13
- 1
gnuviechadminweb/websetup.py Просмотреть файл

@@ -5,7 +5,9 @@ from paste.deploy import appconfig
from pylons import config

from gnuviechadminweb.config.environment import load_environment
from gnuviechadminweb.model import meta
from gnuviechadminweb.model import meta, User, Group, Role
from gnuviechadminweb.model.GVAUsers import GVAUsers
from authkit.users import md5

log = logging.getLogger(__name__)

@@ -13,6 +15,16 @@ def setup_config(command, filename, section, vars):
"""Place any commands to setup gnuviechadminweb here"""
conf = appconfig('config:' + filename)
load_environment(conf.global_conf, conf.local_conf)

log.info("Creating tables")
meta.metadata.create_all(bind=meta.engine)

log.info("Creating default admin user, role and group")
users = GVAUsers('gnuviechadminweb.model:meta', md5)
users.role_create("admin")
users.group_create("administrators")

users.user_create("admin", "admin", "administrators")
users.user_add_role("admin", "admin")

log.info("Successfully setup")

+ 1
- 1
setup.py Просмотреть файл

@@ -12,7 +12,7 @@ setup(
author='Jan Dittberner',
author_email='jan@dittberner.info',
url='http://www.gnuviech-server.de/projects/gnuviechadminweb',
install_requires=["Pylons>=0.9.6.2", "Elixir>=0.5.2"],
install_requires=["Pylons>=0.9.6.2", "SQLAlchemy>=0.4", "AuthKit>=0.4"],
packages=find_packages(exclude=['ez_setup']),
include_package_data=True,
test_suite='nose.collector',


Загрузка…
Отмена
Сохранить