Fix registration.

Fix state assignment.
Fix character ownership transfer.
Disable non-staff passwords.
Fix dashboard groups panel placement.
Fix corpstats viewmodel retrieval.
This commit is contained in:
Adarnof 2017-03-24 19:50:50 -04:00
parent ab10f062f7
commit 963cecb365
33 changed files with 268 additions and 317 deletions

View File

@ -4,5 +4,5 @@ from __future__ import absolute_import, unicode_literals
# Django starts so that shared_task will use this app.
from .celeryapp import app as celery_app # noqa
__version__ = '1.15.0'
__version__ = '1.16-dev'
NAME = 'Alliance Auth v%s' % __version__

View File

@ -225,6 +225,7 @@ SITE_NAME = 'Alliance Auth'
#################
# EMAIL SETTINGS
#################
# DEFAULT_FROM_EMAIL - no-reply email address
# DOMAIN - The Alliance Auth domain (or subdomain) address, starting with http://
# EMAIL_HOST - SMTP Server URL
# EMAIL_PORT - SMTP Server PORT
@ -232,6 +233,7 @@ SITE_NAME = 'Alliance Auth'
# EMAIL_HOST_PASSWORD - Email Password
# EMAIL_USE_TLS - Set to use TLS encryption
#################
DEFAULT_FROM_EMAIL = 'no-reply@example.com'
DOMAIN = 'https://example.com'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
@ -261,10 +263,12 @@ ESI_SSO_CALLBACK_URL = ''
# LOGIN_REDIRECT_URL - default destination when logging in if no redirect specified
# LOGOUT_REDIRECT_URL - destination after logging out
# LOGIN_TOKEN_SCOPES - scopes required on new tokens when logging in. Cannot be blank.
# ACCOUNT_ACTIVATION_DAYS - number of days email verification tokens are valid for
##################
LOGIN_REDIRECT_URL = 'authentication:dashboard'
LOGOUT_REDIRECT_URL = 'authentication:dashboard'
LOGIN_TOKEN_SCOPES = ['esi-characters.read_opportunities.v1']
ACCOUNT_ACTIVATION_DAYS = 1
#####################
# Alliance Market

View File

