2017-04-02 00:29:58 +00:00
# -*- coding: utf-8 -*-
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
2018-03-20 22:28:34 +00:00
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
2018-05-08 01:53:14 +00:00
import datetime
2017-04-02 00:29:58 +00:00
2017-04-07 07:35:04 +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 :
2018-03-20 22:28:34 +00:00
user . permissions [ ' read ' ] . check ( g . user )
menu . append ( g . user . url ( ' full ' ) , " %s @ %s " % ( 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
2017-04-13 13:46:20 +00:00
try :
2018-03-20 22:28:34 +00:00
PGPControl . permissions [ ' read ' ] . check ( g . user )
2017-04-13 13:46:20 +00:00
menu . append ( PGPControl . url ( ' full ' , handle = self . user . handle_string ) , ' PGP Management ' )
except poobrains . auth . AccessDenied :
pass
try :
2018-03-20 22:28:34 +00:00
CertControl . permissions [ ' read ' ] . check ( g . user )
2017-04-13 13:46:20 +00:00
menu . append ( CertControl . url ( ' full ' , handle = self . user . handle_string ) , ' Certificate Management ' )
except poobrains . auth . AccessDenied :
pass
try :
2018-03-20 22:28:34 +00:00
NotificationControl . permissions [ ' read ' ] . check ( g . user )
2017-04-13 13:46:20 +00:00
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 ) , ' %d unread notifications ' % notification_count )
except poobrains . auth . AccessDenied :
pass
2017-04-10 04:04:29 +00:00
2017-04-07 07:35:04 +00:00
self . items . append ( menu )
2017-04-09 23:54:08 +00:00
2017-08-09 18:34:50 +00:00
@app.box ( ' dashbar ' )
2017-04-07 07:35:04 +00:00
def dashbar ( ) :
2018-03-20 22:28:34 +00:00
user = g . user
2017-04-07 07:35:04 +00:00
if user . id != 1 : # not "anonymous"
2018-03-20 22:28:34 +00:00
return Dashbar ( g . user )
2017-04-07 07:35:04 +00:00
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 ( [
2018-05-08 01:53:14 +00:00
( ' 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
2018-05-08 01:53:14 +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
2018-05-08 01:53:14 +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 :
2018-11-07 03:22:41 +00:00
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
2018-05-08 01:53:14 +00:00
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
2018-05-08 01:53:14 +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 :
2018-03-20 22:28:34 +00:00
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 )
2018-05-08 01:53:14 +00:00
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
2018-05-08 01:53:14 +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 = " Your current key is: %s " % self . user . pgp_fingerprint )
self . fields . order = [ ' current_key ' , ' pubkey ' ]
2017-04-02 00:29:58 +00:00
2017-09-30 02:21:33 +00:00
def process ( self , submit ) :
2017-04-02 00:29:58 +00:00
pubkey = self . fields [ ' pubkey ' ] . value . read ( )
crypto = poobrains . mailing . getgpg ( )
2017-07-04 00:21:41 +00:00
result = crypto . import_keys ( pubkey )
2017-04-02 00:29:58 +00:00
2017-07-04 00:21:41 +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 ( u " Something went wrong when importing your new key. A pack of lazy raccoons has been dispatched to look at your plight in disinterested amusement. " )
2017-08-09 18:34:50 +00:00
app . logger . error ( " GPG key import error: %s " % result . stderr )
2017-07-04 00:21:41 +00:00
return flask . redirect ( flask . request . path ) # reload page to show flash()es
2017-04-02 00:29:58 +00:00
2017-04-10 04:04:29 +00:00
class NotificationControl ( poobrains . auth . Protected ) :
2017-04-11 04:53:15 +00:00
results = None
pagination = None
2017-04-10 04:04:29 +00:00
2017-04-11 04:53:15 +00:00
def __init__ ( self , handle = None , offset = 0 , * * kwargs ) :
2017-04-10 04:04:29 +00:00
super ( NotificationControl , self ) . __init__ ( * * kwargs )
2017-04-11 04:53:15 +00:00
user = poobrains . auth . User . load ( handle )
2017-04-10 04:04:29 +00:00
2017-04-10 16:52:45 +00:00
self . form = NotificationForm ( )
2017-04-10 04:04:29 +00:00
2017-10-23 00:29:35 +00:00
pagination = poobrains . storage . Pagination ( [ user . notifications_unread , user . notifications . where ( poobrains . auth . Notification . read == True ) ] , offset , ' site.notification_offset ' , handle = handle )
2017-04-11 04:53:15 +00:00
self . results = pagination . results
self . pagination = pagination . menu
2018-05-08 01:53:14 +00:00
self . table = poobrains . rendering . Table ( css_class = ' notifications ' )
2017-06-06 12:25:02 +00:00
2017-04-11 04:53:15 +00:00
for notification in pagination . results :
2017-04-12 01:28:22 +00:00
classes = ' read inactive ' if notification . read else ' unread active '
2017-10-20 01:49:57 +00:00
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-10 04:04:29 +00:00
2017-04-10 16:52:45 +00:00
2017-04-12 01:28:22 +00:00
@poobrains.helpers.themed
2017-04-10 16:52:45 +00:00
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 ( ) )
2017-11-06 01:41:16 +00:00
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 ' )
2017-09-09 11:27:53 +00:00
else :
2017-06-06 12:25:02 +00:00
2017-09-09 11:27:53 +00:00
if len ( self . form . fields [ ' mark ' ] . value ) : # means we have to issue a query
2017-10-20 01:49:57 +00:00
self . form . process ( flask . request . form [ ' submit ' ] [ len ( self . form . ref_id ) + 1 : ] )
2017-09-09 11:27:53 +00:00
return flask . redirect ( flask . request . path )
2017-04-12 01:28:22 +00:00
return self
2017-04-10 16:52:45 +00:00
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 ' )
2017-04-10 16:52:45 +00:00
class NotificationForm ( poobrains . form . Form ) :
2017-10-20 01:49:57 +00:00
mark = poobrains . form . fields . Checkbox ( type = poobrains . form . types . StorableInstanceParamType ( poobrains . auth . Notification ) , multi = True )
2017-04-11 04:53:15 +00:00
mark_read = poobrains . form . Button ( ' submit ' , label = ' Mark as read ' )
2017-04-10 16:52:45 +00:00
delete = poobrains . form . Button ( ' submit ' , label = ' Delete ' )
2017-09-30 02:21:33 +00:00
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 :
2017-06-10 23:47:12 +00:00
flask . flash ( u " Marking notification %d as read. " % instance . id )
2017-04-12 01:28:22 +00:00
instance . read = True
instance . save ( )
elif self . controls [ ' delete ' ] . value :
2017-06-10 23:47:12 +00:00
flask . flash ( u " Deleting notification %d . " % instance . id )
2017-04-12 01:28:22 +00:00
instance . delete_instance ( )
2017-04-10 21:45:52 +00:00
return self