basic ldap support, needs SSL verify

master
Tomek Dubrownik 2012-09-15 01:38:30 +02:00
parent 3cb096cc76
commit c0e8cc2ccf
5 changed files with 29 additions and 95 deletions

84
at.py
View File

@ -4,6 +4,9 @@ import sqlite3
import threading import threading
import traceback import traceback
import json import json
import requests
import os
import logging
from flask import Flask, render_template, abort, g, \ from flask import Flask, render_template, abort, g, \
redirect, session, request, flash, url_for redirect, session, request, flash, url_for
from werkzeug.contrib.fixers import ProxyFix from werkzeug.contrib.fixers import ProxyFix
@ -44,6 +47,10 @@ def req_to_ctx():
def strfts(ts, format='%d/%m/%Y %H:%M'): def strfts(ts, format='%d/%m/%Y %H:%M'):
return datetime.fromtimestamp(ts).strftime(format) return datetime.fromtimestamp(ts).strftime(format)
@app.template_filter('wikiurl')
def wikiurl(user):
return config.wiki_url % { 'login': user }
@app.before_request @app.before_request
def make_connection(): def make_connection():
conn = sqlite3.connect(config.db) conn = sqlite3.connect(config.db)
@ -56,18 +63,15 @@ def close_connection(exception):
g.db.close() g.db.close()
DeviceInfo = namedtuple('DeviceInfo', ['hwaddr', 'name', 'owner', 'ignored']) DeviceInfo = namedtuple('DeviceInfo', ['hwaddr', 'name', 'owner', 'ignored'])
User = namedtuple('User', ['id', 'login', 'passwd', 'url'])
def get_device_info(conn, hwaddr): def get_device_info(conn, hwaddr):
return list(get_device_infos(conn, (hwaddrs,)))[0] return list(get_device_infos(conn, (hwaddrs,)))[0]
def get_device_infos(conn, hwaddrs): def get_device_infos(conn, hwaddrs):
stmt = '''select hwaddr, name, ignored, userid, login, url from stmt = '''select hwaddr, name, ignored, owner from
devices left join users on devices.owner = users.userid devices where devices.hwaddr in (''' + ','.join(['?'] * len(hwaddrs)) + ')'
where devices.hwaddr in (''' + ','.join(['?'] * len(hwaddrs)) + ')'
for row in conn.execute(stmt, hwaddrs): for row in conn.execute(stmt, hwaddrs):
owner = User(row['userid'], row['login'], None, row['url']) \ owner = row['owner'] or ''
if row['login'] else None
ignored = row['ignored'] ignored = row['ignored']
yield DeviceInfo(row['hwaddr'], row['name'], owner, ignored) yield DeviceInfo(row['hwaddr'], row['name'], owner, ignored)
@ -78,6 +82,7 @@ class Updater(threading.Thread):
self.lease_offset = lease_offset self.lease_offset = lease_offset
self.active = {} self.active = {}
threading.Thread.__init__(self, *a, **kw) threading.Thread.__init__(self, *a, **kw)
self.daemon = True
def purge_stale(self): def purge_stale(self):
now = time() now = time()
for addr, (atime, ip, name) in self.active.items(): for addr, (atime, ip, name) in self.active.items():
@ -134,7 +139,6 @@ class MtimeUpdater(Updater):
def file_changed(self, f): def file_changed(self, f):
pass pass
def run(self): def run(self):
import os
while True: while True:
try: try:
mtime = os.stat(self.lease_file).st_mtime mtime = os.stat(self.lease_file).st_mtime
@ -190,10 +194,9 @@ def list_all():
result = now_at() result = now_at()
def prettify_user((user, atime)): def prettify_user((user, atime)):
return { return {
'login': user.login, 'login': user,
'timestamp': atime, 'timestamp': atime,
'pretty_time': strfts(atime), 'pretty_time': strfts(atime),
'url': user.url,
} }
result['users'] = map(prettify_user, result['users']) result['users'] = map(prettify_user, result['users'])
result['unknown'] = len(result['unknown']) result['unknown'] = len(result['unknown'])
@ -212,45 +215,18 @@ def now_at():
restrict_to_hs = restrict_ip(prefix=config.claimable_prefix, restrict_to_hs = restrict_ip(prefix=config.claimable_prefix,
exclude=config.claimable_exclude) exclude=config.claimable_exclude)
@app.route('/register', methods=['GET'])
@restrict_to_hs
def register_form():
return render_template('register.html', **req_to_ctx())
@app.route('/register', methods=['POST'])
@restrict_to_hs
def register():
login = request.form['login'].lower()
url = request.form['url']
if 'wiki' in request.form:
url = config.wiki_url % { 'login': login }
try:
g.db.execute('insert into users (login, url, pass) values (?, ?, ?)',
[login, url, sha256(request.form['password']).hexdigest()])
return redirect('/')
except sqlite3.Error as e:
flash('Cannot add user - username taken?', category='error')
return register_form()
@app.route('/login', methods=['GET']) @app.route('/login', methods=['GET'])
def login_form(): def login_form():
return render_template('login.html', **req_to_ctx()) return render_template('login.html', **req_to_ctx())
def get_user(conn, login, password):
row = conn.execute('select userid, login, pass, url from users where\
login = ? and pass = ?', [login, sha256(password).hexdigest()]).fetchone()
return row and User(row['userid'], row['login'], None, row['url'])
@app.route('/login', methods=['POST']) @app.route('/login', methods=['POST'])
def login(): def login():
login = request.form.get('login', '').lower() login = request.form.get('login', '').lower()
pwd = request.form.get('password', '') pwd = request.form.get('password', '')
goto = request.values.get('goto') or '/' goto = request.values.get('goto') or '/'
user = get_user(g.db, login, pwd) if requests.post('https://auth.hackerspace.pl', verify=False,
if user: data = { 'login': login, 'password': pwd }).status_code == 200:
session['userid'] = user.id session['login'] = login
session['login'] = user.login
session['user'] = user
return redirect(goto) return redirect(goto)
else: else:
flash('Username or password invalid', category='error') flash('Username or password invalid', category='error')
@ -264,7 +240,7 @@ def logout():
def login_required(f): def login_required(f):
@wraps(f) @wraps(f)
def func(*a, **kw): def func(*a, **kw):
if 'userid' not in session: if 'login' not in session:
flash('You must log in to continue', category='error') flash('You must log in to continue', category='error')
return redirect('/login?' + return redirect('/login?' +
urlencode({'goto': request.path})) urlencode({'goto': request.path}))
@ -287,10 +263,10 @@ def claim():
if not hwaddr: if not hwaddr:
ctx = { 'error': 'Invalid device.' } ctx = { 'error': 'Invalid device.' }
else: else:
userid = session['userid'] login = session['login']
try: try:
g.db.execute('insert into devices (hwaddr, name, owner, ignored)\ g.db.execute('insert into devices (hwaddr, name, owner, ignored)\
values (?, ?, ?, ?)', [hwaddr, request.form['name'], userid, False]) values (?, ?, ?, ?)', [hwaddr, request.form['name'], login, False])
ctx = {} ctx = {}
except sqlite3.Error as e: except sqlite3.Error as e:
ctx = { 'error': 'Could not add device! Perhaps someone claimed it?' } ctx = { 'error': 'Could not add device! Perhaps someone claimed it?' }
@ -298,39 +274,28 @@ def claim():
def get_user_devices(conn, user): def get_user_devices(conn, user):
devs = conn.execute('select hwaddr, name, ignored from devices where\ devs = conn.execute('select hwaddr, name, ignored from devices where\
owner = ?', [user.id]) owner = ?', [user])
return (DeviceInfo(row['hwaddr'], row['name'], user, row['ignored']) for return (DeviceInfo(row['hwaddr'], row['name'], user, row['ignored']) for
row in devs) row in devs)
def set_password(conn, user, password): @app.route('/account', methods=['GET'])
return conn.execute('update users set pass = ? where userid = ?',
[sha256(password).hexdigest(), user.id])
@app.route('/account', methods=['GET','POST'])
@login_required @login_required
def account(): def account():
if request.method == 'POST': devices = get_user_devices(g.db, session['login'])
old = request.form['old']
if get_user(g.db, session['login'], old) and \
set_password(g.db, session['user'], request.form['new']):
flash('Password changed', category='message')
else:
flash('Could not change password!', category='error')
devices = get_user_devices(g.db, session['user'])
return render_template('account.html', devices=devices) return render_template('account.html', devices=devices)
def set_ignored(conn, hwaddr, user, value): def set_ignored(conn, hwaddr, user, value):
return conn.execute('update devices set ignored = ? where hwaddr = ? and owner = ?', return conn.execute('update devices set ignored = ? where hwaddr = ? and owner = ?',
[value, hwaddr, user.id]) [value, hwaddr, user])
def delete_device(conn, hwaddr, user): def delete_device(conn, hwaddr, user):
return conn.execute('delete from devices where hwaddr = ? and owner = ?', return conn.execute('delete from devices where hwaddr = ? and owner = ?',
[hwaddr, user.id]) [hwaddr, user])
@app.route('/devices/<id>/<action>/') @app.route('/devices/<id>/<action>/')
@login_required @login_required
def device(id, action): def device(id, action):
user = session['user'] user = session['login']
if action == 'hide': if action == 'hide':
set_ignored(g.db, id, user, True) set_ignored(g.db, id, user, True)
if action == 'show': if action == 'show':
@ -341,7 +306,6 @@ def device(id, action):
port = 8080 port = 8080
if __name__ == '__main__': if __name__ == '__main__':
import logging
app.logger.setLevel(logging.DEBUG) app.logger.setLevel(logging.DEBUG)
updater = DhcpdUpdater(config.lease_file, config.timeout, config.lease_offset) updater = DhcpdUpdater(config.lease_file, config.timeout, config.lease_offset)
updater.start() updater.start()

View File

@ -1,13 +1,6 @@
create table users (
userid integer primary key,
login varchar(50) unique not null,
pass character(64),
url varchar(300)
);
create table devices ( create table devices (
hwaddr character(17) primary key, hwaddr character(17) primary key,
name varchar(50), name varchar(50),
owner integer references users(userid), owner varchar(100) not null,
ignored boolean ignored boolean
); );

View File

@ -5,27 +5,6 @@
{% for msg in get_flashed_messages(True) %} {% for msg in get_flashed_messages(True) %}
<p class="{{ msg[0] }}">{{ msg[1] }}</p> <p class="{{ msg[0] }}">{{ msg[1] }}</p>
{% endfor %} {% endfor %}
<h3>Change password</h3>
<form action="" method="POST">
<table>
<label><tr>
<td>Current password:</td>
<td><input type="password" name="old"></td>
</tr></label>
<label><tr>
<td>New password:</td>
<td><input type="password" name="new"></td>
</tr></label>
<label><tr>
<td>Confirm new password:</td>
<td><input type="password" name="new2"></td>
</tr></label>
<label><tr>
<td></td>
<td><input type="submit" value="Save"></td>
</tr></label>
</table>
</form>
<h3>Claimed devices</h3> <h3>Claimed devices</h3>
<table class="devices"> <table class="devices">
<tr> <tr>

View File

@ -14,7 +14,7 @@
<a href="account">account</a> | <a href="account">account</a> |
<a href="logout">log out</a> <a href="logout">log out</a>
{% else %} {% else %}
<a href="login">login</a> | <a href="register">register</a> <a href="login">login</a>
{% endif %} {% endif %}
</div> </div>
{% block content %} {% block content %}

View File

@ -8,11 +8,9 @@ Now at hackerspace
<ul> <ul>
{% for user, timestamp in users %} {% for user, timestamp in users %}
<li> <li>
{% if user.url %} <a href="{{ user | wikiurl }}">
<a href="{{ user.url }}">{% endif %} {{ user }} ({{ timestamp|strfts() }})
{{ user.login }} ({{ timestamp|strfts() }}) </a>
{% if user.url %}</a>
{% endif %}
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>