@ -4,6 +4,7 @@ from django.conf.urls.i18n import i18n_patterns
from django.utils.translation import ugettext_lazy as _
from django.contrib import admin
import authentication.urls
from authentication import hmac_urls
import authentication.views
import services.views
import groupmanagement.views
@ -19,6 +20,7 @@ import esi.urls
import permissions_tool.urls
from alliance_auth import NAME
from alliance_auth.hooks import get_hooks
from django.views.generic.base import TemplateView
admin.site.site_header = NAME
@ -30,6 +32,8 @@ urlpatterns = [
# Authentication
url(r'', include(authentication.urls, namespace='authentication')),
url(r'^account/login/$', TemplateView.as_view(template_name='public/login.html'), name='auth_login_user'),
url(r'account/', include(hmac_urls)),
# Admin urls
url(r'^admin/', include(admin.site.urls)),

View File

@ -5,7 +5,7 @@ from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import User
from django.utils.text import slugify
from django import forms
from authentication.models import State, get_guest_state
from authentication.models import State, get_guest_state, CharacterOwnership, UserProfile
from alliance_auth.hooks import get_hooks
from services.hooks import ServicesHook
@ -97,3 +97,6 @@ class StateAdmin(admin.ModelAdmin):
if obj == get_guest_state():
return False
admin.site.register(CharacterOwnership)
admin.site.register(UserProfile)

View File

@ -45,7 +45,7 @@ class StateBackend(ModelBackend):
return self.create_user(token)
def create_user(self, token):
username = self.iterate_username(token.charater_name) # build unique username off character name
username = self.iterate_username(token.character_name) # build unique username off character name
user = User.objects.create_user(username)
user.set_unusable_password() # prevent login via password
user.is_active = False # prevent login until email set

View File

@ -0,0 +1,13 @@
from django.conf.urls import url, include
from authentication import views
urlpatterns = [
url(r'^activate/complete/$', views.activation_complete, name='registration_activation_complete'),
# The activation key can make use of any character from the
# URL-safe base64 alphabet, plus the colon as a separator.
url(r'^activate/(?P<activation_key>[-:\w]+)/$', views.ActivationView.as_view(), name='registration_activate'),
url(r'^register/$', views.RegistrationView.as_view(), name='registration_register'),
url(r'^register/complete/$', views.registration_complete, name='registration_complete'),
url(r'^register/closed/$', views.registration_closed, name='registration_disallowed'),
url(r'', include('registration.auth_urls')),
]

View File

@ -7,21 +7,25 @@ import logging
logger = logging.getLogger(__name__)
def available_states_query(character):
query = Q(member_characters__character_id=character.character_id)
query |= Q(member_corporations__corporation_id=character.corporation_id)
query |= Q(member_alliances__alliance_id=character.alliance_id)
query |= Q(public=True)
return query
class CharacterOwnershipManager(Manager):
def create_by_token(self, token):
if not EveCharacter.objects.filter(character_id=token.character_id).exists():
EveManager.create_character(token.character_id)
return self.create(character=EveCharacter.objects.get(characte_id=token.character_id), user=token.user,
return self.create(character=EveCharacter.objects.get(character_id=token.character_id), user=token.user,
owner_hash=token.character_owner_hash)
class StateQuerySet(QuerySet):
def available_to_character(self, character):
query = Q(member_characters__character_id=character.character_id)
query |= Q(member_corporations__corporation_id=character.corporation_id)
query |= Q(member_alliances__alliance_id=character.alliance_id)
query |= Q(public=True)
return self.filter(query)
return self.filter(available_states_query(character))
def available_to_user(self, user):
if user.profile.main_character:
@ -41,7 +45,7 @@ class StateManager(Manager):
return self.get_queryset().available_to_user(user)
def get_for_character(self, character):
states = self.get_queryset().available_to_character(character).order_by('priority')
states = self.get_queryset().available_to_character(character)
if states.exists():
return states[0]
else:

View File

@ -6,6 +6,7 @@ import authentication.models
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
from django.contrib.auth.hashers import make_password
def create_guest_state(apps, schema_editor):
@ -41,8 +42,8 @@ def create_member_group(apps, schema_editor):
State = apps.get_model('authentication', 'State')
member_state_name = getattr(settings, 'DEFAULT_AUTH_GROUP', 'Member')
g = Group.objects.get_or_create(name=member_state_name)[0]
try:
g = Group.objects.get(name=member_state_name)
# move permissions back
state = State.objects.get(name=member_state_name)
[g.permissions.add(p.pk) for p in state.permissions.all()]
@ -50,7 +51,7 @@ def create_member_group(apps, schema_editor):
# move users back
for profile in state.userprofile_set.all().select_related('user'):
profile.user.groups.add(g.pk)
except State.DoesNotExist:
except (Group.DoesNotExist, State.DoesNotExist):
pass
@ -82,8 +83,8 @@ def create_blue_group(apps, schema_editor):
State = apps.get_model('authentication', 'State')
blue_state_name = getattr(settings, 'DEFAULT_BLUE_GROUP', 'Blue')
g = Group.objects.get_or_create(name=blue_state_name)[0]
try:
g = Group.objects.get(name=blue_state_name)
# move permissions back
state = State.objects.get(name=blue_state_name)
[g.permissions.add(p.pk) for p in state.permissions.all()]
@ -91,7 +92,7 @@ def create_blue_group(apps, schema_editor):
# move users back
for profile in state.userprofile_set.all().select_related('user'):
profile.user.groups.add(g.pk)
except State.DoesNotExist:
except (Group.DoesNotExist, State.DoesNotExist):
pass
@ -124,17 +125,18 @@ def create_profiles(apps, schema_editor):
unique_mains = [auth['main_char_id'] for auth in
AuthServicesInfo.objects.exclude(main_char_id='').values('main_char_id').annotate(
n=models.Count('main_char_id')) if
auth['n'] == 1 and EveCharacter.objects.filter(character_id=auth['main_char_id'].exists())]
auth['n'] == 1 and EveCharacter.objects.filter(character_id=auth['main_char_id']).exists()]
auths = AuthServicesInfo.objects.filter(main_char_id__in=unique_mains).select_related('user')
for auth in auths:
# carry states and mains forward
profile = UserProfile.objects.get_or_create(user=auth.user.pk)
state = State.objects.get(name=auth.state if auth.state else 'Guest')
profile.state = state.pk
char = EveCharacter.objects.get(character_id=auth.main_char_id)
profile.main_character = char.pk
profile.save()
profile = UserProfile.objects.create(user=auth.user, state=state, main_character=char)
for auth in AuthServicesInfo.objects.exclude(main_char_id__in=unique_mains).select_related('user'):
# prepare empty profiles
state = State.objects.get(name='Guest')
UserProfile.objects.create(user=auth.user, state=state)
def recreate_authservicesinfo(apps, schema_editor):
@ -154,6 +156,15 @@ def recreate_authservicesinfo(apps, schema_editor):
AuthServicesInfo.objects.update_or_create(user=profile.user, defaults={'state': profile.state.name})
def disable_passwords(apps, schema_editor):
User = apps.get_model('auth', 'User')
for u in User.objects.exclude(is_staff=True):
# remove passwords for non-staff users to prevent password-based authentication
# set_unusable_password is unavailable in migrations because :reasons:
u.password = make_password(None)
u.save()
class Migration(migrations.Migration):
dependencies = [
('auth', '0008_alter_user_username_max_length'),
@ -187,13 +198,13 @@ class Migration(migrations.Migration):
('name', models.CharField(max_length=20, unique=True)),
('priority', models.IntegerField(unique=True)),
('public', models.BooleanField(default=False)),
('member_alliances', models.ManyToManyField(to='eveonline.EveAllianceInfo')),
('member_characters', models.ManyToManyField(to='eveonline.EveCharacter')),
('member_corporations', models.ManyToManyField(to='eveonline.EveCorporationInfo')),
('member_alliances', models.ManyToManyField(blank=True,to='eveonline.EveAllianceInfo')),
('member_characters', models.ManyToManyField(blank=True,to='eveonline.EveCharacter')),
('member_corporations', models.ManyToManyField(blank=True,to='eveonline.EveCorporationInfo')),
('permissions', models.ManyToManyField(blank=True, to='auth.Permission')),
],
options={
'ordering': ['priority'],
'ordering': ['-priority'],
},
),
migrations.CreateModel(
@ -204,7 +215,7 @@ class Migration(migrations.Migration):
models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL,
to='eveonline.EveCharacter')),
('state', models.ForeignKey(on_delete=models.SET(authentication.models.get_guest_state),
to='authentication.State')),
to='authentication.State', default=authentication.models.get_guest_state_pk)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile',
to=settings.AUTH_USER_MODEL)),
],
@ -224,4 +235,5 @@ class Migration(migrations.Migration):
migrations.DeleteModel(
name='AuthServicesInfo',
),
migrations.RunPython(disable_passwords, migrations.RunPython.noop),
]

