mirror of
https://github.com/nottinghamtec/PyRIGS.git
synced 2026-01-24 17:02:18 +00:00
Made it work :)
This commit is contained in:
@@ -79,58 +79,21 @@ AUTHENTICATION_BACKENDS = (
|
|||||||
'django.contrib.auth.backends.ModelBackend',
|
'django.contrib.auth.backends.ModelBackend',
|
||||||
)
|
)
|
||||||
|
|
||||||
# Environ
|
|
||||||
# DISCOURSE_HOST='http://localhost:4000'
|
|
||||||
# DISCOURSE_SSO_SECRET='ABCDEFGHIJKLMNOP'
|
|
||||||
|
|
||||||
SOCIAL_AUTH_LOGIN_REDIRECT_URL = '/rigboard'
|
|
||||||
SOCIAL_AUTH_LOGIN_URL = '/'
|
|
||||||
|
|
||||||
SOCIAL_AUTH_PIPELINE = (
|
SOCIAL_AUTH_PIPELINE = (
|
||||||
# Get the information we can about the user and return it in a simple
|
'social.pipeline.social_auth.social_details', # Load remote details
|
||||||
# format to create the user instance later. On some cases the details are
|
'social.pipeline.social_auth.social_uid', # Load remote ID
|
||||||
# already part of the auth response from the provider, but sometimes this
|
'social.pipeline.social_auth.auth_allowed', # Check not blacklisted
|
||||||
# could hit a provider API.
|
'social.pipeline.social_auth.social_user', # If already associated, login
|
||||||
'social.pipeline.social_auth.social_details',
|
'RIGS.discourse.pipeline.new_connection', # Choose a user account, much UI
|
||||||
|
'social.pipeline.social_auth.associate_user', # Associate the social auth with the user
|
||||||
# Get the social uid from whichever service we're authing thru. The uid is
|
'social.pipeline.user.user_details', # Save any details that changed
|
||||||
# the unique identifier of the given user in the provider.
|
|
||||||
'social.pipeline.social_auth.social_uid',
|
|
||||||
|
|
||||||
# Verifies that the current auth process is valid within the current
|
|
||||||
# project, this is were emails and domains whitelists are applied (if
|
|
||||||
# defined).
|
|
||||||
'social.pipeline.social_auth.auth_allowed',
|
|
||||||
|
|
||||||
# Checks if the current social-account is already associated in the site.
|
|
||||||
'social.pipeline.social_auth.social_user',
|
|
||||||
|
|
||||||
# Make up a username for this person, appends a random string at the end if
|
|
||||||
# there's any collision.
|
|
||||||
# 'social.pipeline.user.get_username',
|
|
||||||
|
|
||||||
# Send a validation email to the user to verify its email address.
|
|
||||||
# Disabled by default.
|
|
||||||
# 'social.pipeline.mail.mail_validation',
|
|
||||||
|
|
||||||
# Associates the current social details with another user account with
|
|
||||||
# a similar email address. Disabled by default.
|
|
||||||
'social.pipeline.social_auth.associate_by_email',
|
|
||||||
|
|
||||||
# Create a user account if we haven't found one yet.
|
|
||||||
#'social.pipeline.user.create_user',
|
|
||||||
|
|
||||||
# Create the record that associated the social account with this user.
|
|
||||||
#'social.pipeline.social_auth.associate_user',
|
|
||||||
|
|
||||||
# Populate the extra_data field in the social record with the values
|
|
||||||
# specified by settings (and the default ones like access_token, etc).
|
|
||||||
'social.pipeline.social_auth.load_extra_data',
|
|
||||||
|
|
||||||
# Update the user record with any changed info from the auth service.
|
|
||||||
'social.pipeline.user.user_details',
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
DISCOURSE_HOST=os.environ.get('DISCOURSE_HOST') if os.environ.get('DISCOURSE_HOST') else 'http://localhost:4000'
|
||||||
|
DISCOURSE_SSO_SECRET=os.environ.get('DISCOURSE_SSO_SECRET') if os.environ.get('DISCOURSE_SSO_SECRET') else 'ABCDEFGHIJKLMNOP'
|
||||||
|
|
||||||
|
REGISTRATION_OPEN = False # Disable built-in django registration - must register using forum
|
||||||
|
|
||||||
ROOT_URLCONF = 'PyRIGS.urls'
|
ROOT_URLCONF = 'PyRIGS.urls'
|
||||||
|
|
||||||
WSGI_APPLICATION = 'PyRIGS.wsgi.application'
|
WSGI_APPLICATION = 'PyRIGS.wsgi.application'
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import os
|
|
||||||
from social.backends.base import BaseAuth
|
from social.backends.base import BaseAuth
|
||||||
from social.exceptions import AuthException
|
from django.conf import settings
|
||||||
|
|
||||||
from .sso import DiscourseSSO
|
from .sso import DiscourseSSO
|
||||||
|
|
||||||
@@ -20,8 +19,8 @@ class DiscourseAssociation(object):
|
|||||||
class DiscourseAuth(BaseAuth):
|
class DiscourseAuth(BaseAuth):
|
||||||
"""Discourse authentication backend"""
|
"""Discourse authentication backend"""
|
||||||
name = 'discourse'
|
name = 'discourse'
|
||||||
secret = os.environ['DISCOURSE_SSO_SECRET']
|
secret = settings.DISCOURSE_SSO_SECRET
|
||||||
host = os.environ['DISCOURSE_HOST']
|
host = settings.DISCOURSE_HOST
|
||||||
|
|
||||||
EXTRA_DATA = [
|
EXTRA_DATA = [
|
||||||
('username', 'username'),
|
('username', 'username'),
|
||||||
|
|||||||
62
RIGS/discourse/pipeline.py
Normal file
62
RIGS/discourse/pipeline.py
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.contrib.auth import REDIRECT_FIELD_NAME
|
||||||
|
from django.shortcuts import render_to_response
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
|
from social.pipeline.partial import partial
|
||||||
|
|
||||||
|
from RIGS.models import Profile
|
||||||
|
from RIGS import forms
|
||||||
|
|
||||||
|
|
||||||
|
class SocialRegisterForm(forms.ProfileRegistrationFormUniqueEmail):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(SocialRegisterForm, self).__init__(*args, **kwargs)
|
||||||
|
self.fields.pop('password1')
|
||||||
|
self.fields.pop('password2')
|
||||||
|
self.fields.pop('captcha')
|
||||||
|
|
||||||
|
self.fields['email'].widget.attrs['readonly'] = True
|
||||||
|
|
||||||
|
def clean_email(self):
|
||||||
|
initial = getattr(self, 'initial', None)
|
||||||
|
if(initial['email'] != self.cleaned_data['email']):
|
||||||
|
raise ValidationError("You cannot change the email")
|
||||||
|
|
||||||
|
return initial['email']
|
||||||
|
|
||||||
|
|
||||||
|
@partial
|
||||||
|
def new_connection(backend, details, response, user=None, is_new=False, social=None, request=None, *args, **kwargs):
|
||||||
|
if social is not None:
|
||||||
|
return
|
||||||
|
|
||||||
|
data = backend.strategy.request_data()
|
||||||
|
|
||||||
|
if data.get('UseCurrentAccount') is not None:
|
||||||
|
return
|
||||||
|
|
||||||
|
alreadyLoggedIn = user is not None
|
||||||
|
|
||||||
|
context = {
|
||||||
|
'details': details,
|
||||||
|
'alreadyLoggedIn': alreadyLoggedIn,
|
||||||
|
'loggedInUser': user,
|
||||||
|
}
|
||||||
|
|
||||||
|
if not alreadyLoggedIn:
|
||||||
|
completeUrl = reverse('social:complete', kwargs={'backend': backend.name})
|
||||||
|
context['login_url'] = "{0}?{1}={2}".format(reverse('login'), REDIRECT_FIELD_NAME, completeUrl)
|
||||||
|
|
||||||
|
if data.get('username') is None:
|
||||||
|
form = SocialRegisterForm(initial=details)
|
||||||
|
else:
|
||||||
|
form = SocialRegisterForm(data, initial=details)
|
||||||
|
|
||||||
|
if form.is_valid():
|
||||||
|
new_user = Profile.objects.create_user(**form.cleaned_data)
|
||||||
|
return {'user': new_user}
|
||||||
|
|
||||||
|
context['form'] = form
|
||||||
|
|
||||||
|
return render_to_response('RIGS/social-associate.html', context)
|
||||||
@@ -3,6 +3,7 @@ from django import forms
|
|||||||
from django.utils import formats
|
from django.utils import formats
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core import serializers
|
from django.core import serializers
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
from django.contrib.auth.forms import UserCreationForm, UserChangeForm, AuthenticationForm, PasswordResetForm
|
from django.contrib.auth.forms import UserCreationForm, UserChangeForm, AuthenticationForm, PasswordResetForm
|
||||||
from registration.forms import RegistrationFormUniqueEmail
|
from registration.forms import RegistrationFormUniqueEmail
|
||||||
from captcha.fields import ReCaptchaField
|
from captcha.fields import ReCaptchaField
|
||||||
@@ -27,6 +28,16 @@ class ProfileRegistrationFormUniqueEmail(RegistrationFormUniqueEmail):
|
|||||||
raise forms.ValidationError("These initials are already in use. Please supply different initials.")
|
raise forms.ValidationError("These initials are already in use. Please supply different initials.")
|
||||||
return self.cleaned_data['initials']
|
return self.cleaned_data['initials']
|
||||||
|
|
||||||
|
def clean_first_name(self):
|
||||||
|
if self.cleaned_data["first_name"].strip() == '':
|
||||||
|
raise ValidationError("First name is required.")
|
||||||
|
return self.cleaned_data["first_name"]
|
||||||
|
|
||||||
|
def clean_last_name(self):
|
||||||
|
if self.cleaned_data["last_name"].strip() == '':
|
||||||
|
raise ValidationError("Last name is required.")
|
||||||
|
return self.cleaned_data["last_name"]
|
||||||
|
|
||||||
|
|
||||||
# Login form
|
# Login form
|
||||||
class PasswordReset(PasswordResetForm):
|
class PasswordReset(PasswordResetForm):
|
||||||
|
|||||||
BIN
RIGS/static/imgs/forum-logo.gif
Normal file
BIN
RIGS/static/imgs/forum-logo.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 300 KiB |
@@ -133,6 +133,20 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
|
<h4>Linked to {{object.social_auth.count}} Forum Account(s)</h4>
|
||||||
|
|
||||||
|
{% if object.social_auth.count > 0 %}
|
||||||
|
|
||||||
|
<a href="{% url 'unlink_forum' %}" class="btn btn-danger">
|
||||||
|
Unlink Forum Account(s) <span class="glyphicon glyphicon-pencil"></span>
|
||||||
|
</a>
|
||||||
|
{% else %}
|
||||||
|
<a href="{% url "social:begin" "discourse" %}" class="btn btn-success">
|
||||||
|
Link Forum Account <span class="glyphicon glyphicon-pencil"></span>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
55
RIGS/templates/RIGS/social-associate.html
Normal file
55
RIGS/templates/RIGS/social-associate.html
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load widget_tweaks %}
|
||||||
|
{% block title %}Associate{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="col-sm-10 col-sm-offset-1">
|
||||||
|
|
||||||
|
<div class="text-center">
|
||||||
|
<h1>R<small>ig</small> I<small>nformation</small> G<small>athering</small> S<small>ystem</small></h1>
|
||||||
|
</div>
|
||||||
|
<h2 class="text-center">Welcome <strong>{{details.username}}</strong></h2>
|
||||||
|
<h4 class="text-center">This is the first time you've visited RIGS with your forum account, so we need a few details to get you set up</h4>
|
||||||
|
<hr/>
|
||||||
|
|
||||||
|
|
||||||
|
{% if alreadyLoggedIn %}
|
||||||
|
<h2 class="text-center">You are logged in to RIGS as <strong>{{loggedInUser.username}}</strong></h2>
|
||||||
|
<div class="col-sm-8 col-sm-offset-2">
|
||||||
|
<form action="", method="post">{% csrf_token %}
|
||||||
|
<input type="hidden" name="UseCurrentAccount" value="1"/>
|
||||||
|
<button type="submit" class="btn btn-lg btn-primary center btn-block">Link Forum account to RIGS Account</button>
|
||||||
|
</form>
|
||||||
|
<a class="btn btn-lg btn-warning center btn-block" href="{% url 'logout' %}" role="button">Logout</a>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<h4 class="text-center"><a class="btn btn-info" href="{{login_url}}" role="button">I already have a RIGS account</a></h4>
|
||||||
|
{% if form.errors or supplement_form.errors %}
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
{{form.errors}}
|
||||||
|
{{supplement_form.errors}}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="col-sm-8 col-sm-offset-2">
|
||||||
|
<form action="" method="post" class="form-horizontal" role="form">{% csrf_token %}
|
||||||
|
{% for field in form %}
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="{{ field.id_for_label }}" class="control-label col-sm-4">{{ field.label }}</label>
|
||||||
|
<div class="controls col-sm-8">
|
||||||
|
{% render_field field class+="form-control" placeholder=field.label %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
<p><input type="submit" value="Register" class="btn btn-primary pull-right"></p>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
from django.conf.urls import patterns, url
|
from django.conf.urls import patterns, url
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from RIGS import models, views, rigboard, finance, ical, versioning, forms
|
from RIGS import models, views, rigboard, finance, ical, versioning, forms
|
||||||
|
from RIGS.discourse import views as discourseViews
|
||||||
from django.views.generic import RedirectView
|
from django.views.generic import RedirectView
|
||||||
from django.views.decorators.clickjacking import xframe_options_exempt
|
from django.views.decorators.clickjacking import xframe_options_exempt
|
||||||
|
|
||||||
@@ -15,6 +16,7 @@ urlpatterns = patterns('',
|
|||||||
url(r'^closemodal/$', views.CloseModal.as_view(), name='closemodal'),
|
url(r'^closemodal/$', views.CloseModal.as_view(), name='closemodal'),
|
||||||
|
|
||||||
url('^user/login/$', 'RIGS.views.login', name='login'),
|
url('^user/login/$', 'RIGS.views.login', name='login'),
|
||||||
|
url('^user/associate/$', discourseViews.Associate.as_view(), name='associate'),
|
||||||
url('^user/login/embed/$', xframe_options_exempt(views.login_embed), name='login_embed'),
|
url('^user/login/embed/$', xframe_options_exempt(views.login_embed), name='login_embed'),
|
||||||
url(r'^user/password_reset/$', 'django.contrib.auth.views.password_reset', {'password_reset_form': forms.PasswordReset}),
|
url(r'^user/password_reset/$', 'django.contrib.auth.views.password_reset', {'password_reset_form': forms.PasswordReset}),
|
||||||
|
|
||||||
@@ -153,6 +155,7 @@ urlpatterns = patterns('',
|
|||||||
url(r'^user/edit/$', login_required(views.ProfileUpdateSelf.as_view()),
|
url(r'^user/edit/$', login_required(views.ProfileUpdateSelf.as_view()),
|
||||||
name='profile_update_self'),
|
name='profile_update_self'),
|
||||||
url(r'^user/reset_api_key$', login_required(views.ResetApiKey.as_view(permanent=False)), name='reset_api_key'),
|
url(r'^user/reset_api_key$', login_required(views.ResetApiKey.as_view(permanent=False)), name='reset_api_key'),
|
||||||
|
url(r'^user/unlink_forum$', login_required(views.UnlinkForum.as_view(permanent=False)), name='unlink_forum'),
|
||||||
|
|
||||||
# ICS Calendar - API key authentication
|
# ICS Calendar - API key authentication
|
||||||
url(r'^ical/(?P<api_pk>\d+)/(?P<api_key>\w+)/rigs.ics$', api_key_required(ical.CalendarICS()), name="ics_calendar"),
|
url(r'^ical/(?P<api_pk>\d+)/(?P<api_key>\w+)/rigs.ics$', api_key_required(ical.CalendarICS()), name="ics_calendar"),
|
||||||
|
|||||||
@@ -382,3 +382,12 @@ class ResetApiKey(generic.RedirectView):
|
|||||||
self.request.user.save()
|
self.request.user.save()
|
||||||
|
|
||||||
return reverse_lazy('profile_detail')
|
return reverse_lazy('profile_detail')
|
||||||
|
|
||||||
|
class UnlinkForum(generic.RedirectView):
|
||||||
|
def get_redirect_url(self, *args, **kwargs):
|
||||||
|
for link in self.request.user.social_auth.all():
|
||||||
|
link.delete()
|
||||||
|
|
||||||
|
self.request.user.save()
|
||||||
|
|
||||||
|
return reverse_lazy('profile_detail')
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
|
{% load static from staticfiles %}
|
||||||
|
|
||||||
{% block title %}Login{% endblock %}
|
{% block title %}Login{% endblock %}
|
||||||
|
|
||||||
@@ -6,5 +7,40 @@
|
|||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<h1>R<small>ig</small> I<small>nformation</small> G<small>athering</small> S<small>ystem</small></h1>
|
<h1>R<small>ig</small> I<small>nformation</small> G<small>athering</small> S<small>ystem</small></h1>
|
||||||
</div>
|
</div>
|
||||||
{% include 'registration/loginform.html' %}
|
|
||||||
|
|
||||||
|
<div class="panel-group">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h4 class="panel-title">
|
||||||
|
|
||||||
|
Login with TEC Forum
|
||||||
|
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div id="forumLogin">
|
||||||
|
<div class="panel-body" style="text-align:center;">
|
||||||
|
<a class="btn btn-default" href="{% url "social:begin" "discourse" %}?next={{request.GET.next}}">
|
||||||
|
|
||||||
|
<h4>Login using</h4>
|
||||||
|
<img src="{% static "imgs/forum-logo.gif" %}" width=200></img>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h4 class="panel-title">
|
||||||
|
Login with RIGS Credentials
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="panel-body">
|
||||||
|
{% include 'registration/loginform.html' %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
{% render_field form.password class+="form-control" placeholder=form.password.label %}
|
{% render_field form.password class+="form-control" placeholder=form.password.label %}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
<a href="{% url 'registration_register' %}" class="btn">Register</a>
|
{# <a href="{% url 'registration_register' %}" class="btn">Register</a> #}
|
||||||
<a href="{% url 'password_reset' %}" class="btn">Forgotten Password</a>
|
<a href="{% url 'password_reset' %}" class="btn">Forgotten Password</a>
|
||||||
<input type="submit" value="Login" class="btn btn-primary"/>
|
<input type="submit" value="Login" class="btn btn-primary"/>
|
||||||
<input type="hidden" name="next" value="{{ next }}"/>
|
<input type="hidden" name="next" value="{{ next }}"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user