import base64
-from models import CommunityAuthSite
+from models import CommunityAuthSite, CommunityAuthOrg
class CommunityAuthSiteAdminForm(forms.ModelForm):
class Meta:
return self.readonly_fields
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
return self.cleaned_data
raise e
+class CommunityAuthConsentForm(forms.Form):
+ consent = forms.BooleanField(help_text='Consent to sharing this data')
+ next = forms.CharField(widget=forms.widgets.HiddenInput())
+
+ def __init__(self, orgname, *args, **kwargs):
+ self.orgname = orgname
+ super(CommunityAuthConsentForm, self).__init__(*args, **kwargs)
+
+ self.fields['consent'].label = 'Consent to sharing data with {0}'.format(self.orgname)
class SignupForm(forms.Form):
username = forms.CharField(max_length=30)
--- /dev/null
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.10 on 2018-05-29 17:20
+from __future__ import unicode_literals
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('account', '0002_lowercase_email'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='CommunityAuthConsent',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('consentgiven', models.DateTimeField()),
+ ],
+ ),
+ migrations.CreateModel(
+ name='CommunityAuthOrg',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('orgname', models.CharField(help_text=b'Name of the organisation', max_length=100)),
+ ('require_consent', models.BooleanField(default=True)),
+ ],
+ ),
+ migrations.RunSQL("INSERT INTO account_communityauthorg (orgname, require_consent) VALUES ('PostgreSQL Global Development Group', false)", reverse_sql=migrations.RunSQL.noop),
+ migrations.AddField(
+ model_name='communityauthconsent',
+ name='org',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='account.CommunityAuthOrg'),
+ ),
+ migrations.AddField(
+ model_name='communityauthconsent',
+ name='user',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
+ ),
+ migrations.AddField(
+ model_name='communityauthsite',
+ name='org',
+ field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='account.CommunityAuthOrg'),
+ preserve_default=False,
+ ),
+ migrations.AlterUniqueTogether(
+ name='communityauthconsent',
+ unique_together=set([('user', 'org')]),
+ ),
+ ]
from django.db import models
from django.contrib.auth.models import User
+class CommunityAuthOrg(models.Model):
+ orgname = models.CharField(max_length=100, null=False, blank=False,
+ help_text="Name of the organisation")
+ require_consent = models.BooleanField(null=False, blank=False, default=True)
+
+ def __unicode__(self):
+ return self.orgname
+
class CommunityAuthSite(models.Model):
name = models.CharField(max_length=100, null=False, blank=False,
help_text="Note that the value in this field is shown on the login page, so make sure it's user-friendly!")
cryptkey = models.CharField(max_length=100, null=False, blank=False,
help_text="Use tools/communityauth/generate_cryptkey.py to create a key")
comment = models.TextField(null=False, blank=True)
+ org = models.ForeignKey(CommunityAuthOrg, null=False, blank=False)
cooloff_hours = models.IntegerField(null=False, blank=False, default=0,
help_text="Number of hours a user must have existed in the systems before allowed to log in to this site")
def __unicode__(self):
return self.name
+class CommunityAuthConsent(models.Model):
+ user = models.ForeignKey(User, null=False, blank=False)
+ org = models.ForeignKey(CommunityAuthOrg, null=False, blank=False)
+ consentgiven = models.DateTimeField(null=False, blank=False)
+
+ class Meta:
+ unique_together = (('user', 'org'), )
+
class EmailChangeToken(models.Model):
user = models.OneToOneField(User, null=False, blank=False)
email = models.EmailField(max_length=75, null=False, blank=False)
# Community authenticatoin
url(r'^auth/(\d+)/$', pgweb.account.views.communityauth),
url(r'^auth/(\d+)/logout/$', pgweb.account.views.communityauth_logout),
+ url(r'^auth/(\d+)/consent/$', pgweb.account.views.communityauth_consent),
url(r'^auth/(\d+)/search/$', pgweb.account.views.communityauth_search),
url(r'^auth/(\d+)/getkeys/(\d+/)?$', pgweb.account.views.communityauth_getkeys),
from pgweb.downloads.models import Product
from pgweb.profserv.models import ProfessionalService
-from models import CommunityAuthSite, EmailChangeToken
+from models import CommunityAuthSite, CommunityAuthConsent, EmailChangeToken
from forms import PgwebAuthenticationForm
+from forms import CommunityAuthConsentForm
from forms import SignupForm, SignupOauthForm
from forms import UserForm, UserProfileForm, ContributorForm
from forms import ChangeEmailForm, PgwebPasswordResetForm
else:
d = None
+ if d:
+ urldata = "?d=%s" % d
+ elif su:
+ urldata = "?su=%s" % su
+ else:
+ urldata = ""
+
# Verify if the user is authenticated, and if he/she is not, generate
# a login form that has information about which site is being logged
# in to, and basic information about how the community login system
# works.
if not request.user.is_authenticated():
- if d:
- urldata = "?d=%s" % d
- elif su:
- urldata = "?su=%s" % su
- else:
- urldata = ""
if request.method == "POST" and 'next' in request.POST and 'this_is_the_login_form' in request.POST:
# This is a postback of the login form. So pick the next filed
# from that one, so we keep it across invalid password entries.
'site': site,
})
+ if site.org.require_consent:
+ if not CommunityAuthConsent.objects.filter(org=site.org, user=request.user).exists():
+ return HttpResponseRedirect('/account/auth/{0}/consent/?{1}'.format(siteid,
+ urllib.urlencode({'next': '/account/auth/{0}/{1}'.format(siteid, urldata)})))
+
info = {
'u': request.user.username.encode('utf-8'),
'f': request.user.first_name.encode('utf-8'),
# Redirect user back to the specified suburl
return HttpResponseRedirect("%s?s=logout" % site.redirecturl)
+def communityauth_consent(request, siteid):
+ org = get_object_or_404(CommunityAuthSite, id=siteid).org
+ if request.method == 'POST':
+ form = CommunityAuthConsentForm(org.orgname, data=request.POST)
+ if form.is_valid():
+ CommunityAuthConsent(user=request.user, org=org, consentgiven=datetime.now()).save()
+ return HttpResponseRedirect(form.cleaned_data['next'])
+ else:
+ form = CommunityAuthConsentForm(org.orgname, initial={'next': request.GET['next']})
+
+ return render_pgweb(request, 'account', 'base/form.html', {
+ 'form': form,
+ 'operation': 'Authentication',
+ 'form_intro': 'The site you are about to log into is run by {0}. If you choose to proceed with this authentication, your name and email address will be shared with <em>{1}</em>.</p><p>Please confirm that you consent to this sharing.'.format(org.orgname, org.orgname),
+ 'savebutton': 'Proceed with login',
+ })
+
+
def _encrypt_site_response(site, s):
# Encrypt it with the shared key (and IV!)
r = Random.new()
postgresql.org community login system. In this system you create a
central account that is used to log into most postgresql.org services.
Once you are logged into this account, you will automatically be
-logged in to the associated postgresql.org services. Note that this
-single sign on is only used for official postgresql.org websites.
+logged in to the associated postgresql.org services.
</p>
<p>
Your account does not have all the fields filled out required to perform
postgresql.org community login system. In this system you create a
central account that is used to log into most postgresql.org services.
Once you are logged into this account, you will automatically be
-logged in to the associated postgresql.org services. Note that this
-single sign on is only used for official postgresql.org websites.
+logged in to the associated postgresql.org services.
{%else%}
Please log in to your community account to reach this area.
{%endif%}