View File

@ -4,6 +4,9 @@ from django.db import models
from django.contrib.auth.models import User, Permission
from authentication.managers import CharacterOwnershipManager, StateManager
from eveonline.models import EveCharacter, EveCorporationInfo, EveAllianceInfo
import logging
logger = logging.getLogger(__name__)
@python_2_unicode_compatible
@ -12,22 +15,35 @@ class State(models.Model):
permissions = models.ManyToManyField(Permission, blank=True)
priority = models.IntegerField(unique=True)
member_characters = models.ManyToManyField(EveCharacter)
member_corporations = models.ManyToManyField(EveCorporationInfo)
member_alliances = models.ManyToManyField(EveAllianceInfo)
member_characters = models.ManyToManyField(EveCharacter, blank=True)
member_corporations = models.ManyToManyField(EveCorporationInfo, blank=True)
member_alliances = models.ManyToManyField(EveAllianceInfo, blank=True)
public = models.BooleanField(default=False)
objects = StateManager()
class Meta:
ordering = ['priority']
ordering = ['-priority']
def __str__(self):
return self.name
def available_to_character(self, character):
return self in State.objects.available_to_character(character)
def available_to_user(self, user):
return self in State.objects.available_to_user(user)
def get_guest_state():
return State.objects.update_or_create(name='Guest', defaults={'priority': 0, 'public': True})[0]
try:
return State.objects.get(name='Guest')
except State.DoesNotExist:
return State.objects.create(name='Guest', priority=0, public=True)
def get_guest_state_pk():
return get_guest_state().pk
@python_2_unicode_compatible
@ -37,15 +53,18 @@ class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='profile', on_delete=models.CASCADE)
main_character = models.OneToOneField(EveCharacter, blank=True, null=True, on_delete=models.SET_NULL)
state = models.ForeignKey(State, on_delete=models.SET(get_guest_state))
state = models.ForeignKey(State, on_delete=models.SET(get_guest_state), default=get_guest_state_pk)
def assign_state(self, commit=True):
self.state = State.objects.get_for_user(self.user)
if commit:
self.save(update_fields=['state'])
state = State.objects.get_for_user(self.user)
if self.state != state:
self.state = state
if commit:
logger.info('Updating {} state to {}'.format(self.user, self.state))
self.save(update_fields=['state'])
def __str__(self):
return "%s Profile" % self.user
return str(self.user)
@python_2_unicode_compatible

View File

