mirror of
https://github.com/postgres/pgweb.git
synced 2025-08-06 09:57:57 +00:00

This adds the concept of an apiurl to each site that uses community authentication, that the main website server can make calls to and send updates. This URL will receive POSTs from the main website when a user account that has been used on this site gets updated, and can then optionally update it's local entries with it (the django plugin sample is updated to handle this fully). Updates are only sent for users that have a history of having logged into the specific site -- this way we avoid braodcasting user information to sites requiring specific constent that the user hasn't given, and also decreases the amount of updates that have to be sent. Updates are queued by the system in a table and using listen/notify a daemon that's running picks up what needs to be updated and posts it to the endpoints. If this daemon is not running, obviously nothing gets sent. Updates are tracked using triggers in the database which push information into this queue.
92 lines
3.3 KiB
Python
92 lines
3.3 KiB
Python
from django.contrib import admin
|
|
from django.contrib.auth.admin import UserAdmin
|
|
from django.contrib.auth.forms import UserChangeForm
|
|
from django.contrib.auth.models import User
|
|
from django import forms
|
|
|
|
import base64
|
|
|
|
from pgweb.util.widgets import TemplateRenderWidget
|
|
from pgweb.util.db import exec_to_dict
|
|
|
|
from .models import CommunityAuthSite, CommunityAuthOrg
|
|
|
|
|
|
class CommunityAuthSiteAdminForm(forms.ModelForm):
|
|
class Meta:
|
|
model = CommunityAuthSite
|
|
exclude = ()
|
|
|
|
def clean_cryptkey(self):
|
|
x = None
|
|
try:
|
|
x = base64.b64decode(self.cleaned_data['cryptkey'])
|
|
except TypeError:
|
|
raise forms.ValidationError("Crypto key must be base64 encoded")
|
|
|
|
if (len(x) != 16 and len(x) != 24 and len(x) != 32):
|
|
raise forms.ValidationError("Crypto key must be 16, 24 or 32 bytes before being base64-encoded")
|
|
return self.cleaned_data['cryptkey']
|
|
|
|
def clean(self):
|
|
d = super().clean()
|
|
|
|
if d.get('push_changes', False) and not d['apiurl']:
|
|
self.add_error('push_changes', 'API url must be specified to enable push changes!')
|
|
|
|
if d.get('push_ssh', False) and not d.get('push_changes', False):
|
|
self.add_error('push_ssh', 'SSH changes can only be pushed if general change push is enabled')
|
|
|
|
return d
|
|
|
|
|
|
class CommunityAuthSiteAdmin(admin.ModelAdmin):
|
|
list_display = ('name', 'cooloff_hours', 'push_changes', 'push_ssh', 'org')
|
|
form = CommunityAuthSiteAdminForm
|
|
|
|
|
|
class PGUserChangeForm(UserChangeForm):
|
|
logininfo = forms.CharField(label="Community login history", required=False)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(PGUserChangeForm, self).__init__(*args, **kwargs)
|
|
# because the auth.User model is set to "blank=False" and the Django
|
|
# auth.UserChangeForm is setup as a ModelForm, it will always validate
|
|
# the "username" even though it is not present. Thus the best way to
|
|
# avoid the validation is to remove the "username" field, if it exists
|
|
if self.fields.get('username'):
|
|
del self.fields['username']
|
|
|
|
self.fields['logininfo'].widget = TemplateRenderWidget(
|
|
template='forms/widgets/community_auth_usage_widget.html',
|
|
context={
|
|
'logins': exec_to_dict("SELECT s.name AS service, lastlogin, logincount FROM account_communityauthsite s INNER JOIN account_communityauthlastlogin l ON s.id=l.site_id WHERE user_id=%(userid)s ORDER BY lastlogin DESC", {
|
|
'userid': self.instance.pk,
|
|
}),
|
|
})
|
|
|
|
|
|
class PGUserAdmin(UserAdmin):
|
|
"""overrides default Django user admin"""
|
|
form = PGUserChangeForm
|
|
|
|
def get_readonly_fields(self, request, obj=None):
|
|
"""this prevents users from changing a username once created"""
|
|
if obj:
|
|
return self.readonly_fields + ('username',)
|
|
return self.readonly_fields
|
|
|
|
@property
|
|
def fieldsets(self):
|
|
fs = list(super().fieldsets)
|
|
fs.append(
|
|
('Community authentication', {'fields': ('logininfo', )}),
|
|
)
|
|
return fs
|
|
|
|
|
|
admin.site.register(CommunityAuthSite, CommunityAuthSiteAdmin)
|
|
admin.site.register(CommunityAuthOrg)
|
|
admin.site.unregister(User) # have to unregister default User Admin...
|
|
admin.site.register(User, PGUserAdmin) # ...in order to add overrides
|