First commit
This commit is contained in:
commit
6dc68ff8ac
26
adduser.py
Executable file
26
adduser.py
Executable file
@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*-coding:utf-8 -*
|
||||
|
||||
import sauth
|
||||
import getpass
|
||||
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(description="""
|
||||
Add a user with its password
|
||||
""", formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
parser.add_argument('username', help="Username to add")
|
||||
parser.add_argument('--password', '-p', help="Password. If not provided will be asked", type=str, dest='password')
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.password is None :
|
||||
password = getpass.getpass('Password : ')
|
||||
else:
|
||||
password = args.password
|
||||
|
||||
sa = sauth.SAuth()
|
||||
if sa.add_user(args.username, password):
|
||||
print("User added !")
|
||||
else:
|
||||
print("An error occured (user already present for instance)")
|
||||
|
||||
|
165
example.py
Executable file
165
example.py
Executable file
@ -0,0 +1,165 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*-coding:utf-8 -*
|
||||
|
||||
import bottle
|
||||
import beaker.middleware
|
||||
import sauth
|
||||
|
||||
app = bottle.app()
|
||||
|
||||
session_opts = {
|
||||
'session.type': 'file',
|
||||
'session.data_dir': './sessions/',
|
||||
'session.auto': True,
|
||||
}
|
||||
|
||||
app = beaker.middleware.SessionMiddleware(app, session_opts)
|
||||
sa = sauth.SAuth()
|
||||
auth = sa.make_auth_decorator(fail_redirect="/login")
|
||||
|
||||
|
||||
def post_get(name, default=''):
|
||||
return bottle.request.POST.get(name, default).strip()
|
||||
|
||||
def get_get(name, default=''):
|
||||
return bottle.request.GET.get(name, default).strip()
|
||||
|
||||
|
||||
@bottle.route('/')
|
||||
@bottle.view('home')
|
||||
def hello():
|
||||
usn = sauth.get_username()
|
||||
usr = sauth.get_roles()
|
||||
if usn=='':
|
||||
usn=None
|
||||
return {'username':usn, 'roles':usr}
|
||||
|
||||
@bottle.route('/login')
|
||||
@bottle.view('login')
|
||||
def login_form():
|
||||
"""Serve login form"""
|
||||
if get_get('error')=='':
|
||||
return {'error':False}
|
||||
else:
|
||||
return {'error':True}
|
||||
|
||||
@bottle.post('/login')
|
||||
def login():
|
||||
"""Authenticate users"""
|
||||
username = post_get('username')
|
||||
password = post_get('password')
|
||||
sa.login(username, password, success_redirect='/private', fail_redirect='/login?error=1')
|
||||
|
||||
@bottle.route('/logout')
|
||||
def logout():
|
||||
sa.logout()
|
||||
return "LOGOUT DONE <a href=\"/\">Back to home</a>"
|
||||
|
||||
@bottle.route('/password')
|
||||
@bottle.view('password')
|
||||
@auth()
|
||||
def password_form():
|
||||
return {'username':sauth.get_username()}
|
||||
|
||||
@bottle.post('/password')
|
||||
@auth()
|
||||
def password():
|
||||
username = sauth.get_username()
|
||||
password = post_get('password')
|
||||
sa.set_password(username, password)
|
||||
bottle.redirect('/')
|
||||
|
||||
@bottle.route('/private')
|
||||
@auth()
|
||||
def private():
|
||||
return "Page for Log-in users only. <a href=\"/\">Back to home</a>"
|
||||
|
||||
|
||||
################################################################
|
||||
# Admin section
|
||||
################################################################
|
||||
|
||||
@bottle.route('/admin')
|
||||
@bottle.view('admin')
|
||||
@auth(role='admin')
|
||||
def admin():
|
||||
sa.refresh()
|
||||
luser = []
|
||||
for u in sa.get_all_usernames():
|
||||
us = sa.get_user(u)
|
||||
luser.append([u, us])
|
||||
return {'user':sauth.get_username(), 'luser':luser, 'lroles':sa.get_all_roles()}
|
||||
|
||||
@bottle.post('/admin/user/add')
|
||||
@auth(role='admin')
|
||||
def admin_user_add():
|
||||
username = post_get('username')
|
||||
password = post_get('password')
|
||||
sa.add_user(username, password)
|
||||
bottle.redirect('/admin')
|
||||
return
|
||||
|
||||
@bottle.route('/admin/user/<user>/del_role/<role>')
|
||||
@auth(role='admin')
|
||||
def admin_user_delrole(user, role):
|
||||
sa.rm_user_role(user, role)
|
||||
bottle.redirect('/admin')
|
||||
return
|
||||
|
||||
@bottle.post('/admin/user/<user>/add_role')
|
||||
@auth(role='admin')
|
||||
def admin_user_addrole(user):
|
||||
role = post_get('role')
|
||||
if not role=="":
|
||||
sa.add_user_role(user, role)
|
||||
bottle.redirect('/admin')
|
||||
return
|
||||
|
||||
@bottle.post('/admin/user/<user>/password')
|
||||
@auth(role='admin')
|
||||
def admin_user_password(user):
|
||||
password = post_get('password')
|
||||
sa.set_password(user, password)
|
||||
bottle.redirect('/admin')
|
||||
return
|
||||
|
||||
@bottle.route('/admin/user/<user>/rm')
|
||||
@auth(role='admin')
|
||||
def admin_user_rm(user):
|
||||
sa.rm_user(user)
|
||||
bottle.redirect('/admin')
|
||||
return
|
||||
|
||||
@bottle.post('/admin/role/add')
|
||||
@auth(role='admin')
|
||||
def admin_role_add():
|
||||
role = post_get('role')
|
||||
sa.add_role(role)
|
||||
bottle.redirect('/admin')
|
||||
return
|
||||
|
||||
@bottle.route('/admin/role/<role>/del_sub/<subrole>')
|
||||
@auth(role='admin')
|
||||
def admin_role_delsub(role, subrole):
|
||||
sa.rm_role(role, subrole=subrole)
|
||||
bottle.redirect('/admin')
|
||||
return
|
||||
|
||||
@bottle.post('/admin/role/<role>/add_sub/')
|
||||
@auth(role='admin')
|
||||
def admin_role_addsub(role):
|
||||
subrole = post_get('addrole')
|
||||
print("1::subrole::{}".format(subrole))
|
||||
if not subrole=='':
|
||||
sa.add_role(role, subrole=subrole)
|
||||
bottle.redirect('/admin')
|
||||
return
|
||||
|
||||
@bottle.route('/admin/role/<role>/rm/')
|
||||
@auth(role='admin')
|
||||
def admin_role_rm(role):
|
||||
sa.rm_role(role)
|
||||
bottle.redirect('/admin')
|
||||
return
|
||||
|
||||
bottle.run(app, host='localhost', port=8080)
|
260
sauth/__init__.py
Normal file
260
sauth/__init__.py
Normal file
@ -0,0 +1,260 @@
|
||||
# -*-coding:utf-8 -*
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger()
|
||||
|
||||
import bottle
|
||||
import hashlib
|
||||
import os
|
||||
|
||||
|
||||
def get_session():
|
||||
return bottle.request.environ['beaker.session']
|
||||
|
||||
def get_username():
|
||||
session = get_session()
|
||||
if 'username' in session:
|
||||
username = get_session()['username']
|
||||
if username is None or username=='' :
|
||||
return None
|
||||
else:
|
||||
return username
|
||||
return None
|
||||
|
||||
def get_roles():
|
||||
session = get_session()
|
||||
if 'roles' in session:
|
||||
roles = get_session()['roles']
|
||||
if roles is None or roles=='' :
|
||||
return None
|
||||
else:
|
||||
return roles
|
||||
return None
|
||||
|
||||
def _redirect(location):
|
||||
if location is None :
|
||||
bottle.abort(401, "Sorry, access denied.")
|
||||
else:
|
||||
bottle.redirect(location)
|
||||
|
||||
class SAuth(object):
|
||||
"""
|
||||
Simple Auth class with decorators for bottle
|
||||
"""
|
||||
|
||||
def __init__(self, backend=None, backend_infos={}, hashing_algorithm=None):
|
||||
"""Auth/Authorization/Accounting class
|
||||
|
||||
backend: backend to be used (str)
|
||||
backend_infos: arguments passed to backend constructor
|
||||
hashing_algorithm: hashing algorith to use, sha256 by default
|
||||
"""
|
||||
if hashing_algorithm in hashlib.algorithms_available :
|
||||
self.hashing_algorithm = hashing_algorithm
|
||||
elif 'sha256' in hashlib.algorithms_available :
|
||||
self.hashing_algorithm = 'sha256'
|
||||
|
||||
# Setup JsonBackend by default
|
||||
# Currently no other backend available
|
||||
if backend is None or backend == 'json':
|
||||
from sauth import json_backend
|
||||
logger.info("Selected backend : json")
|
||||
self._backend = json_backend.JsonBackend(**backend_infos)
|
||||
else:
|
||||
raise ValueError("Selected backend not found")
|
||||
|
||||
def make_auth_decorator(self, username=None, role=None, fail_redirect='/login'):
|
||||
'''
|
||||
Create a decorator to be used for authentication and authorization
|
||||
|
||||
:param username: A resource can be protected for a specific user
|
||||
:param role: Minimum role level required for authorization
|
||||
:param fail_redirect: The URL to redirect to if a login is required.
|
||||
'''
|
||||
session_manager = self
|
||||
def auth_require(username=username, role=role, fail_redirect=fail_redirect):
|
||||
def decorator(func):
|
||||
import functools
|
||||
|
||||
@functools.wraps(func)
|
||||
def wrapper(*a, **ka):
|
||||
session_manager._require(username=username, role=role, fail_redirect=fail_redirect)
|
||||
return func(*a, **ka)
|
||||
return wrapper
|
||||
return decorator
|
||||
return(auth_require)
|
||||
|
||||
def get_user(self, username=None):
|
||||
"""
|
||||
return a User object for the current user or a specified username
|
||||
return None if user nout logged in or not found
|
||||
"""
|
||||
if username is None :
|
||||
user = get_username()
|
||||
if(user is None):
|
||||
return None
|
||||
else:
|
||||
return self.get_user(username=user)
|
||||
else:
|
||||
return self._backend.get_user(username)
|
||||
|
||||
def get_user_roles(self, username=None):
|
||||
"""
|
||||
return a list of the roles of user (main roles and herited)
|
||||
return None if not found
|
||||
"""
|
||||
if username is None :
|
||||
user = get_username()
|
||||
if(user is None):
|
||||
return None
|
||||
else:
|
||||
return self.get_user_roles(user)
|
||||
else:
|
||||
return self._backend.get_user_roles(username)
|
||||
|
||||
def _require(self, username=None, role=None, fail_redirect=None):
|
||||
"""
|
||||
Teste authentification for decorator
|
||||
"""
|
||||
# Authentication
|
||||
user = get_username()
|
||||
|
||||
if user is None:
|
||||
_redirect(fail_redirect)
|
||||
|
||||
if(username is not None):
|
||||
if not (user==username):
|
||||
_redirect(fail_redirect)
|
||||
|
||||
if(role is not None):
|
||||
roles = get_roles()
|
||||
if not (role in roles):
|
||||
_redirect(fail_redirect)
|
||||
|
||||
return # success
|
||||
|
||||
|
||||
def login(self, username, password, success_redirect=None, fail_redirect=None):
|
||||
"""
|
||||
Login function
|
||||
username : str
|
||||
password : str
|
||||
success_redirect and fail_redirect : locations on which to redirect if
|
||||
login is successful or not
|
||||
"""
|
||||
user = self.get_user(username)
|
||||
ip = bottle.request.environ.get('REMOTE_ADDR')
|
||||
if(user is not None):
|
||||
pw=user.password
|
||||
if pw is None or not len(pw.split('/'))==3 :
|
||||
logger.warn("Login FAIL user {} from {}".format(user, ip))
|
||||
_redirect(fail_redirect)
|
||||
return False
|
||||
pw=pw.split('/')
|
||||
hashing_algorithm = pw[0]
|
||||
salt = bytes.fromhex(pw[1])
|
||||
dk = hashlib.pbkdf2_hmac(hashing_algorithm, password.encode('utf-8'), salt, 100000)
|
||||
pwtest = dk.hex()
|
||||
if(pw[2]==pwtest):
|
||||
get_session()['username']=username
|
||||
get_session()['roles']=self.get_user_roles(username)
|
||||
logger.info("Login OK user {} from {}".format(user, ip))
|
||||
if(success_redirect is not None):
|
||||
_redirect(success_redirect)
|
||||
return True
|
||||
logger.warn("Login FAIL user {} from {}".format(user, ip))
|
||||
_redirect(fail_redirect)
|
||||
return False
|
||||
|
||||
def logout(self, redirect=None):
|
||||
"""
|
||||
Logout function. Destroys session.
|
||||
"""
|
||||
usn = get_username()
|
||||
ip = bottle.request.environ.get('REMOTE_ADDR')
|
||||
session = get_session()
|
||||
session['username']=None
|
||||
session.delete()
|
||||
logger.info("Logout user {} from {}".format(usn, ip))
|
||||
if(redirect is not None):
|
||||
_redirect(redirect)
|
||||
return
|
||||
|
||||
def add_user(self, username, password):
|
||||
"""
|
||||
Add a new user
|
||||
username: str (return False if alsready exists)
|
||||
password: str
|
||||
"""
|
||||
if self.get_user(username) is not None:
|
||||
return False
|
||||
logger.info("Adduser {}".format(username))
|
||||
self._backend.add_user(username)
|
||||
self.set_password(username, password)
|
||||
return True
|
||||
|
||||
def add_role(self, role, subrole=None):
|
||||
"""
|
||||
Add a role or a subrole
|
||||
(role given by another)
|
||||
"""
|
||||
self._backend.add_role(role, subrole=subrole)
|
||||
return
|
||||
|
||||
def add_user_role(self, username, role):
|
||||
"""
|
||||
Add a role to user
|
||||
"""
|
||||
return self._backend.add_user_role(username, role)
|
||||
|
||||
def rm_user(self, username):
|
||||
"""
|
||||
Delete a user by name
|
||||
"""
|
||||
return self._backend.rm_user(username)
|
||||
|
||||
def rm_role(self, role, subrole=None):
|
||||
"""
|
||||
Delete a role.
|
||||
If subrole is not None, delete the subrole of role
|
||||
"""
|
||||
return self._backend.rm_role(role, subrole=subrole)
|
||||
|
||||
def rm_user_role(self, username, role):
|
||||
"""
|
||||
Remove a role to user
|
||||
"""
|
||||
return self._backend.rm_user_role(username, role)
|
||||
|
||||
def update_user(self, username=None, **kwargs):
|
||||
if username is None :
|
||||
username = get_username()
|
||||
self._backend.update_user(username, **kwargs)
|
||||
return
|
||||
|
||||
def set_password(self, username, password):
|
||||
"""
|
||||
Set a new password to user
|
||||
username: username of the user (str)
|
||||
password: new password (str)
|
||||
"""
|
||||
logger.info("Change password {}".format(username))
|
||||
salt = os.urandom(16)
|
||||
dk = hashlib.pbkdf2_hmac(self.hashing_algorithm, password.encode('utf-8'), salt, 100000)
|
||||
ssalt = salt.hex()
|
||||
spassword = dk.hex()
|
||||
s = self.hashing_algorithm + "/" + ssalt + "/" + spassword
|
||||
return self._backend.update_user(username, password=s)
|
||||
|
||||
def get_all_usernames(self):
|
||||
return self._backend.get_users()
|
||||
|
||||
def get_all_roles(self):
|
||||
return self._backend.get_roles()
|
||||
|
||||
def refresh(self):
|
||||
if 'refresh' in dir(self._backend):
|
||||
self._backend.refresh()
|
||||
return
|
||||
return
|
||||
|
133
sauth/json_backend.py
Normal file
133
sauth/json_backend.py
Normal file
@ -0,0 +1,133 @@
|
||||
# -*-coding:utf-8 -*
|
||||
|
||||
import json
|
||||
import collections
|
||||
from sauth import user
|
||||
|
||||
class JsonBackend:
|
||||
def __init__(self, directory='users', users_fname='users.json', roles_fname='roles.json'):
|
||||
"""
|
||||
directory: directory in which configuration files are (str)
|
||||
users_fname: filename for users (str)
|
||||
roles_fname: filename for roles (str)
|
||||
"""
|
||||
if not (directory[-1]=='/'):
|
||||
directory = directory + "/"
|
||||
self.users_f = directory + users_fname
|
||||
self.roles_f = directory + roles_fname
|
||||
self.refresh()
|
||||
|
||||
def get_user(self, username):
|
||||
if username in self._json_users :
|
||||
data = self._json_users[username]
|
||||
return user.User(username, **data)
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_user_roles(self, username):
|
||||
if username in self._json_users :
|
||||
data = self._json_users[username]
|
||||
us = user.User(username, **data)
|
||||
for r in us.roles:
|
||||
if(r in self._json_roles):
|
||||
for sr in self._json_roles[r]:
|
||||
if not sr in us.roles:
|
||||
us.roles.append(sr)
|
||||
return us.roles
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_roles(self):
|
||||
return self._json_roles
|
||||
|
||||
def get_users(self):
|
||||
return list(self._json_users.keys())
|
||||
|
||||
def add_role(self, role, subrole=None):
|
||||
print("Role :{} subrole:{}".format(role, subrole))
|
||||
if role not in self._json_roles:
|
||||
print("Role added")
|
||||
self._json_roles[role]=[]
|
||||
if(subrole is not None):
|
||||
print("Subrole added")
|
||||
self._json_roles[role].append(subrole)
|
||||
self._update_json_roles()
|
||||
return
|
||||
|
||||
def add_user_role(self, username, role):
|
||||
if 'roles' in self._json_users[username]:
|
||||
if not role in self._json_users[username]['roles']:
|
||||
self._json_users[username]['roles'].append(role)
|
||||
self._update_json()
|
||||
return True
|
||||
else:
|
||||
self._json_users[username]['roles'] = [role]
|
||||
self._update_json()
|
||||
return True
|
||||
return False
|
||||
|
||||
def rm_user(self, username):
|
||||
if username in self._json_users :
|
||||
del self._json_users[username]
|
||||
self._update_json_roles()
|
||||
return True
|
||||
return False
|
||||
|
||||
def rm_role(self, role, subrole=None):
|
||||
if role in self._json_roles:
|
||||
if subrole is None:
|
||||
del self._json_roles[role]
|
||||
self._update_json_roles()
|
||||
return True
|
||||
else:
|
||||
for i in range(len(self._json_roles[role])):
|
||||
if self._json_roles[role][i]==subrole :
|
||||
del self._json_roles[role][i]
|
||||
self._update_json_roles()
|
||||
return True
|
||||
break
|
||||
else:
|
||||
return False
|
||||
|
||||
def rm_user_role(self, username, role):
|
||||
if 'roles' in self._json_users[username] and role in self._json_users[username]['roles']:
|
||||
for i in range(len(self._json_users[username]['roles'])):
|
||||
if self._json_users[username]['roles'][i]==role:
|
||||
del self._json_users[username]['roles'][i]
|
||||
self._update_json()
|
||||
return True
|
||||
break
|
||||
return False
|
||||
|
||||
def add_user(self, username, **kwargs):
|
||||
if (username in self._json_users):
|
||||
return False
|
||||
self._json_users[username] = {}
|
||||
self._update_json()
|
||||
return True
|
||||
|
||||
def update_user(self, username, **kwargs):
|
||||
if(username in self._json_users):
|
||||
for e in kwargs:
|
||||
self._json_users[username][e] = kwargs[e]
|
||||
self._update_json()
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def _update_json(self):
|
||||
with open(self.users_f, 'w') as f:
|
||||
json.dump(self._json_users, f, indent=2, ensure_ascii=False)
|
||||
return
|
||||
|
||||
def _update_json_roles(self):
|
||||
with open(self.roles_f, 'w') as f:
|
||||
json.dump(self._json_roles, f, ensure_ascii=False)
|
||||
return
|
||||
|
||||
def refresh(self):
|
||||
with open(self.users_f, 'r') as f:
|
||||
self._json_users = json.load(f, object_pairs_hook=collections.OrderedDict)
|
||||
with open(self.roles_f, 'r') as f:
|
||||
self._json_roles = json.load(f, object_pairs_hook=collections.OrderedDict)
|
||||
|
19
sauth/user.py
Normal file
19
sauth/user.py
Normal file
@ -0,0 +1,19 @@
|
||||
# -*-coding:utf-8 -*
|
||||
|
||||
class User:
|
||||
def __init__(self, username, password=None, roles=[], **kw):
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.roles = roles
|
||||
self.info = {}
|
||||
for e in kw:
|
||||
self.info[e] = kw[e]
|
||||
|
||||
def __str__(self):
|
||||
s = "User {}".format(self.username)
|
||||
for e in self.info:
|
||||
s+= " {} : {}".format(e, self.info[e])
|
||||
return s
|
||||
|
||||
def __repr__(self):
|
||||
return self.__str__()
|
1
users/roles.json
Normal file
1
users/roles.json
Normal file
@ -0,0 +1 @@
|
||||
{"admin": ["user"], "user": []}
|
15
users/users.json
Normal file
15
users/users.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"admin": {
|
||||
"password": "sha256/34467647c75727d973bcef4ca739388d/9c6ee4cfe5091789035f162cf479fe2b853b679c381ebfc5b4174959b1a280e3",
|
||||
"roles": [
|
||||
"admin",
|
||||
"user"
|
||||
]
|
||||
},
|
||||
"user": {
|
||||
"password": "sha256/13332f080564518570773fdd24b7602d/9b88dba299cc37fe8ab171066c791e4343a0e71853dfbcbcc76e4df43d57f923",
|
||||
"roles": [
|
||||
"user"
|
||||
]
|
||||
}
|
||||
}
|
160
views/admin.tpl
Normal file
160
views/admin.tpl
Normal file
@ -0,0 +1,160 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="content-type">
|
||||
<div id='main'>
|
||||
<h2>Administration page</h2>
|
||||
<p>Welcome {{user}}</p>
|
||||
<div id="users">
|
||||
<h4> Users </h4>
|
||||
<p>Create new user:</p>
|
||||
<form action="/admin/user/add" method="post">
|
||||
<p><label>Username</label> <input type="text" name="username" /></p>
|
||||
<p><label>Password</label> <input type="password" name="password" /></p>
|
||||
<button type="submit" style="margin-left: 13em;"> OK </button>
|
||||
</form>
|
||||
%for u in luser:
|
||||
<fieldset>
|
||||
<legend><b>{{u[0]}}</b></legend>
|
||||
<b>Roles : </b>
|
||||
%if u[1].roles:
|
||||
%for r in u[1].roles:
|
||||
{{r}} (<a href="/admin/user/{{u[0]}}/del_role/{{r}}">X</a>),
|
||||
%end
|
||||
%end
|
||||
<br/>
|
||||
<a href="javascript:showhide('user_{{u[0]}}')">Details +/-</a>
|
||||
<div id="user_{{u[0]}}" style="display:none;">
|
||||
<!-- Add Role -->
|
||||
<p>
|
||||
<form action="/admin/user/{{u[0]}}/add_role" method="post">
|
||||
<b>Add role : </b>
|
||||
<select name="role">
|
||||
<option></option>
|
||||
%for r in lroles:
|
||||
<option>{{r}}</option>
|
||||
%end
|
||||
</select>
|
||||
<button type="submit">OK</button>
|
||||
</form>
|
||||
</p>
|
||||
|
||||
<!-- Change Password -->
|
||||
<p>
|
||||
<form action="/admin/user/{{u[0]}}/password" method="post">
|
||||
<b>Change password : </b>
|
||||
<input type="password" name="password" />
|
||||
<button type="submit">OK</button>
|
||||
</form>
|
||||
</p>
|
||||
|
||||
<!-- Other data -->
|
||||
%for i in u[1].info :
|
||||
%if i not in ['password', 'roles']:
|
||||
<p>
|
||||
<b>{{i}} :</b> {{u[1].info[i]}}
|
||||
</p>
|
||||
%end
|
||||
%end
|
||||
|
||||
<p><a href="/admin/user/{{u[0]}}/rm">Delete user</a></p>
|
||||
</div>
|
||||
</fieldset>
|
||||
%end
|
||||
<br/>
|
||||
</div>
|
||||
|
||||
<div id='roles'>
|
||||
<h4> Roles </h4>
|
||||
<p>Create new role:</p>
|
||||
<form action="/admin/role/add" method="post">
|
||||
<p><label>Role</label> <input type="text" name="role" /></p>
|
||||
<button type="submit" style="margin-left: 13em;"> OK </button>
|
||||
</form>
|
||||
<br />
|
||||
%for r in lroles:
|
||||
<fieldset>
|
||||
<legend><b>{{r}}</b></legend>
|
||||
%for sr in lroles[r]:
|
||||
{{sr}} (<a href="/admin/role/{{r}}/del_sub/{{sr}}">X</a>),
|
||||
%end
|
||||
<br/>
|
||||
<a href="javascript:showhide('role_{{r}}')">Details +/-</a>
|
||||
<div id="role_{{r}}" style="display:none;">
|
||||
<!-- Add Role -->
|
||||
<p>
|
||||
<form action="/admin/role/{{r}}/add_sub/" method="post">
|
||||
<b>Add provided : </b>
|
||||
<select name="addrole">
|
||||
<option></option>
|
||||
%for ri in lroles:
|
||||
<option>{{ri}}</option>
|
||||
%end
|
||||
</select>
|
||||
<button type="submit">OK</button>
|
||||
</form>
|
||||
</p>
|
||||
|
||||
<p><a href="/admin/role/{{r}}/rm/">Delete role</a></p>
|
||||
</div>
|
||||
</fieldset>
|
||||
%end
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="clear"></div>
|
||||
|
||||
<div id="urls">
|
||||
<a href="/">index</a> <a href="/logout">logout</a>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function showhide (id) {
|
||||
element = document.getElementById(id)
|
||||
if(element.style.display=='block'){
|
||||
element.style.display = 'none';
|
||||
}
|
||||
else{
|
||||
element.style.display = 'block';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
div#roles { width: 45%; float: right}
|
||||
div#users { width: 45%; float: left}
|
||||
div#main {
|
||||
color: #777;
|
||||
margin: auto;
|
||||
margin-left: 5em;
|
||||
font-size: 80%;
|
||||
}
|
||||
input {
|
||||
background: #f8f8f8;
|
||||
border: 1px solid #777;
|
||||
margin: auto;
|
||||
}
|
||||
input:hover { background: #fefefe}
|
||||
label {
|
||||
width: 8em;
|
||||
float: left;
|
||||
text-align: right;
|
||||
margin-right: 0.5em;
|
||||
display: block
|
||||
}
|
||||
div#status {
|
||||
border: 1px solid #999;
|
||||
padding: .5em;
|
||||
margin: 2em;
|
||||
width: 15em;
|
||||
-moz-border-radius: 10px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.clear { clear: both;}
|
||||
div#urls {
|
||||
position:absolute;
|
||||
top:0;
|
||||
right:1em;
|
||||
}
|
||||
</style>
|
||||
|
46
views/home.tpl
Normal file
46
views/home.tpl
Normal file
@ -0,0 +1,46 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="content-type">
|
||||
<style>
|
||||
div {
|
||||
color: #777;
|
||||
margin: auto;
|
||||
width: 20em;
|
||||
text-align: center;
|
||||
}
|
||||
div#hbox {width: 100%;}
|
||||
div#hbox div.box {float: center; width: 33%;}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="hbox">
|
||||
<div class="box">
|
||||
<h2>Simple Auth for Bottle</h2>
|
||||
<h3>Demonstration site</h3>
|
||||
%if username is None:
|
||||
<p>You are not loged in</p>
|
||||
%else:
|
||||
<p>You are loged as {{username}}</p>
|
||||
<p>You have these roles :
|
||||
%for r in roles:
|
||||
{{r}},
|
||||
%end
|
||||
</p>
|
||||
<p><a href="/logout">Logout</a></p>
|
||||
<p><a href="/password">Change password</a></p>
|
||||
%end
|
||||
<h4>Pages of the test site :</h4>
|
||||
<p>
|
||||
<ul>
|
||||
<li>This home page</a>
|
||||
<li><a href="/login">Login page</a>
|
||||
<li><a href="/private">A restricted access page</a>
|
||||
<li><a href="/admin">The admin page</a>
|
||||
</ul>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
44
views/login.tpl
Normal file
44
views/login.tpl
Normal file
@ -0,0 +1,44 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="content-type">
|
||||
<style>
|
||||
div {
|
||||
color: #777;
|
||||
margin: auto;
|
||||
width: 20em;
|
||||
text-align: center;
|
||||
}
|
||||
div#hbox {width: 100%;}
|
||||
div#hbox div.box {float: center; width: 33%;}
|
||||
p.error {width:100%; background-color:#ffb3b3;}
|
||||
input {
|
||||
background: #f8f8f8;
|
||||
border: 1px solid #777;
|
||||
margin: auto;
|
||||
}
|
||||
input:hover { background: #fefefe}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="hbox">
|
||||
<div class="box">
|
||||
<h2>Login</h2>
|
||||
<p>Please insert your credentials:</p>
|
||||
%if error:
|
||||
<p class="error">Incorrect username/password.</p>
|
||||
%end
|
||||
<form action="login" method="post" name="login">
|
||||
<p>Username : <input type="text" name="username" /></p>
|
||||
<p>Password : <input type="password" name="password" /></p>
|
||||
|
||||
<br/><br/>
|
||||
<button type="submit"> OK </button>
|
||||
</form>
|
||||
<br />
|
||||
</div>
|
||||
<br style="clear: left;" />
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
27
views/password.tpl
Normal file
27
views/password.tpl
Normal file
@ -0,0 +1,27 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="content-type">
|
||||
<div class="box">
|
||||
<h2>Password change</h2>
|
||||
<p>Please insert your new password:</p>
|
||||
<form action="/password" method="post">
|
||||
<p><input type="password" name="password" /></p>
|
||||
<p><button type="submit" > OK </button></p>
|
||||
</form>
|
||||
<br />
|
||||
</div>
|
||||
<style>
|
||||
div {
|
||||
color: #777;
|
||||
margin: auto;
|
||||
width: 20em;
|
||||
text-align: center;
|
||||
}
|
||||
input {
|
||||
background: #f8f8f8;
|
||||
border: 1px solid #777;
|
||||
margin: auto;
|
||||
}
|
||||
input:hover { background: #fefefe}
|
||||
</style>
|
Loading…
Reference in New Issue
Block a user