@ -1,8 +1,9 @@
from __future__ import unicode_literals
from django.db.models.signals import post_save, pre_delete
from django.db.models.signals import post_save, pre_delete, m2m_changed
from django.db.models import Q
from django.dispatch import receiver
from django.contrib.auth.models import User
from authentication.models import CharacterOwnership, UserProfile, get_guest_state
from authentication.models import CharacterOwnership, UserProfile, get_guest_state, State
from services.tasks import validate_services
from esi.models import Token
from eveonline.managers import EveManager
@ -12,12 +13,48 @@ import logging
logger = logging.getLogger(__name__)
def trigger_state_check(state):
# evaluate all current members to ensure they still have access
for profile in state.userprofile_set.all():
profile.assign_state()
# we may now be available to others with lower states
check_states = State.objects.filter(priority__lt=state.priority)
for profile in UserProfile.objects.filter(state__in=check_states):
if state.available_to_user(profile.user):
profile.state = state
profile.save(update_fields=['state'])
@receiver(m2m_changed, sender=State.member_characters.through)
def state_member_characters_changed(sender, instance, action, *args, **kwargs):
if action.startswith('post_'):
trigger_state_check(instance)
@receiver(m2m_changed, sender=State.member_corporations.through)
def state_member_corporations_changed(sender, instance, action, *args, **kwargs):
if action.startswith('post_'):
trigger_state_check(instance)
@receiver(m2m_changed, sender=State.member_alliances.through)
def state_member_alliances_changed(sender, instance, action, *args, **kwargs):
if action.startswith('post_'):
trigger_state_check(instance)
@receiver(post_save, sender=State)
def state_saved(sender, instance, *args, **kwargs):
trigger_state_check(instance)
# Is there a smarter way to intercept pre_save with a diff main_character or state?
@receiver(post_save, sender=UserProfile)
def reassess_on_profile_save(sender, instance, created, *args, **kwargs):
# catches post_save from profiles to trigger necessary service and state checks
if not created:
update_fields = kwargs.pop('update_fields', [])
update_fields = kwargs.pop('update_fields', []) or []
if 'state' not in update_fields:
instance.assign_state()
# TODO: how do we prevent running this twice on profile state change?
@ -35,14 +72,17 @@ def create_required_models(sender, instance, created, *args, **kwargs):
def record_character_ownership(sender, instance, created, *args, **kwargs):
if created:
# purge ownership records if the hash or auth user account has changed
CharacterOwnership.objects.filter(character__character_id=instance.character_id).exclude(
owner_hash=instance.owner_hash).exclude(user=instance.user).delete()
CharacterOwnership.objects.filter(character__character_id=instance.character_id).exclude(Q(
owner_hash=instance.character_owner_hash) & Q(user=instance.user)).delete()
# create character if needed
if EveCharacter.objects.filter(character_id=instance.character_id).exists() is False:
EveManager.create_character(instance.character_id)
char = EveCharacter.objects.get(character_id=instance.character_id)
CharacterOwnership.objects.update_or_create(character=char,
defaults={'owner_hash': instance.owner_hash, 'user': instance.user})
# check if we need to create ownership
if instance.user and not CharacterOwnership.objects.filter(character__character_id=instance.character_id).exists():
CharacterOwnership.objects.update_or_create(character=char,
defaults={'owner_hash': instance.character_owner_hash,
'user': instance.user})
@receiver(pre_delete, sender=CharacterOwnership)

View File

@ -49,23 +49,23 @@
</div>
</div>
</div>
</div>
<div class="col-lg-6 text-center">
<div class="panel panel-success">
<div class="panel-heading">{% trans "Groups" %}</div>
<div class="panel-body">
<div style="height: 236px;overflow:-moz-scrollbars-vertical;overflow-y:auto;">
<table class="table table-striped">
{% for group in user.groups.all %}
<tr>
<td>{{ group.name }}</td>
</tr>
{% endfor %}
</table>
<div class="col-lg-6 text-center">
<div class="panel panel-success">
<div class="panel-heading">{% trans "Groups" %}</div>
<div class="panel-body">
<div style="height: 236px;overflow:-moz-scrollbars-vertical;overflow-y:auto;">
<table class="table table-striped">
{% for group in user.groups.all %}
<tr>
<td>{{ group.name }}</td>
</tr>
{% endfor %}
</table>
</div>
</div>
</div>
</div>
</div>
<div>
</div>
<div class="clearfix"></div>
<div class="panel panel-default">
@ -81,6 +81,7 @@
{% for ownership in request.user.character_ownerships.all %}
{% with ownership.character as char %}
<tr>
<td class="text-center"><img class="ra-avatar img-circle" src="https://image.eveonline.com/Character/{{ char.character_id }}_32.jpg"></td>
<td class="text-center">{{ char.character_name }}</td>
<td class="text-center">{{ char.corporation_name }}</td>
<td class="text-center">{{ char.alliance_name }}</td>

