Compare commits

...

8 Commits

41 changed files with 307 additions and 8 deletions

View File

@ -4,7 +4,7 @@ By popular demand, I'm building a better anonkun. It doesn't do much right now t
## Requirements ## Requirements
Python 3.6+ Python 3.6+
PostgreSQL 10.4+ PostgreSQL 10.4+
Python packages: `django psycopg2 channels jinja2` Python packages: `django psycopg2 channels jinja2 argon2-cffi`
## Install ## Install
``` ```

View File

@ -3,7 +3,9 @@
/create_quest app views. /create_quest app views.
""" """
from django.shortcuts import render from django.shortcuts import render
from django.contrib.auth.decorators import login_required
@login_required(login_url='/login/')
def index(request): def index(request):
""" """
The index page for creating new quests. The index page for creating new quests.

View File

@ -3,7 +3,8 @@
{% block content %} {% block content %}
<h1>Quests 'n Shiet</h1> <h1>Quests 'n Shiet</h1>
<a href="./quest/1">Unga Bunga Quest</a><br /> <a href="./quest/1">Unga Bunga Quest</a><br />
<a href="./create_quest">Create New Quest</a><br /> <a href="{{ url('create_quest:index') }}">Create New Quest</a><br />
<a href="./signup">Sign up</a><br /> <a href="{{ url('signup:index') }}">Sign up</a><br />
<a href="./login">Login</a><br /> <a href="{{ url('login:index') }}">Login</a><br />
<a href="{{ url('logout:index') }}">Logout</a><br />
{% endblock %} {% endblock %}

View File

@ -36,6 +36,14 @@
<ul id="headerHidden" class="header" style="{% if request.session.get("hide_header") == True %}display:initial;{% else %}display:none;{% endif %}"> <ul id="headerHidden" class="header" style="{% if request.session.get("hide_header") == True %}display:initial;{% else %}display:none;{% endif %}">
<li><a onclick="toggleHeader();" href="javascript:void(0);"></a></li> <li><a onclick="toggleHeader();" href="javascript:void(0);"></a></li>
</ul> </ul>
<br /> <!-- TODO: make this more exact. -->
<div id="messages">
<ul class="messages">
{% for message in get_messages(request) %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
{% endfor %}
</ul>
</div>
<div id="content">{% block content %}{% endblock %} </div> <div id="content">{% block content %}{% endblock %} </div>
</body> </body>
</html> </html>

0
login/__init__.py Normal file
View File

3
login/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

5
login/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class LoginConfig(AppConfig):
name = 'login'

View File

@ -0,0 +1,11 @@
{% extends "base.html" %}
{% block title %}Login{% endblock %}
{% block content %}
<h1>Login</h1>
<form method="post" action="{{ url('login:index') }}">
{{ csrf_input }}
<input type="text" placeholder="Username" name="username" maxlength="20" required/><br />
<input type="password" placeholder="Password" name="password" maxlength="1024" required/><br />
<input type="submit" value="Log in" name="submit"/>
</form>
{% endblock %}

View File

3
login/models.py Normal file
View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

3
login/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

12
login/urls.py Normal file
View File

@ -0,0 +1,12 @@
#!/usr/bin/env python3
"""
Login app URL configuration.
"""
from django.urls import path
from . import views
app_name = 'login'
urlpatterns = [
path('', views.index, name='index'),
]

25
login/views.py Normal file
View File

@ -0,0 +1,25 @@
#!/usr/bin/env python3
"""
/login app views.
"""
from django.contrib import messages
from django.shortcuts import redirect, render
from django.contrib.auth import authenticate, login
def index(request):
"""
The login page.
"""
if request.method == "GET":
context = {}
return render(request, 'login/index.html', context)
username = request.POST['username']
password = request.POST['password']
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
messages.success(request, "Logged in")
return redirect('homepage:index')
else:
messages.error(request, "Invalid credentials")
return redirect('login:index')

0
logout/__init__.py Normal file
View File

3
logout/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

5
logout/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class LogoutConfig(AppConfig):
name = 'logout'

View File

3
logout/models.py Normal file
View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

3
logout/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

12
logout/urls.py Normal file
View File

@ -0,0 +1,12 @@
#!/usr/bin/env python3
"""
Logout app URL configuration.
"""
from django.urls import path
from . import views
app_name = 'logout'
urlpatterns = [
path('', views.index, name='index'),
]

15
logout/views.py Normal file
View File

@ -0,0 +1,15 @@
#!/usr/bin/env python3
"""
/logout app views.
"""
from django.contrib import messages
from django.shortcuts import redirect
from django.contrib.auth import logout
def index(request):
"""
Logs the user out.
"""
logout(request)
messages.success(request, "Logged out")
return redirect('homepage:index')

0
signup/__init__.py Normal file
View File

3
signup/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

5
signup/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class SignupConfig(AppConfig):
name = 'signup'

View File

@ -0,0 +1,24 @@
{% extends "base.html" %}
{% block title %}Sign up a new account{% endblock %}
{% block content %}
<h1>Sign up</h1>
<div id="namePassRules">
<p>Username rules:
<ul>
<li>Must be between 3 and 20 characters</li>
<li>Can only contain ASCII letters (case sensitive) and numbers</li>
</ul>
<p>Password rules:
<ul>
<li>Must be between 8 and 1024 characters</li>
</ul>
</div>
<form method="post" action="{{ url('signup:index') }}">
{{ csrf_input }}
<input type="text" placeholder="Username" name="username" maxlength="20" required/><br />
<input type="text" placeholder="Email" name="email" maxlength="20" required/><br />
<input type="password" placeholder="Password" name="password" maxlength="1024" required/><br />
<input type="password" placeholder="Verify password" name="password_verify" maxlength="1024" required/><br />
<input type="submit" value="Sign up" name="submit"/>
</form>
{% endblock %}

View File

3
signup/models.py Normal file
View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

3
signup/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

12
signup/urls.py Normal file
View File

@ -0,0 +1,12 @@
#!/usr/bin/env python3
"""
signup app URL configuration.
"""
from django.urls import path
from . import views
app_name = 'signup'
urlpatterns = [
path('', views.index, name='index'),
]

13
signup/views.py Normal file
View File

@ -0,0 +1,13 @@
#!/usr/bin/env python3
"""
/signup app views.
"""
from django.shortcuts import render
def index(request):
"""
The signup page.
"""
if request.method == "GET":
context = {}
return render(request, 'signup/index.html', context)

View File

@ -3,8 +3,9 @@
Custom Jinja2 environment. Custom Jinja2 environment.
""" """
import jinja2 import jinja2
from django.contrib.staticfiles.storage import staticfiles_storage
from django.urls import reverse from django.urls import reverse
from django.contrib import messages
from django.contrib.staticfiles.storage import staticfiles_storage
def environment(**options): def environment(**options):
""" """
@ -12,11 +13,13 @@ def environment(**options):
""" """
env = jinja2.Environment(**options) env = jinja2.Environment(**options)
env.globals.update({ env.globals.update({
'static': staticfiles_storage.url,
'url': reverse,
'undefined': jinja2.StrictUndefined, 'undefined': jinja2.StrictUndefined,
'autoescape': True, 'autoescape': True,
'trim_blocks': True, 'trim_blocks': True,
'lstrip_blocks': True, 'lstrip_blocks': True,
'url': reverse,
'static': staticfiles_storage.url,
'get_messages': messages.get_messages,
}) })
return env return env

View File

@ -31,9 +31,12 @@ INSTALLED_APPS = [
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'quest.apps.QuestConfig', 'users.apps.UsersConfig',
'homepage.apps.HomepageConfig', 'homepage.apps.HomepageConfig',
'create_quest.apps.CreateQuestConfig', 'create_quest.apps.CreateQuestConfig',
'quest.apps.QuestConfig',
'login.apps.LoginConfig',
'signup.apps.SignupConfig',
] ]
MIDDLEWARE = [ MIDDLEWARE = [
@ -58,6 +61,19 @@ TEMPLATES = [
'auto_reload': DEBUG, 'auto_reload': DEBUG,
}, },
}, },
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
] ]
WSGI_APPLICATION = 'titivillus.wsgi.application' WSGI_APPLICATION = 'titivillus.wsgi.application'
@ -87,6 +103,9 @@ AUTH_PASSWORD_VALIDATORS = [
}, },
{ {
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
'OPTIONS': {
'min_length': 8,
}
}, },
{ {
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
@ -119,3 +138,12 @@ STATIC_ROOT = '/usr/local/www/html/static/'
STATICFILES_DIRS = [ STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static'), os.path.join(BASE_DIR, 'static'),
] ]
AUTH_USER_MODEL = 'users.User'
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.Argon2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
]

