Add multiuser support

Currently, this uses anonymous sessions. You log in to your instance, and you
get associated with a session. Your client information and account information
are persisted (created only if needed, reattached if not). Passwords are never
stored, only access tokens.
This commit is contained in:
Jason McBrayer 2018-04-24 14:49:43 -04:00
parent 5c48180a7c
commit 267e94077f
5 changed files with 62 additions and 19 deletions

View File

@ -15,7 +15,7 @@ Brutaldon is not ready for use yet.
* [X] Single user read-only access; log in and read home timeline * [X] Single user read-only access; log in and read home timeline
* [X] Fix edge cases of toot display (CW, media, boosts) * [X] Fix edge cases of toot display (CW, media, boosts)
* [ ] Multi-user, multi-instance support * [X] Multi-user, multi-instance support
* [ ] Add support for reading local and federated timelines, notifications, favorites, threads * [ ] Add support for reading local and federated timelines, notifications, favorites, threads
* [ ] Add support for tag timelines * [ ] Add support for tag timelines
* [ ] Add support for viewing profiles * [ ] Add support for viewing profiles

View File

@ -3,7 +3,7 @@ from django import forms
class LoginForm(forms.Form): class LoginForm(forms.Form):
instance = forms.CharField(label="Instance", instance = forms.CharField(label="Instance",
max_length=256) max_length=256)
username = forms.CharField(label="Username", username = forms.CharField(label="Email",
max_length=256) max_length=256)
password = forms.CharField(widget=forms.PasswordInput()) password = forms.CharField(widget=forms.PasswordInput())

View File

@ -0,0 +1,18 @@
# Generated by Django 2.0.4 on 2018-04-24 18:24
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('brutaldon', '0003_auto_20180424_1255'),
]
operations = [
migrations.AlterField(
model_name='account',
name='username',
field=models.EmailField(max_length=254),
),
]

View File

@ -11,7 +11,7 @@ class Client(models.Model):
return self.name + ": " + self.api_base_id return self.name + ": " + self.api_base_id
class Account(models.Model): class Account(models.Model):
username = models.CharField(max_length=80) username = models.EmailField()
django_user = models.ForeignKey(settings.AUTH_USER_MODEL, models.CASCADE, null=True) django_user = models.ForeignKey(settings.AUTH_USER_MODEL, models.CASCADE, null=True)
access_token = models.TextField(null=True, blank=True) access_token = models.TextField(null=True, blank=True)
client= models.ForeignKey(Client, models.SET_NULL, null=True) client= models.ForeignKey(Client, models.SET_NULL, null=True)

View File

@ -4,13 +4,19 @@ from brutaldon.forms import LoginForm
from brutaldon.models import Client, Account from brutaldon.models import Client, Account
from mastodon import Mastodon from mastodon import Mastodon
import datetime import datetime
from urllib import parse
def home(request): def home(request):
now = datetime.datetime.now() now = datetime.datetime.now()
if not (request.session.has_key('instance') and
request.session.has_key('username')):
return redirect(login)
try: try:
client = Client.objects.all()[0] client = Client.objects.get(api_base_id=request.session['instance'])
user = Account.objects.all()[0] user = Account.objects.get(username=request.session['username'])
except: except (Client.DoesNotExist, Client.MultipleObjectsReturned,
Account.DoesNotExist, Account.MultipleObjectsReturned):
return redirect(login) return redirect(login)
mastodon = Mastodon( mastodon = Mastodon(
@ -31,32 +37,51 @@ def login(request):
elif request.method == "POST": elif request.method == "POST":
form = LoginForm(request.POST) form = LoginForm(request.POST)
if form.is_valid(): if form.is_valid():
api_base_url = form.cleaned_data['instance'] # Fixme, make sure this is url api_base_url = form.cleaned_data['instance']
# Fixme, make sure this is url
tmp_base = parse.urlparse(api_base_url.lower())
if tmp_base.netloc == '':
api_base_url = parse.urlunparse(('https', tmp_base.path,
'','','',''))
else:
api_base_url = api_base_url.lower()
request.session['instance'] = api_base_url
username = form.cleaned_data['username'] username = form.cleaned_data['username']
password = form.cleaned_data['password'] password = form.cleaned_data['password']
(client_id, client_secret) = Mastodon.create_app('brutaldon', try:
client = Client.objects.get(api_base_id=api_base_url)
except (Client.DoesNotExist, Client.MultipleObjectsReturned):
(client_id, client_secret) = Mastodon.create_app('brutaldon',
api_base_url=api_base_url) api_base_url=api_base_url)
client = Client( client = Client(
api_base_id = api_base_url, api_base_id = api_base_url,
client_id=client_id, client_id=client_id,
client_secret = client_secret) client_secret = client_secret)
client.save() client.save()
mastodon = Mastodon( mastodon = Mastodon(
client_id = client_id, client_id = client.client_id,
client_secret = client_secret, client_secret = client.client_secret,
api_base_url = api_base_url) api_base_url = api_base_url)
access_token = mastodon.log_in(username, access_token = mastodon.log_in(username,
password) password)
account = Account(
username = username, try:
access_token = access_token) account = Account.objects.get(username=username, client_id=client.id)
account.access_token = access_token
except (Account.DoesNotExist, Account.MultipleObjectsReturned):
account = Account(
username = username,
access_token = access_token,
client = client)
account.save() account.save()
request.session['username'] = username
return redirect(home) return redirect(home)
else: else:
return redirect(error) return render(request, 'setup/login.html', {'form': form})
def error(request): def error(request):
return render('error.html', { 'error': "Not logged in yet."}) return render('error.html', { 'error': "Not logged in yet."})