View File

@ -1,3 +1,4 @@
{% load static %}
<html lang="en">
<head>
<meta charset="utf-8">

View File

@ -1,27 +1,10 @@
{% extends 'public/base.html' %}
{% extends 'public/middle_box.html' %}
{% load static %}
{% block title %}Login{% endblock %}
{% block content %}
<div class="col-md-4 col-md-offset-4">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.level_tag}}">{{ message }}</div>
{% endfor %}
{% endif %}
<div class="panel panel-default panel-transparent">
<div class="panel-body">
<div class="col-md-12">
<p style="text-align:center">
<a href="{% url 'auth_sso_login' %}">
<img src="{% static 'img/sso/EVE_SSO_Login_Buttons_Large_Black.png' %}" border=0>
</a>
</p>
</div>
</div>
</div>
{% include 'public/lang_select.html' %}
</div>
{% endblock %}
{% block extra_include %}
{% include 'bundles/bootstrap-js.html' %}
{% endblock %}
{% block middle_box_content %}
<p style="text-align:center">
<a href="{% url 'auth_sso_login' %}">
<img src="{% static 'img/sso/EVE_SSO_Login_Buttons_Large_Black.png' %}" border=0>
</a>
</p>
{% endblock %}

View File

@ -0,0 +1,24 @@
{% extends 'public/base.html' %}
{% load static %}
{% block title %}Login{% endblock %}
{% block content %}
<div class="col-md-4 col-md-offset-4">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.level_tag}}">{{ message }}</div>
{% endfor %}
{% endif %}
<div class="panel panel-default panel-transparent">
<div class="panel-body">
<div class="col-md-12">
{% block middle_box_content %}
{% endblock %}
</div>
</div>
{% include 'public/lang_select.html' %}
</div>
</div>
{% endblock %}
{% block extra_include %}
{% include 'bundles/bootstrap-js.html' %}
{% endblock %}

View File

@ -67,9 +67,9 @@
{% if user.is_staff %}
<li><a href="{% url 'admin:index' %}">{% trans "Admin" %}</a></li>
{% endif %}
<li><a href="{% url 'auth_logout_user' %}">{% trans "Logout" %}</a></li>
<li><a href="{% url 'auth_logout' %}">{% trans "Logout" %}</a></li>
{% else %}
<li><a href="{% url 'auth_login_user' %}">{% trans "Login" %}</a></li>
<li><a href="{% url 'authentication:login' %}">{% trans "Login" %}</a></li>
{% endif %}
</ul>
@ -85,7 +85,7 @@
</li>
<li>
<a class="{% navactive request 'auth_dashboard' %}" href="{% url 'auth_dashboard' %}">
<a class="{% navactive request 'authentication:dashboard' %}" href="{% url 'authentication:dashboard' %}">
<i class="fa fa-dashboard fa-fw grayiconecolor"></i>{% trans " Dashboard" %}
</a>
</li>
@ -95,7 +95,7 @@
</a>
</li>
<li>
<a class="{% navactive request 'auth_help' %}" href="{% url 'auth_help' %}">
<a class="{% navactive request 'authentication:help' %}" href="{% url 'authentication:help' %}">
<i class="fa fa-question fa-fw grayiconecolor"></i>{% trans " Help" %}
</a>
</li>
@ -170,9 +170,6 @@
</a>
</li>
{% menu_aux %}
<li class="text-center divider-horizontal">
<h5>{% trans "Util" %}</h5>
</li>
{% if perms.auth.jabber_broadcast or perms.auth.jabber_broadcast_all or user.is_superuser %}
<li>

View File

@ -0,0 +1,5 @@
{% extends 'public/middle_box.html' %}
{% load i18n %}
{% block middle_box_content %}
<div class="alert alert-danger">{% trans 'Invalid or expired activation link.' %}</div>
{% endblock %}

View File

@ -0,0 +1,9 @@
You're receiving this email because someone has entered this email address while registering for an account on {{ site.domain }}
If this was you, please go to the following URL to confirm your email address:
{{ url }}
This link will expire in {{ expiration_days }} day{{ plural }}.
If this was not you, it is safe to ignore this email.

View File

@ -0,0 +1 @@
Confirm your Alliance Auth account email address

View File

