poobrains/poobrains/profile.py

284 lines
9.8 KiB
Python
Raw Permalink Normal View History

2017-04-09 21:11:08 +00:00
import collections
2017-04-02 10:37:33 +00:00
import OpenSSL as openssl
2017-04-11 22:00:35 +00:00
import werkzeug
2017-04-02 00:29:58 +00:00
import flask
2017-04-11 22:00:35 +00:00
2017-08-09 18:34:50 +00:00
#import poobrains
from poobrains import app, g
2017-08-09 18:34:50 +00:00
import poobrains.helpers
import poobrains.mailing
import poobrains.rendering
import poobrains.form
import poobrains.storage
import poobrains.auth
2017-04-02 00:29:58 +00:00
2017-06-06 12:25:02 +00:00
import time
import datetime
2017-04-02 00:29:58 +00:00
class Dashbar(poobrains.rendering.Container):
user = None
def __init__(self, user, **kwargs):
super(Dashbar, self).__init__(**kwargs)
self.user = user
menu = poobrains.rendering.Menu('dashbar-actions')
2017-04-14 21:29:40 +00:00
try:
user.permissions['read'].check(g.user)
menu.append(g.user.url('full'), f"{user.name}@{app.config['SITE_NAME']}")
except poobrains.auth.AccessDenied:
pass
try:
poobrains.auth.AccessAdminArea.check(g.user)
2017-04-14 21:29:40 +00:00
menu.append(flask.url_for('admin.admin_index'), 'Admin Area')
except poobrains.auth.AccessDenied:
pass
try:
PGPControl.permissions['read'].check(g.user)
menu.append(PGPControl.url('full', handle=self.user.handle_string), 'PGP Management')
except poobrains.auth.AccessDenied:
pass
try:
CertControl.permissions['read'].check(g.user)
menu.append(CertControl.url('full', handle=self.user.handle_string), 'Certificate Management')
except poobrains.auth.AccessDenied:
pass
try:
NotificationControl.permissions['read'].check(g.user)
notification_count = self.user.notifications_unread.count()
if notification_count == 1:
menu.append(NotificationControl.url('full', handle=self.user.handle_string), '1 unread notification')
else:
menu.append(NotificationControl.url('full', handle=self.user.handle_string), f'{notification_count} unread notifications') # FIXME: pretty_si
except poobrains.auth.AccessDenied:
pass
self.items.append(menu)
2017-04-09 23:54:08 +00:00
2017-08-09 18:34:50 +00:00
@app.box('dashbar')
def dashbar():
user = g.user
if user.id != 1: # not "anonymous"
return Dashbar(g.user)
2017-04-09 11:01:32 +00:00
class CertControl(poobrains.auth.Protected):
2017-04-02 00:29:58 +00:00
2017-04-09 21:11:08 +00:00
class Meta:
modes = collections.OrderedDict([
('add', 'create'),
2017-04-10 01:28:29 +00:00
('full', 'read'),
('delete', 'delete')
2017-04-09 21:11:08 +00:00
])
2017-04-02 00:29:58 +00:00
user = None
2017-04-02 10:37:33 +00:00
cert_table = None
2017-04-02 00:29:58 +00:00
2017-04-09 21:11:08 +00:00
def __init__(self, handle=None, cert_handle=None, **kwargs):
2017-04-02 00:29:58 +00:00
super(CertControl, self).__init__(handle=handle, **kwargs)
2017-04-09 11:01:32 +00:00
self.user = poobrains.auth.User.load(handle)
2017-04-09 21:11:08 +00:00
#self.title = self.user.name
2017-04-02 00:29:58 +00:00
self.pre = poobrains.rendering.Menu('certtoken-add')
self.pre.append(self.url(mode='add'), 'Add new')
2017-04-02 10:37:33 +00:00
if len(flask.request.environ['SSL_CLIENT_CERT']):
cert_current = openssl.crypto.load_certificate(openssl.crypto.FILETYPE_PEM, flask.request.environ['SSL_CLIENT_CERT'])
else:
cert_current = None
2017-04-09 21:11:08 +00:00
self.cert_table = poobrains.rendering.Table(columns=['Name', 'Key length', 'Fingerprint', 'Actions'])
2017-04-02 10:37:33 +00:00
for cert_info in self.user.clientcerts:
if cert_current and cert_info.fingerprint == cert_current.digest('sha512').replace(b':', b''):
2017-04-02 10:37:33 +00:00
classes = 'active'
else:
classes = None
2017-04-05 23:20:07 +00:00
actions = poobrains.rendering.Menu('certificate-actions')
2017-04-09 21:11:08 +00:00
actions.append(CertControl.url(mode='delete', handle=handle, cert_handle=cert_info.handle_string), 'Delete')
self.cert_table.append(cert_info.name, cert_info.keylength, cert_info.fingerprint, actions,_classes=classes)
@poobrains.helpers.themed
def view(self, handle=None, cert_handle=None, mode='full', **kwargs):
if mode == 'add':
token = poobrains.auth.ClientCertToken()
token.user = self.user
f = token.form('add')
f.fields['user'].readonly = True
f.created = poobrains.form.fields.Value(value=datetime.datetime.now())
f.redeemed = poobrains.form.fields.Value(value=False)
r = poobrains.helpers.ThemedPassthrough(f.view(**kwargs))
if flask.request.method == 'POST':
return flask.redirect(CertControl.url(mode='full', handle=handle))
return r
2017-04-09 21:11:08 +00:00
if cert_handle is not None and mode == 'delete':
2017-04-09 21:11:08 +00:00
cert = poobrains.auth.ClientCert.load(cert_handle)
if self.user == cert.user:
cert.permissions['read'].check(g.user)
2017-04-09 21:11:08 +00:00
r = cert.form('delete').view(handle=cert_handle, **kwargs)
if flask.request.method in ['POST', 'DELETE']:
return flask.redirect(CertControl.url(mode='full', handle=handle))
return poobrains.helpers.ThemedPassthrough(r)
return poobrains.helpers.ThemedPassthrough(super(CertControl, self).view(handle=handle, cert_handle=cert_handle, mode=mode, **kwargs))
2017-04-09 21:11:08 +00:00
app.site.add_view(CertControl, '/~<handle>/cert/', mode='full', endpoint='certcontrol')
app.site.add_view(CertControl, '/~<handle>/cert/add/', mode='add', endpoint='certcontrol_add')
app.site.add_view(CertControl, '/~<handle>/cert/<cert_handle>', mode='delete', endpoint='certcontrol_delete')
2017-04-09 21:11:08 +00:00
class PGPControl(poobrains.auth.Protected):
2017-04-05 23:20:07 +00:00
2017-04-09 21:11:08 +00:00
user = None
def __init__(self, handle=None, **kwargs):
super(PGPControl, self).__init__(**kwargs)
self.user = poobrains.auth.User.load(handle)
def view(self, handle=None, **kwargs):
2017-04-02 10:37:33 +00:00
2017-04-09 23:54:08 +00:00
r = super(PGPControl, self).view(handle=handle, **kwargs) # checks permissions
return PGPForm(handle=handle).view(handle=handle, **kwargs)
#return r
2017-04-02 00:29:58 +00:00
2017-08-09 18:34:50 +00:00
app.site.add_view(PGPControl, '/~<handle>/pgp', mode='full')
2017-04-02 00:29:58 +00:00
2017-04-09 21:11:08 +00:00
class PGPForm(poobrains.form.Form):
2017-04-02 00:29:58 +00:00
2017-04-09 11:01:32 +00:00
current_key = None
2017-04-02 00:29:58 +00:00
pubkey = poobrains.form.fields.File()
submit = poobrains.form.Button('submit', label='Update key')
2017-04-09 11:01:32 +00:00
def __init__(self, handle=None, **kwargs):
2017-04-02 00:29:58 +00:00
2017-04-09 23:54:08 +00:00
super(PGPForm, self).__init__(**kwargs)
2017-04-09 11:01:32 +00:00
self.user = poobrains.auth.User.load(handle)
self.current_key = poobrains.form.fields.Message(value=f"Your current key is: {self.user.pgp_fingerprint}")
2017-04-09 11:01:32 +00:00
self.fields.order = ['current_key', 'pubkey']
2017-04-02 00:29:58 +00:00
def process(self, submit):
2017-04-02 00:29:58 +00:00
pubkey = self.fields['pubkey'].value.read()
crypto = poobrains.mailing.getgpg()
result = crypto.import_keys(pubkey)
2017-04-02 00:29:58 +00:00
if len(result.fingerprints) == 1:
self.user.pgp_fingerprint = result.fingerprints[0]
self.user.save()
flask.flash(u"Imported new key and assigned it to you.")
elif len(result.fingerprints) > 1:
flask.flash(u"Keyfile may only hold a single key.")
else:
# Fun fact: I'm more proud of this error message than half my code.
flask.flash("Something went wrong when importing your new key. A pack of lazy raccoons has been dispatched to look at your plight in disinterested amusement.", 'error')
app.logger.error(f"GPG key import error: {result.stderr}")
return flask.redirect(flask.request.path) # reload page to show flash()es
2017-04-02 00:29:58 +00:00
class NotificationControl(poobrains.auth.Protected):
results = None
pagination = None
def __init__(self, handle=None, offset=0, **kwargs):
super(NotificationControl, self).__init__(**kwargs)
user = poobrains.auth.User.load(handle)
self.form = NotificationForm()
pagination = poobrains.storage.Pagination([user.notifications_unread, user.notifications.where(poobrains.auth.Notification.read == True)], offset, 'site.notification_offset', handle=handle)
self.results = pagination.results
self.pagination = pagination.menu
self.table = poobrains.rendering.Table(css_class='notifications')
2017-06-06 12:25:02 +00:00
for notification in pagination.results:
2017-04-12 01:28:22 +00:00
classes = 'read inactive' if notification.read else 'unread active'
mark_checkbox = poobrains.form.fields.Checkbox(form=self.form, name='mark', label='', type=poobrains.form.types.StorableInstanceParamType(poobrains.auth.Notification), choices=[(notification, None)], multi=True)
2017-04-12 01:28:22 +00:00
self.table.append(notification, mark_checkbox, _classes=classes)
2017-04-12 01:28:22 +00:00
@poobrains.helpers.themed
def view(self, handle=None, **kwargs):
if flask.request.method in ['POST', 'DELETE']:
2017-04-12 01:28:22 +00:00
2017-04-11 22:00:35 +00:00
values = flask.request.form.get(self.form.name, werkzeug.datastructures.MultiDict())
2017-04-12 01:28:22 +00:00
try:
self.form.bind(values, werkzeug.datastructures.MultiDict())
except poobrains.errors.CompoundError as e:
2017-04-12 01:28:22 +00:00
for error in e.errors:
2018-11-10 17:42:41 +00:00
flask.flash(str(e), 'error')
else:
2017-06-06 12:25:02 +00:00
if len(self.form.fields['mark'].value): # means we have to issue a query
self.form.process(flask.request.form['submit'][len(self.form.ref_id)+1:])
return flask.redirect(flask.request.path)
2017-04-12 01:28:22 +00:00
return self
2017-08-09 18:34:50 +00:00
app.site.add_view(NotificationControl, '/~<handle>/notifications/', mode='full')
app.site.add_view(NotificationControl, '/~<handle>/notifications/+<int:offset>', mode='full', endpoint='notification_offset')
class NotificationForm(poobrains.form.Form):
mark = poobrains.form.fields.Checkbox(type=poobrains.form.types.StorableInstanceParamType(poobrains.auth.Notification), multi=True)
mark_read = poobrains.form.Button('submit', label='Mark as read')
delete = poobrains.form.Button('submit', label='Delete')
def process(self, submit):
2017-06-06 12:25:02 +00:00
2017-04-12 01:28:22 +00:00
for handle in self.fields['mark'].value:
instance = poobrains.auth.Notification.load(handle)
if self.controls['mark_read'].value:
flask.flash(f"Marking notification {instance.id} as read.")
2017-04-12 01:28:22 +00:00
instance.read = True
instance.save()
elif self.controls['delete'].value:
flask.flash(f"Deleting notification {instance.id}.")
2017-04-12 01:28:22 +00:00
instance.delete_instance()
2017-04-10 21:45:52 +00:00
return self