View File

@ -11,4 +11,7 @@ urlpatterns = [
path('quest/', include('quest.urls')), path('quest/', include('quest.urls')),
path('create_quest/', include('create_quest.urls')), path('create_quest/', include('create_quest.urls')),
path('set_session/', include('set_session.urls')), path('set_session/', include('set_session.urls')),
path('signup/', include('signup.urls')),
path('login/', include('login.urls')),
path('logout/', include('logout.urls')),
] ]

0
users/__init__.py Normal file
View File

3
users/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

5
users/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class UsersConfig(AppConfig):
name = 'users'

View File

@ -0,0 +1,42 @@
# Generated by Django 2.0.7 on 2018-08-10 20:30
import django.contrib.auth.models
import django.contrib.auth.validators
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0009_alter_user_last_name_max_length'),
]
operations = [
migrations.CreateModel(
name='User',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 20 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=20, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
('email', models.EmailField(max_length=254, verbose_name='email address')),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
],
options={
'verbose_name': 'user',
'verbose_name_plural': 'users',
'abstract': False,
},
managers=[
('objects', django.contrib.auth.models.UserManager()),
],
),
]

View File

32
users/models.py Normal file
View File

@ -0,0 +1,32 @@
#!/usr/bin/env python3
"""
A custom user model.
"""
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.validators import UnicodeUsernameValidator
class User(AbstractUser):
"""
A custom user model.
"""
username_validator = UnicodeUsernameValidator()
username = models.CharField(
_('username'),
max_length=20,
unique=True,
help_text=_("Required. 20 characters or fewer. "
+ "Letters, digits and @/./+/-/_ only."),
validators=[username_validator],
error_messages={
'unique': _("A user with that username already exists."),
},)
first_name = None
last_name = None
email = models.EmailField(_('email address'))
def get_full_name(self):
return None
def get_short_name(self):
return None

3
users/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

3
users/views.py Normal file
View File

@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.