@ -1,24 +0,0 @@
{% extends "registered/base.html" %}
{% load bootstrap %}
{% load i18n static %}
{% block title %}{% trans 'Password Change' %}{% endblock %}
{% block content %}
<div class="col-lg-12">
<h1 class="page-header text-center">{% trans "Change Password" %}</h1>
<div class="container-fluid">
<div class="col-md-4 col-md-offset-4">
<div class="row">
<p class="text-center">
{% trans "Completed" %}
</p>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,26 +0,0 @@
{% extends "registered/base.html" %}
{% load bootstrap %}
{% load i18n static %}
{% block title %}{% trans "Password Change" %}{% endblock %}
{% block content %}
<div class="col-lg-12">
<h1 class="page-header text-center">{% trans "Change Password" %}</h1>
<div class="container-fluid">
<div class="col-md-4 col-md-offset-4">
<div class="row">
<form class="form-signin" role="form" action="" method="POST">
{% csrf_token %}
{{ form|bootstrap }}
<br/>
<button class="btn btn-lg btn-primary btn-block" type="submit">{% trans "Change Password" %}</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,44 +0,0 @@
{% load staticfiles %}
{% load i18n %}
{% load bootstrap %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title>{{ SITE_NAME }} - {% block page_title %}{% endblock page_title %}</title>
{% include 'bundles/bootstrap-css.html' %}
{% include 'bundles/fontawesome.html' %}
<style>
body {
background: url('{% static 'img/index_images/index_blank_bg.jpg' %}') no-repeat scroll;
-webkit-background-size: cover;
-moz-background-size: cover;
-o-background-size: cover;
background-size: cover;
}
.panel-transparent {
background: rgba(48, 48, 48, 0.7);
color: #ffffff;
}
.panel-body {
}
</style>
</head>
<body>
{% block content %}
{% endblock content %}
{% include 'bundles/bootstrap-js.html' %}
</body>
</html>

View File

@ -1,22 +0,0 @@
{% extends 'registration/password_reset_base.html' %}
{% load staticfiles %}
{% load i18n %}
{% load bootstrap %}
{% block page_title %}Login{% endblock %}
{% block content %}
<div class="container" style="margin-top:150px">
<div class="col-md-4 col-md-offset-4">
<div class="panel panel-default panel-transparent">
<div class="panel-body">
<h1 class="text-center">{% trans 'Password reset complete' %}</h1>
<p class="text-center">{% trans "Your password has been set." %}</p>
<a href="{{ login_url }}" class="btn btn-lg btn-success btn-block">{% trans "Login" %}</a>
</div>
</div>
</div>
</div>
{% endblock content %}

View File

@ -1,32 +0,0 @@
{% extends 'registration/password_reset_base.html' %}
{% load staticfiles %}
{% load i18n %}
{% load bootstrap %}
{% block page_title %}Login{% endblock %}
{% block content %}
<div class="container" style="margin-top:150px">
<div class="col-md-4 col-md-offset-4">
<div class="panel panel-default panel-transparent">
<div class="panel-body">
{% if validlink %}
<form class="form-signin" role="form" action="" method="POST">
{% csrf_token %}
{{ form |bootstrap }}
<div class="">
<button class="btn btn-lg btn-primary btn-block" type="submit">{% trans "Change Password" %}</button>
</div>
</form>
{% else %}
<h1>{% trans 'Password reset unsuccessful' %}</h1>
<p>{% trans "The password reset link was invalid, possibly because it has already been used. Please request a new password reset." %}</p>
{% endif %}
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,23 +0,0 @@
{% extends 'registration/password_reset_base.html' %}
{% load staticfiles %}
{% load i18n %}
{% load bootstrap %}
{% block page_title %}Login{% endblock %}
{% block content %}
<div class="container" style="margin-top:150px">
<div class="col-md-4 col-md-offset-4">
<div class="panel panel-default panel-transparent">
<div class="panel-body">
<h1 class="text-center">{% trans 'Password Reset Success' %}</h1>
<p>{% trans "We've emailed you instructions for setting your password. You should be receiving them shortly." %}</p>
<p>{% trans "If you don't receive an email, please make sure you've entered the address you registered with, and check your spam folder." %}</p>
</div>
</div>
</div>
</div>
{% endblock content %}

View File

@ -1,27 +0,0 @@
{% extends 'registration/password_reset_base.html' %}
{% load staticfiles %}
{% load i18n %}
{% load bootstrap %}
{% block page_title %}Login{% endblock %}
{% block content %}
<div class="container" style="margin-top:150px">
<div class="col-md-4 col-md-offset-4">
<div class="panel panel-default panel-transparent">
<div class="panel-body">
<form class="form-signin" role="form" action="" method="POST">
{% csrf_token %}
<h1 class="text-center">{% trans "Password Reset" %}</h1>
<p class="text-center">{% trans "Forgotten your password? Enter your email below." %}</p>
{{ form|bootstrap }}
<div class="">
<button class="btn btn-lg btn-primary btn-block" type="submit">{% trans "Reset Password" %}</button>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock content %}

View File

@ -0,0 +1,14 @@
{% extends 'public/middle_box.html' %}
{% load bootstrap %}
{% load i18n %}
{% load static %}
{% block title %}Register{% endblock %}
{% block middle_box_content %}
<form class="form-signin" role="form" action="" method="POST">
{% csrf_token %}
{{ form|bootstrap }}
<br/>
<button class="btn btn-lg btn-primary btn-block" type="submit">{% trans "Submit" %}</button>
<br/>
</form>
{% endblock %}

View File

@ -1,20 +1,15 @@
from django.conf.urls import url, include
from django.conf.urls import url
from django.contrib.auth.decorators import login_required
from django.views.generic.base import TemplateView
from authentication import views
from registration.backends.hmac import urls
# inject our custom view classes into the HMAC scheme but use their urlpatterns because :efficiency:
urls.views = views
app_name = 'authentication'
urlpatterns = [
url(r'^$', TemplateView.as_view(template_name='public/login.html'), name='login'),
url(r'^account/login/$', TemplateView.as_view(template_name='public/login.html')),
url(r'^$', login_required(TemplateView.as_view(template_name='authentication/dashboard.html')),),
url(r'^account/login/$', TemplateView.as_view(template_name='public/login.html'), name='login'),
url(r'^account/characters/main/$', views.main_character_change, name='change_main_character'),
url(r'^account/characters/add/$', views.add_character, name='add_character'),
url(r'^account/', include(urls, namespace='registration')),
url(r'^help/$', login_required(TemplateView.as_view(template_name='public/help.html')), name='help'),
url(r'^dashboard/$',
login_required(TemplateView.as_view(template_name='authentication/dashboard.html')), name='dashboard'),

View File

@ -9,6 +9,7 @@ from django.contrib import messages
from django.contrib.auth.models import User
from django.conf import settings
from django.core import signing
from django.core.urlresolvers import reverse
from esi.decorators import token_required
from registration.backends.hmac.views import RegistrationView as BaseRegistrationView, \
ActivationView as BaseActivationView, REGISTRATION_SALT
@ -23,13 +24,19 @@ logger = logging.getLogger(__name__)
def main_character_change(request, token):
logger.debug("main_character_change called by user %s for character %s" % (request.user, token.character_name))
try:
co = CharacterOwnership.objects.get(character__character_id=token.character_id)
co = CharacterOwnership.objects.get(character__character_id=token.character_id, user=request.user)
except CharacterOwnership.DoesNotExist:
co = CharacterOwnership.objects.create_by_token(token)
request.user.profile.main_character = co.character
request.user.profile.save(update_fields=['main_character'])
messages.success(request, _('Changed main character to %(char)s') % {"char": co.character})
return redirect("auth_dashboard")
if not CharacterOwnership.objects.filter(character__character_id=token.character_id).exists():
co = CharacterOwnership.objects.create_by_token(token)
else:
messages.error(request, 'Cannot change main character to %(char)s: character owned by a different account.' % ({'char': token.character_name}))
co = None
if co:
request.user.profile.main_character = co.character
request.user.profile.save(update_fields=['main_character'])
messages.success(request, _('Changed main character to %(char)s') % {"char": co.character})
logger.info('Changed user %(user)s main character to %(char)s' % ({'user': request.user, 'char': co.character}))
return redirect("authentication:dashboard")
@token_required(new=True, scopes=settings.LOGIN_TOKEN_SCOPES)
@ -38,7 +45,7 @@ def add_character(request, token):
owner_hash=token.character_owner_hash).filter(user=request.user).exists():
messages.success(request, _('Added %(name)s to your account.'% ({'name': token.character_name})))
else:
messages.error(request, _('Failed to add %(name)s to your account.' % ({'name': token.charater_name})))
messages.error(request, _('Failed to add %(name)s to your account: they already have an account.' % ({'name': token.character_name})))
return redirect('authentication:dashboard')
@ -65,12 +72,12 @@ def sso_login(request, token):
user = authenticate(token=token)
if user and user.is_active:
login(request, user)
return redirect(request.POST.get('next', request.GET.get('next', 'auth_dashboard')))
return redirect(request.POST.get('next', request.GET.get('next', 'authentication:dashboard')))
elif user and not user.email:
# Store the new user PK in the session to enable us to identify the registering user in Step 2
request.session['registration_uid'] = user.pk
# Go to Step 2
return redirect('authentication:register')
return redirect('registration_register')
else:
messages.error(request, _('Unable to authenticate as the selected character.'))
return redirect(settings.LOGIN_URL)
@ -100,6 +107,13 @@ class RegistrationView(BaseRegistrationView):
def get_activation_key(self, user):
return signing.dumps(obj=[getattr(user, User.USERNAME_FIELD), user.email], salt=REGISTRATION_SALT)
def get_email_context(self, activation_key):
context = super(RegistrationView, self).get_email_context(activation_key)
context['url'] = context['site'].domain + reverse('registration_activate', args=[activation_key])
context['plural'] = 's' if context['expiration_days'] > 1 else '',
print(context)
return context
# Step 3
class ActivationView(BaseActivationView):
@ -121,3 +135,18 @@ class ActivationView(BaseActivationView):
user.save()
return user
return False
def registration_complete(request):
messages.success(request, _('Sent confirmation email. Please follow the link to confirm your email address.'))
return redirect('authentication:login')
def activation_complete(request):
messages.success(request, _('Confirmed your email address. Please login to continue.'))
return redirect('authentication:dashboard')
def registration_closed(request):
messages.error(request, _('Registraion of new accounts it not allowed at this time.'))
return redirect('authentication:login')

View File

@ -145,7 +145,7 @@ class CorpStats(models.Model):
class ViewModel(object):
def __init__(self, corpstats, user):
self.corp = corpstats.corp
self.members = corpstats.get_member_objects(user)
self.members = corpstats.get_member_objects()
self.can_update = corpstats.can_update(user)
self.total_members = len(self.members)
self.total_users = corpstats.user_count(self.members)

View File

@ -90,7 +90,7 @@ def corpstats_view(request, corp_id=None):
members = []
if corpstats:
page = request.GET.get('page', 1)
members = get_page(corpstats.get_member_objects(request.user), page)
members = get_page(corpstats.get_member_objects(), page)
if corpstats:
context.update({

View File

@ -7,20 +7,14 @@ def auth_settings(request):
return {
'DOMAIN': settings.DOMAIN,
'MUMBLE_URL': settings.MUMBLE_URL,
'FORUM_URL': settings.FORUM_URL,
'FORUM_URL': settings.PHPBB3_URL,
'TEAMSPEAK3_PUBLIC_URL': settings.TEAMSPEAK3_PUBLIC_URL,
'DISCORD_SERVER_ID': settings.DISCORD_GUILD_ID,
'KILLBOARD_URL': settings.KILLBOARD_URL,
'DISCOURSE_URL': settings.DISCOURSE_URL,
'IPS4_URL': settings.IPS4_URL,
'SMF_URL': settings.SMF_URL,
'MARKET_URL': settings.MARKET_URL,
'EXTERNAL_MEDIA_URL': settings.EXTERNAL_MEDIA_URL,
'CURRENT_UTC_TIME': timezone.now(),
'BLUE_API_MASK': settings.BLUE_API_MASK,
'BLUE_API_ACCOUNT': settings.BLUE_API_ACCOUNT,
'MEMBER_API_MASK': settings.MEMBER_API_MASK,
'MEMBER_API_ACCOUNT': settings.MEMBER_API_ACCOUNT,
'JABBER_URL': settings.JABBER_URL,
'SITE_NAME': settings.SITE_NAME,
}

View File

@ -41,7 +41,7 @@ def only_one(function=None, key="", timeout=None):
@app.task(bind=True)
def validate_services(self, user):
logger.debug('Ensuring user %s has permissions for active services'.format(user))
logger.debug('Ensuring user {} has permissions for active services'.format(user))
# Iterate through services hooks and have them check the validity of the user
for svc in ServicesHook.get_services():
try:

View File

@ -1,12 +1,9 @@
from __future__ import unicode_literals
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
from django.utils.translation import ugettext_lazy as _
from alliance_auth.hooks import get_hooks
from eveonline.models import EveCharacter
from services.forms import FleetFormatterForm
import logging
@ -47,7 +44,7 @@ def fleet_formatter_view(request):
@login_required
def services_view(request):
logger.debug("services_view called by user %s" % request.user)
char = request.profile.main_character
char = request.user.profile.main_character
context = {'service_ctrls': []}
for fn in get_hooks('services_hook'):
# Render hooked services controls