mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2026-02-04 22:26:19 +01:00
Bulk of the profile work is done.
This commit is contained in:
@@ -5,39 +5,11 @@ 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 AuthServicesInfo, State, get_none_state
|
||||
from eveonline.models import EveCharacter
|
||||
from authentication.models import State, get_guest_state
|
||||
from alliance_auth.hooks import get_hooks
|
||||
from services.hooks import ServicesHook
|
||||
|
||||
|
||||
@admin.register(AuthServicesInfo)
|
||||
class AuthServicesInfoManager(admin.ModelAdmin):
|
||||
|
||||
@staticmethod
|
||||
def main_character(obj):
|
||||
if obj.main_char_id:
|
||||
try:
|
||||
return EveCharacter.objects.get(character_id=obj.main_char_id)
|
||||
except EveCharacter.DoesNotExist:
|
||||
pass
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def has_delete_permission(request, obj=None):
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def has_add_permission(request, obj=None):
|
||||
return False
|
||||
|
||||
search_fields = [
|
||||
'user__username',
|
||||
'main_char_id',
|
||||
]
|
||||
list_display = ('user', 'main_character')
|
||||
|
||||
|
||||
def make_service_hooks_update_groups_action(service):
|
||||
"""
|
||||
Make a admin action for the given service
|
||||
@@ -103,16 +75,16 @@ class StateForm(forms.ModelForm):
|
||||
def _is_none_state(self):
|
||||
instance = getattr(self, 'instance', None)
|
||||
if instance and instance.pk:
|
||||
return instance == get_none_state()
|
||||
return instance == get_guest_state()
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(StateForm, self).__init__(*args, **kwargs)
|
||||
if _is_none_state():
|
||||
if self._is_none_state():
|
||||
self.fields['name'].widget.attrs['readonly'] = True
|
||||
|
||||
def clean_name(self):
|
||||
if self._is_none_state():
|
||||
return instance.name
|
||||
return self.instance.name
|
||||
return self.cleaned_data['name']
|
||||
|
||||
|
||||
@@ -122,6 +94,6 @@ class StateAdmin(admin.ModelAdmin):
|
||||
|
||||
@staticmethod
|
||||
def has_delete_permission(request, obj=None):
|
||||
if obj == get_none_state():
|
||||
if obj == get_guest_state():
|
||||
return False
|
||||
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.apps import AppConfig
|
||||
from django.core.checks import register, Tags
|
||||
|
||||
|
||||
class AuthenticationConfig(AppConfig):
|
||||
name = 'authentication'
|
||||
|
||||
def ready(self):
|
||||
super(AuthenticationConfig, self).ready()
|
||||
import authentication.signals
|
||||
from authentication import checks
|
||||
register(Tags.security)(checks.check_login_scopes_setting)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from django.contrib.auth.backends import ModelBackend
|
||||
from django.contrib.auth.models import Permission
|
||||
from authentication.models import UserProfile
|
||||
from authentication.models import UserProfile, CharacterOwnership
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
|
||||
class StateBackend(ModelBackend):
|
||||
@@ -10,7 +11,7 @@ class StateBackend(ModelBackend):
|
||||
user_state_query = 'state__%s__user' % profile_state_field.related_query_name()
|
||||
return Permission.objects.filter(**{user_state_query: user_obj})
|
||||
|
||||
def get_state_permission(self, user_obj, obj=None):
|
||||
def get_state_permissions(self, user_obj, obj=None):
|
||||
return self._get_permissions(user_obj, obj, 'state')
|
||||
|
||||
def get_all_permissions(self, user_obj, obj=None):
|
||||
@@ -21,3 +22,49 @@ class StateBackend(ModelBackend):
|
||||
user_obj._perm_cache.update(self.get_group_permissions(user_obj))
|
||||
user_obj._perm_cache.update(self.get_state_permissions(user_obj))
|
||||
return user_obj._perm_cache
|
||||
|
||||
def authenticate(self, token=None):
|
||||
if not token:
|
||||
return None
|
||||
try:
|
||||
ownership = CharacterOwnership.objects.get(character__character_id=token.character_id)
|
||||
if ownership.owner_hash == token.character_owner_hash:
|
||||
return ownership.user
|
||||
else:
|
||||
ownership.delete()
|
||||
return self.create_user(token)
|
||||
except CharacterOwnership.DoesNotExist:
|
||||
try:
|
||||
# insecure legacy main check for pre-sso registration auth installs
|
||||
profile = UserProfile.objects.get(main_character__character_id=token.character_id)
|
||||
# attach an ownership
|
||||
CharacterOwnership.objects.create_by_token(token)
|
||||
return profile.user
|
||||
except UserProfile.DoesNotExist:
|
||||
pass
|
||||
return self.create_user(token)
|
||||
|
||||
def create_user(self, token):
|
||||
username = self.iterate_username(token.charater_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
|
||||
user.save()
|
||||
token.user = user
|
||||
co = CharacterOwnership.objects.create_by_token(token) # assign ownership to this user
|
||||
user.profile.main_character = co.character # assign main character as token character
|
||||
user.profile.save()
|
||||
return user
|
||||
|
||||
@staticmethod
|
||||
def iterate_username(name):
|
||||
if User.objects.filter(username__startswith=name).exists():
|
||||
u = User.objects.filter(username__startswith=name)
|
||||
num = len(u)
|
||||
username = "%s_%s" % (name, num)
|
||||
while u.filter(username=username).exists():
|
||||
num += 1
|
||||
username = "%s_%s" % (name, num)
|
||||
else:
|
||||
username = name
|
||||
return username
|
||||
|
||||
13
authentication/checks.py
Normal file
13
authentication/checks.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from __future__ import unicode_literals
|
||||
from django.core.checks import Error
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
def check_login_scopes_setting(*args, **kwargs):
|
||||
errors = []
|
||||
try:
|
||||
assert settings.LOGIN_TOKEN_SCOPES
|
||||
except (AssertionError, AttributeError):
|
||||
errors.append(Error('LOGIN_TOKEN_SCOPES setting cannot be blank.',
|
||||
hint='SSO tokens used for logging in must require scopes to be refreshable.'))
|
||||
return errors
|
||||
@@ -1,17 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
from authentication.states import NONE_STATE, BLUE_STATE, MEMBER_STATE
|
||||
from authentication.managers import UserState
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
def membership_state(request):
|
||||
return UserState.get_membership_state(request)
|
||||
|
||||
|
||||
def states(request):
|
||||
return {
|
||||
'BLUE_STATE': BLUE_STATE,
|
||||
'MEMBER_STATE': MEMBER_STATE,
|
||||
'NONE_STATE': NONE_STATE,
|
||||
'MEMBER_BLUE_STATE': [MEMBER_STATE, BLUE_STATE],
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
from django.contrib.auth.decorators import user_passes_test
|
||||
from authentication.managers import UserState
|
||||
|
||||
|
||||
def _state_required(state_test, *args, **kwargs):
|
||||
return user_passes_test(state_test, *args, **kwargs)
|
||||
|
||||
|
||||
def members(*args, **kwargs):
|
||||
return _state_required(UserState.member_state, *args, **kwargs)
|
||||
|
||||
|
||||
def blues(*args, **kwargs):
|
||||
return _state_required(UserState.blue_state, *args, **kwargs)
|
||||
|
||||
|
||||
def members_and_blues(*args, **kwargs):
|
||||
return _state_required(UserState.member_or_blue_state, *args, **kwargs)
|
||||
|
||||
|
||||
def none_state(*args, **kwargs):
|
||||
return _state_required(UserState.none_state, *args, **kwargs)
|
||||
@@ -1,42 +1,7 @@
|
||||
from __future__ import unicode_literals
|
||||
from django import forms
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.contrib.auth.models import User
|
||||
import re
|
||||
|
||||
|
||||
class LoginForm(forms.Form):
|
||||
username = forms.CharField(label=_('Username'), max_length=32, required=True)
|
||||
password = forms.CharField(label=_('Password'), widget=forms.PasswordInput())
|
||||
|
||||
|
||||
class RegistrationForm(forms.Form):
|
||||
username = forms.CharField(label=_('Username'), max_length=30, required=True)
|
||||
password = forms.CharField(label=_('Password'), widget=forms.PasswordInput(), required=True)
|
||||
password_again = forms.CharField(label=_('Password Again'), widget=forms.PasswordInput(), required=True)
|
||||
email = forms.CharField(label=_('Email'), max_length=254, required=True)
|
||||
email_again = forms.CharField(label=_('Email Again'), max_length=254, required=True)
|
||||
|
||||
def clean(self):
|
||||
if ' ' in self.cleaned_data['username']:
|
||||
raise forms.ValidationError('Username cannot contain a space')
|
||||
|
||||
# We attempt to get the user object if we succeed we know email as been used
|
||||
try:
|
||||
User.objects.get(email=self.cleaned_data['email'])
|
||||
raise forms.ValidationError('Email as already been used')
|
||||
except User.DoesNotExist:
|
||||
pass
|
||||
|
||||
if not re.match("^\w+$", self.cleaned_data['username']):
|
||||
raise forms.ValidationError('Username contains illegal characters')
|
||||
|
||||
if 'password' in self.cleaned_data and 'password_again' in self.cleaned_data:
|
||||
if self.cleaned_data['password'] != self.cleaned_data['password_again']:
|
||||
raise forms.ValidationError('Passwords do not match')
|
||||
|
||||
if 'email' in self.cleaned_data and 'email_again' in self.cleaned_data:
|
||||
if self.cleaned_data['email'] != self.cleaned_data['email_again']:
|
||||
raise forms.ValidationError('Emails do not match')
|
||||
|
||||
return self.cleaned_data
|
||||
email = forms.EmailField(label=_('Email'), max_length=254, required=True)
|
||||
|
||||
@@ -1,75 +1,57 @@
|
||||
from __future__ import unicode_literals
|
||||
from django.contrib.auth.models import User
|
||||
from django.conf import settings
|
||||
from authentication.states import NONE_STATE, BLUE_STATE, MEMBER_STATE
|
||||
from authentication.models import AuthServicesInfo
|
||||
|
||||
from django.db.models import Manager, QuerySet, Q
|
||||
from eveonline.managers import EveManager
|
||||
from eveonline.models import EveCharacter
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AuthServicesInfoManager:
|
||||
def __init__(self):
|
||||
pass
|
||||
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,
|
||||
owner_hash=token.character_owner_hash)
|
||||
|
||||
@staticmethod
|
||||
def update_main_char_id(char_id, user):
|
||||
if User.objects.filter(username=user.username).exists():
|
||||
logger.debug("Updating user %s main character to id %s" % (user, char_id))
|
||||
authserviceinfo = AuthServicesInfo.objects.get(user=user)
|
||||
authserviceinfo.main_char_id = char_id
|
||||
authserviceinfo.save(update_fields=['main_char_id'])
|
||||
logger.info("Updated user %s main character to id %s" % (user, char_id))
|
||||
|
||||
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)
|
||||
|
||||
def available_to_user(self, user):
|
||||
if user.profile.main_character:
|
||||
return self.available_to_character(user.profile.main_character)
|
||||
else:
|
||||
logger.error("Failed to update user %s main character id to %s: user does not exist." % (user, char_id))
|
||||
|
||||
@staticmethod
|
||||
def update_is_blue(is_blue, user):
|
||||
if User.objects.filter(username=user.username).exists():
|
||||
logger.debug("Updating user %s blue status: %s" % (user, is_blue))
|
||||
authserviceinfo = AuthServicesInfo.objects.get(user=user)
|
||||
authserviceinfo.is_blue = is_blue
|
||||
authserviceinfo.save(update_fields=['is_blue'])
|
||||
logger.info("Updated user %s blue status to %s in authservicesinfo model." % (user, is_blue))
|
||||
return self.none()
|
||||
|
||||
|
||||
class UserState:
|
||||
def __init__(self):
|
||||
pass
|
||||
class StateManager(Manager):
|
||||
def get_queryset(self):
|
||||
return StateQuerySet(self.model, using=self._db)
|
||||
|
||||
MEMBER_STATE = MEMBER_STATE
|
||||
BLUE_STATE = BLUE_STATE
|
||||
NONE_STATE = NONE_STATE
|
||||
def available_to_character(self, character):
|
||||
return self.get_queryset().available_to_character(character)
|
||||
|
||||
@classmethod
|
||||
def member_state(cls, user):
|
||||
return cls.state_required(user, [cls.MEMBER_STATE])
|
||||
def available_to_user(self, user):
|
||||
return self.get_queryset().available_to_user(user)
|
||||
|
||||
@classmethod
|
||||
def member_or_blue_state(cls, user):
|
||||
return cls.state_required(user, [cls.MEMBER_STATE, cls.BLUE_STATE])
|
||||
def get_for_character(self, character):
|
||||
states = self.get_queryset().available_to_character(character).order_by('priority')
|
||||
if states.exists():
|
||||
return states[0]
|
||||
else:
|
||||
from authentication.models import get_guest_state
|
||||
return get_guest_state()
|
||||
|
||||
@classmethod
|
||||
def blue_state(cls, user):
|
||||
return cls.state_required(user, [cls.BLUE_STATE])
|
||||
|
||||
@classmethod
|
||||
def none_state(cls, user):
|
||||
return cls.state_required(user, [cls.NONE_STATE])
|
||||
|
||||
@classmethod
|
||||
def get_membership_state(cls, request):
|
||||
if request.user.is_authenticated:
|
||||
auth = AuthServicesInfo.objects.get(user=request.user)
|
||||
return {'STATE': auth.state}
|
||||
return {'STATE': cls.NONE_STATE}
|
||||
|
||||
@staticmethod
|
||||
def state_required(user, states):
|
||||
if user.is_superuser and settings.SUPERUSER_STATE_BYPASS:
|
||||
return True
|
||||
if user.is_authenticated:
|
||||
auth = AuthServicesInfo.objects.get(user=user)
|
||||
return auth.state in states
|
||||
return False
|
||||
def get_for_user(self, user):
|
||||
states = self.get_queryset().available_to_user(user)
|
||||
if states.exists():
|
||||
return states[0]
|
||||
else:
|
||||
from authentication.models import get_guest_state
|
||||
return get_guest_state()
|
||||
@@ -4,52 +4,6 @@ from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
from authentication.states import MEMBER_STATE, BLUE_STATE, NONE_STATE
|
||||
from django.conf import settings
|
||||
|
||||
def determine_membership_by_character(char, apps):
|
||||
if str(char.corporation_id) in settings.STR_CORP_IDS:
|
||||
return MEMBER_STATE
|
||||
elif str(char.alliance_id) in settings.STR_ALLIANCE_IDS:
|
||||
return MEMBER_STATE
|
||||
EveCorporationInfo = apps.get_model('eveonline', 'EveCorporationInfo')
|
||||
if EveCorporationInfo.objects.filter(corporation_id=char.corporation_id).exists() is False:
|
||||
return NONE_STATE
|
||||
else:
|
||||
corp = EveCorporationInfo.objects.get(corporation_id=char.corporation_id)
|
||||
if corp.is_blue:
|
||||
return BLUE_STATE
|
||||
else:
|
||||
return NONE_STATE
|
||||
|
||||
def determine_membership_by_user(user, apps):
|
||||
AuthServicesInfo = apps.get_model('authentication', 'AuthServicesInfo')
|
||||
auth = AuthServicesInfo.objects.get(user=user)
|
||||
if auth.main_char_id:
|
||||
EveCharacter = apps.get_model('eveonline', 'EveCharacter')
|
||||
if EveCharacter.objects.filter(character_id=auth.main_char_id).exists():
|
||||
char = EveCharacter.objects.get(character_id=auth.main_char_id)
|
||||
return determine_membership_by_character(char, apps)
|
||||
else:
|
||||
return NONE_STATE
|
||||
else:
|
||||
return NONE_STATE
|
||||
|
||||
def set_state(user, apps):
|
||||
if user.is_active:
|
||||
state = determine_membership_by_user(user, apps)
|
||||
else:
|
||||
state = NONE_STATE
|
||||
AuthServicesInfo = apps.get_model('authentication', 'AuthServicesInfo')
|
||||
auth = AuthServicesInfo.objects.get(user=user)
|
||||
if auth.state != state:
|
||||
auth.state = state
|
||||
auth.save()
|
||||
|
||||
def set_initial_state(apps, schema_editor):
|
||||
User = apps.get_model('auth', 'User')
|
||||
for u in User.objects.all():
|
||||
set_state(u, apps)
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
@@ -60,5 +14,4 @@ class Migration(migrations.Migration):
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(set_initial_state, migrations.RunPython.noop)
|
||||
]
|
||||
|
||||
24
authentication/migrations/0014_fleetup_permission.py
Normal file
24
authentication/migrations/0014_fleetup_permission.py
Normal file
@@ -0,0 +1,24 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2016-09-09 23:19
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def create_permission(apps, schema_editor):
|
||||
User = apps.get_model('auth', 'User')
|
||||
ContentType = apps.get_model('contenttypes', 'ContentType')
|
||||
Permission = apps.get_model('auth', 'Permission')
|
||||
ct = ContentType.objects.get_for_model(User)
|
||||
Permission.objects.get_or_create(codename="view_fleetup", content_type=ct, name="view_fleetup")
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('authentication', '0013_service_modules'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(create_permission, migrations.RunPython.noop)
|
||||
]
|
||||
227
authentication/migrations/0015_user_profiles.py
Normal file
227
authentication/migrations/0015_user_profiles.py
Normal file
@@ -0,0 +1,227 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.5 on 2017-03-22 23:09
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import authentication.models
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
def create_guest_state(apps, schema_editor):
|
||||
State = apps.get_model('authentication', 'State')
|
||||
State.objects.update_or_create(name='Guest', defaults={'priority': 0, 'public': True})
|
||||
|
||||
|
||||
def create_member_state(apps, schema_editor):
|
||||
Group = apps.get_model('auth', 'Group')
|
||||
State = apps.get_model('authentication', 'State')
|
||||
EveAllianceInfo = apps.get_model('eveonline', 'EveAllianceInfo')
|
||||
EveCorporationInfo = apps.get_model('eveonline', 'EveCorporationInfo')
|
||||
|
||||
member_state_name = getattr(settings, 'DEFAULT_AUTH_GROUP', 'Member')
|
||||
s = State.objects.update_or_create(name=member_state_name, defaults={'priority': 100, 'public': False})[0]
|
||||
try:
|
||||
# move group permissions to state
|
||||
g = Group.objects.get(name=member_state_name)
|
||||
s.permissions.add(g.permissions.all())
|
||||
g.delete()
|
||||
except Group.DoesNotExist:
|
||||
pass
|
||||
|
||||
# auto-populate member IDs
|
||||
CORP_IDS = getattr(settings, 'CORP_IDS', [])
|
||||
ALLIANCE_IDS = getattr(settings, 'ALLIANCE_IDS', [])
|
||||
s.member_corporations.add(EveCorporationInfo.objects.filter(corporation_id__in=CORP_IDS))
|
||||
s.member_alliances.add(EveAllianceInfo.objects.filter(alliance_id__in=ALLIANCE_IDS))
|
||||
|
||||
|
||||
def create_member_group(apps, schema_editor):
|
||||
Group = apps.get_model('auth', 'Group')
|
||||
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:
|
||||
# move permissions back
|
||||
state = State.objects.get(name=member_state_name)
|
||||
g.permissions.add(state.permissions.all())
|
||||
|
||||
# move users back
|
||||
for profile in state.userprofile_set.all().select_related('user'):
|
||||
profile.user.groups.add(g)
|
||||
except State.DoesNotExist:
|
||||
pass
|
||||
|
||||
|
||||
def create_blue_state(apps, schema_editor):
|
||||
Group = apps.get_model('auth', 'Group')
|
||||
State = apps.get_model('authentication', 'State')
|
||||
EveAllianceInfo = apps.get_model('eveonline', 'EveAllianceInfo')
|
||||
EveCorporationInfo = apps.get_model('eveonline', 'EveCorporationInfo')
|
||||
blue_state_name = getattr(settings, 'DEFAULT_BLUE_GROUP', 'Blue')
|
||||
|
||||
s = State.objects.update_or_create(name=blue_state_name, defaults={'priority': 50, 'public': False})[0]
|
||||
try:
|
||||
# move group permissions to state
|
||||
g = Group.objects.get(name=blue_state_name)
|
||||
s.permissions.add(g.permissions.all())
|
||||
g.permissions.clear()
|
||||
except Group.DoesNotExist:
|
||||
pass
|
||||
|
||||
# auto-populate blue member IDs
|
||||
BLUE_CORP_IDS = getattr(settings, 'BLUE_CORP_IDS', [])
|
||||
BLUE_ALLIANCE_IDS = getattr(settings, 'BLUE_ALLIANCE_IDS', [])
|
||||
s.member_corporations.add(EveCorporationInfo.objects.filter(corporation_id__in=BLUE_CORP_IDS))
|
||||
s.member_alliances.add(EveAllianceInfo.objects.filter(alliance_id__in=BLUE_ALLIANCE_IDS))
|
||||
|
||||
|
||||
def create_blue_group(apps, schema_editor):
|
||||
Group = apps.get_model('auth', 'Group')
|
||||
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:
|
||||
# move permissions back
|
||||
state = State.objects.get(name=blue_state_name)
|
||||
g.permissions.add(state.permissions.all())
|
||||
|
||||
# move users back
|
||||
for profile in state.userprofile_set.all().select_related('user'):
|
||||
profile.user.groups.add(g)
|
||||
except State.DoesNotExist:
|
||||
pass
|
||||
|
||||
|
||||
def populate_ownerships(apps, schema_editor):
|
||||
Token = apps.get_model('esi', 'Token')
|
||||
CharacterOwnership = apps.get_model('authentication', 'CharacterOwnership')
|
||||
EveCharacter = apps.get_model('eveonline', 'EveCharacter')
|
||||
|
||||
unique_character_owners = [t['character_id'] for t in
|
||||
Token.objects.all().values('character_id').annotate(n=models.Count('user')) if
|
||||
t['n'] == 1 and EveCharacter.objects.filter(character_id=t['character_id'].exists())]
|
||||
|
||||
tokens = Token.objects.filter(character_id__in=unique_character_owners)
|
||||
for c_id in unique_character_owners:
|
||||
ts = tokens.filter(character_id=c_id).order_by('created')
|
||||
for t in ts:
|
||||
if t.can_refresh:
|
||||
# find newest refreshable token and use it as basis for CharacterOwnership
|
||||
CharacterOwnership.objecs.create_by_token(t)
|
||||
break
|
||||
|
||||
|
||||
def create_profiles(apps, schema_editor):
|
||||
AuthServicesInfo = apps.get_model('authentication', 'AuthServicesInfo')
|
||||
State = apps.get_model('authentication', 'State')
|
||||
UserProfile = apps.get_model('authentication', 'UserProfile')
|
||||
EveCharacter = apps.get_model('eveonline', 'EveCharacter')
|
||||
|
||||
# grab AuthServicesInfo if they have a unique main_char_id and the EveCharacter exists
|
||||
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())]
|
||||
|
||||
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
|
||||
char = EveCharacter.objects.get(character_id=auth.main_char_id)
|
||||
profile.main_character = char
|
||||
profile.save()
|
||||
|
||||
|
||||
def recreate_authservicesinfo(apps, schema_editor):
|
||||
AuthServicesInfo = apps.get_model('authentication', 'AuthServicesInfo')
|
||||
UserProfile = apps.get_model('authentication', 'UserProfile')
|
||||
User = apps.get_model('auth', 'User')
|
||||
|
||||
# recreate all missing AuthServicesInfo models
|
||||
AuthServicesInfo.objects.bulk_create([AuthServicesInfo(user=u.pk) for u in User.objects.all()])
|
||||
|
||||
# repopulate main characters
|
||||
for profile in UserProfile.objects.exclude(main_character__isnull=True).select_related('user', 'main_character'):
|
||||
AuthServicesInfo.objects.update_or_create(user=profile.user, defaults={'main_char_id': profile.main_character.character_id})
|
||||
|
||||
# repopulate states we understand
|
||||
for profile in UserProfile.objects.exclude(state__name='Guest').filter(state__name__in=['Member', 'Blue']).select_related('user', 'state'):
|
||||
AuthServicesInfo.objects.update_or_create(user=profile.user, defaults={'state': profile.state.name})
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('auth', '0008_alter_user_username_max_length'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('eveonline', '0008_remove_apikeys'),
|
||||
('authentication', '0014_fleetup_permission'),
|
||||
('esi', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CharacterOwnership',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('owner_hash', models.CharField(max_length=28, unique=True)),
|
||||
('character',
|
||||
models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='character_ownership',
|
||||
to='eveonline.EveCharacter')),
|
||||
('user',
|
||||
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='character_ownerships',
|
||||
to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'default_permissions': ('change', 'delete'),
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='State',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('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')),
|
||||
('permissions', models.ManyToManyField(blank=True, to='auth.Permission')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['priority'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='UserProfile',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('main_character',
|
||||
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')),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile',
|
||||
to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'default_permissions': ('change',),
|
||||
},
|
||||
),
|
||||
migrations.RunPython(create_guest_state, migrations.RunPython.noop),
|
||||
migrations.RunPython(create_member_state, create_member_group),
|
||||
migrations.RunPython(create_blue_state, create_blue_group),
|
||||
migrations.RunPython(populate_ownerships, migrations.RunPython.noop),
|
||||
migrations.RunPython(create_profiles, recreate_authservicesinfo),
|
||||
migrations.RemoveField(
|
||||
model_name='authservicesinfo',
|
||||
name='user',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='AuthServicesInfo',
|
||||
),
|
||||
]
|
||||
@@ -1,40 +1,24 @@
|
||||
from __future__ import unicode_literals
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from authentication.states import MEMBER_STATE, BLUE_STATE, NONE_STATE
|
||||
from eveonline.models import EveCharacter
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class AuthServicesInfo(models.Model):
|
||||
class Meta:
|
||||
default_permissions = ('change',)
|
||||
|
||||
STATE_CHOICES = (
|
||||
(NONE_STATE, 'None'),
|
||||
(BLUE_STATE, 'Blue'),
|
||||
(MEMBER_STATE, 'Member'),
|
||||
)
|
||||
|
||||
main_char_id = models.CharField(max_length=64, blank=True, default="")
|
||||
user = models.OneToOneField(User)
|
||||
state = models.CharField(blank=True, null=True, choices=STATE_CHOICES, default=NONE_STATE, max_length=10)
|
||||
|
||||
def __str__(self):
|
||||
return self.user.username + ' - AuthInfo'
|
||||
from django.contrib.auth.models import User, Permission
|
||||
from authentication.managers import CharacterOwnershipManager, StateManager
|
||||
from eveonline.models import EveCharacter, EveCorporationInfo, EveAllianceInfo
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class State(models.Model):
|
||||
name = models.CharField(_('name'), max_length=20, unique=True)
|
||||
permissions = models.ManyToManyField(
|
||||
Permission,
|
||||
verbose_name=_('permissions'),
|
||||
blank=True,
|
||||
)
|
||||
name = models.CharField(max_length=20, unique=True)
|
||||
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)
|
||||
public = models.BooleanField(default=False)
|
||||
|
||||
objects = StateManager()
|
||||
|
||||
class Meta:
|
||||
ordering = ['priority']
|
||||
|
||||
@@ -42,8 +26,8 @@ class State(models.Model):
|
||||
return self.name
|
||||
|
||||
|
||||
def get_none_state():
|
||||
return State.objects.get_or_create(name='None')[0]
|
||||
def get_guest_state():
|
||||
return State.objects.update_or_create(name='Guest', defaults={'priority': 0, 'public': True})[0]
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
@@ -52,7 +36,28 @@ class UserProfile(models.Model):
|
||||
default_permissions = ('change',)
|
||||
|
||||
user = models.OneToOneField(User, related_name='profile', on_delete=models.CASCADE)
|
||||
main_character = models.ForeignKey(EveCharacter, on_delete=models.CASCADE)
|
||||
state = models.ForeignKey(State, on_delete=models.SET(get_none_state))
|
||||
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))
|
||||
|
||||
def assign_state(self, commit=True):
|
||||
self.state = State.objects.get_for_user(self.user)
|
||||
if commit:
|
||||
self.save(update_fields=['state'])
|
||||
|
||||
def __str__(self):
|
||||
return "%s Profile" % self.user
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class CharacterOwnership(models.Model):
|
||||
class Meta:
|
||||
default_permissions = ('change', 'delete')
|
||||
|
||||
character = models.OneToOneField(EveCharacter, on_delete=models.CASCADE, related_name='character_ownership')
|
||||
owner_hash = models.CharField(max_length=28, unique=True)
|
||||
owner_token = models.ForeignKey(Token, on_delete=models.SET_NULL)
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='character_ownerships')
|
||||
|
||||
objects = CharacterOwnershipManager()
|
||||
|
||||
def __str__(self):
|
||||
return "%s: %s" % (self.user, self.character)
|
||||
|
||||
@@ -1,34 +1,70 @@
|
||||
from __future__ import unicode_literals
|
||||
from django.db.models.signals import pre_save, post_save
|
||||
from django.db.models.signals import post_save, pre_delete
|
||||
from django.dispatch import receiver
|
||||
from django.contrib.auth.models import User
|
||||
from authentication.models import AuthServicesInfo
|
||||
from authentication.states import MEMBER_STATE, BLUE_STATE
|
||||
from authentication.tasks import make_member, make_blue, disable_member
|
||||
from authentication.models import CharacterOwnership, UserProfile, get_guest_state
|
||||
from services.tasks import validate_services
|
||||
from esi.models import Token
|
||||
from eveonline.managers import EveManager
|
||||
from eveonline.models import EveCharacter
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@receiver(pre_save, sender=AuthServicesInfo)
|
||||
def pre_save_auth_state(sender, instance, *args, **kwargs):
|
||||
if instance.pk:
|
||||
old_instance = AuthServicesInfo.objects.get(pk=instance.pk)
|
||||
if old_instance.state != instance.state:
|
||||
logger.debug('Detected state change for %s' % instance.user)
|
||||
if instance.state == MEMBER_STATE:
|
||||
make_member(instance)
|
||||
elif instance.state == BLUE_STATE:
|
||||
make_blue(instance)
|
||||
else:
|
||||
disable_member(instance.user)
|
||||
validate_services.apply(args=(instance.user,))
|
||||
# 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', [])
|
||||
if 'state' not in update_fields:
|
||||
instance.assign_state()
|
||||
# TODO: how do we prevent running this twice on profile state change?
|
||||
validate_services(instance.user)
|
||||
|
||||
|
||||
@receiver(post_save, sender=User)
|
||||
def post_save_user(sender, instance, created, *args, **kwargs):
|
||||
def create_required_models(sender, instance, created, *args, **kwargs):
|
||||
# ensure all users have a model
|
||||
if created:
|
||||
AuthServicesInfo.objects.get_or_create(user=instance)
|
||||
UserProfile.objects.get_or_create(user=instance)
|
||||
|
||||
|
||||
@receiver(post_save, sender=Token)
|
||||
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()
|
||||
# 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})
|
||||
|
||||
|
||||
@receiver(pre_delete, sender=CharacterOwnership)
|
||||
def validate_main_character(sender, instance, *args, **kwargs):
|
||||
if instance.user.profile.main_character == instance.character:
|
||||
# clear main character as user no longer owns them
|
||||
instance.user.profile.main_character = None
|
||||
instance.user.profile.save()
|
||||
|
||||
|
||||
@receiver(pre_delete, sender=Token)
|
||||
def validate_main_character_token(sender, instance, *args, **kwargs):
|
||||
if UserProfile.objects.filter(main_character__character_id=instance.character_id):
|
||||
if not Token.objects.filter(character_id=instance.character_id).filter(user=instance.user).exists():
|
||||
# clear main character as we can no longer verify ownership
|
||||
instance.user.profile.main_character = None
|
||||
instance.user.profile.save()
|
||||
|
||||
|
||||
@receiver(post_save, sender=User)
|
||||
def assign_state_on_reactivate(sender, instance, *args, **kwargs):
|
||||
# There's no easy way to trigger an action upon saving from pre_save signal
|
||||
# If we're saving a user and that user is in the Guest state, assume is_active was just set to True and assign state
|
||||
if instance.is_active and instance.profile.state == get_guest_state():
|
||||
instance.profile.assign_state()
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
MEMBER_STATE = 'Member'
|
||||
BLUE_STATE = 'Blue'
|
||||
NONE_STATE = None
|
||||
@@ -1,189 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
from services.tasks import validate_services
|
||||
from django.contrib.auth.models import Group
|
||||
from authentication.models import AuthServicesInfo
|
||||
from authentication.states import MEMBER_STATE, BLUE_STATE, NONE_STATE
|
||||
from eveonline.models import EveCharacter, EveCorporationInfo
|
||||
from notifications import notify
|
||||
from django.conf import settings
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def generate_corp_group_name(corpname):
|
||||
return 'Corp_' + corpname.replace(' ', '_')
|
||||
|
||||
|
||||
def generate_alliance_group_name(alliancename):
|
||||
return 'Alliance_' + alliancename.replace(' ', '_')
|
||||
|
||||
|
||||
def disable_member(user):
|
||||
"""
|
||||
Disable a member who is transitioning to a NONE state.
|
||||
:param user: django.contrib.auth.models.User to disable
|
||||
:return:
|
||||
"""
|
||||
logger.debug("Disabling member %s" % user)
|
||||
if user.user_permissions.all().exists():
|
||||
logger.info("Clearning user %s permission to deactivate user." % user)
|
||||
user.user_permissions.clear()
|
||||
if user.groups.all().exists():
|
||||
logger.info("Clearing all non-public user %s groups to disable member." % user)
|
||||
user.groups.remove(*user.groups.filter(authgroup__public=False))
|
||||
validate_services.apply(args=(user,))
|
||||
|
||||
|
||||
def disable_user(user):
|
||||
"""
|
||||
Disable a user who is being set inactive or deleted
|
||||
:param user: django.contrib.auth.models.User to disable
|
||||
:return:
|
||||
"""
|
||||
logger.debug("Disabling user %s" % user)
|
||||
if user.user_permissions.all().exists():
|
||||
logger.info("Clearning user %s permission to deactivate user." % user)
|
||||
user.user_permissions.clear()
|
||||
if user.groups.all().exists():
|
||||
logger.info("Clearing user %s groups to deactivate user." % user)
|
||||
user.groups.clear()
|
||||
validate_services.apply(args=(user,))
|
||||
|
||||
|
||||
def make_member(auth):
|
||||
logger.debug("Ensuring user %s has member permissions and groups." % auth.user)
|
||||
# ensure member is not blue right now
|
||||
blue_group, c = Group.objects.get_or_create(name=settings.DEFAULT_BLUE_GROUP)
|
||||
if blue_group in auth.user.groups.all():
|
||||
logger.info("Removing user %s blue group" % auth.user)
|
||||
auth.user.groups.remove(blue_group)
|
||||
# make member
|
||||
member_group, c = Group.objects.get_or_create(name=settings.DEFAULT_AUTH_GROUP)
|
||||
if member_group not in auth.user.groups.all():
|
||||
logger.info("Adding user %s to member group" % auth.user)
|
||||
auth.user.groups.add(member_group)
|
||||
assign_corp_group(auth)
|
||||
assign_alliance_group(auth)
|
||||
|
||||
|
||||
def make_blue(auth):
|
||||
logger.debug("Ensuring user %s has blue permissions and groups." % auth.user)
|
||||
# ensure user is not a member
|
||||
member_group, c = Group.objects.get_or_create(name=settings.DEFAULT_AUTH_GROUP)
|
||||
if member_group in auth.user.groups.all():
|
||||
logger.info("Removing user %s member group" % auth.user)
|
||||
auth.user.groups.remove(member_group)
|
||||
# make blue
|
||||
blue_group, c = Group.objects.get_or_create(name=settings.DEFAULT_BLUE_GROUP)
|
||||
if blue_group not in auth.user.groups.all():
|
||||
logger.info("Adding user %s to blue group" % auth.user)
|
||||
auth.user.groups.add(blue_group)
|
||||
assign_corp_group(auth)
|
||||
assign_alliance_group(auth)
|
||||
|
||||
|
||||
def determine_membership_by_character(char):
|
||||
if str(char.corporation_id) in settings.STR_CORP_IDS:
|
||||
logger.debug("Character %s in member corp id %s" % (char, char.corporation_id))
|
||||
return MEMBER_STATE
|
||||
elif str(char.alliance_id) in settings.STR_ALLIANCE_IDS:
|
||||
logger.debug("Character %s in member alliance id %s" % (char, char.alliance_id))
|
||||
return MEMBER_STATE
|
||||
elif not EveCorporationInfo.objects.filter(corporation_id=char.corporation_id).exists():
|
||||
logger.debug("No corp model for character %s corp id %s. Unable to check standings. Non-member." % (
|
||||
char, char.corporation_id))
|
||||
return NONE_STATE
|
||||
else:
|
||||
corp = EveCorporationInfo.objects.get(corporation_id=char.corporation_id)
|
||||
if corp.is_blue:
|
||||
logger.debug("Character %s member of blue corp %s" % (char, corp))
|
||||
return BLUE_STATE
|
||||
else:
|
||||
logger.debug("Character %s member of non-blue corp %s. Non-member." % (char, corp))
|
||||
return NONE_STATE
|
||||
|
||||
|
||||
def determine_membership_by_user(user):
|
||||
logger.debug("Determining membership of user %s" % user)
|
||||
auth = AuthServicesInfo.objects.get(user=user)
|
||||
if auth.main_char_id:
|
||||
if EveCharacter.objects.filter(character_id=auth.main_char_id).exists():
|
||||
char = EveCharacter.objects.get(character_id=auth.main_char_id)
|
||||
return determine_membership_by_character(char)
|
||||
else:
|
||||
logger.debug("Character model matching user %s main character id %s does not exist. Non-member." % (
|
||||
user, auth.main_char_id))
|
||||
return NONE_STATE
|
||||
else:
|
||||
logger.debug("User %s has no main character set. Non-member." % user)
|
||||
return NONE_STATE
|
||||
|
||||
|
||||
def set_state(user):
|
||||
if user.is_active:
|
||||
state = determine_membership_by_user(user)
|
||||
else:
|
||||
state = NONE_STATE
|
||||
logger.debug("Assigning user %s to state %s" % (user, state))
|
||||
auth = AuthServicesInfo.objects.get(user=user)
|
||||
if auth.state != state:
|
||||
auth.state = state
|
||||
auth.save()
|
||||
notify(user, "Membership State Change", message="You membership state has been changed to %s" % state)
|
||||
assign_corp_group(auth)
|
||||
assign_alliance_group(auth)
|
||||
|
||||
|
||||
def assign_corp_group(auth):
|
||||
corp_group = None
|
||||
if auth.main_char_id:
|
||||
if EveCharacter.objects.filter(character_id=auth.main_char_id).exists():
|
||||
char = EveCharacter.objects.get(character_id=auth.main_char_id)
|
||||
corpname = generate_corp_group_name(char.corporation_name)
|
||||
if auth.state == BLUE_STATE and settings.BLUE_CORP_GROUPS:
|
||||
logger.debug("Validating blue user %s has corp group assigned." % auth.user)
|
||||
corp_group, c = Group.objects.get_or_create(name=corpname)
|
||||
elif auth.state == MEMBER_STATE and settings.MEMBER_CORP_GROUPS:
|
||||
logger.debug("Validating member %s has corp group assigned." % auth.user)
|
||||
corp_group, c = Group.objects.get_or_create(name=corpname)
|
||||
else:
|
||||
logger.debug("Ensuring %s has no corp groups assigned." % auth.user)
|
||||
if corp_group:
|
||||
if corp_group not in auth.user.groups.all():
|
||||
logger.info("Adding user %s to corp group %s" % (auth.user, corp_group))
|
||||
auth.user.groups.add(corp_group)
|
||||
for g in auth.user.groups.all():
|
||||
if str.startswith(str(g.name), "Corp_"):
|
||||
if g != corp_group:
|
||||
logger.info("Removing user %s from old corpgroup %s" % (auth.user, g))
|
||||
auth.user.groups.remove(g)
|
||||
|
||||
|
||||
def assign_alliance_group(auth):
|
||||
alliance_group = None
|
||||
if auth.main_char_id:
|
||||
if EveCharacter.objects.filter(character_id=auth.main_char_id).exists():
|
||||
char = EveCharacter.objects.get(character_id=auth.main_char_id)
|
||||
if char.alliance_name:
|
||||
alliancename = generate_alliance_group_name(char.alliance_name)
|
||||
if auth.state == BLUE_STATE and settings.BLUE_ALLIANCE_GROUPS:
|
||||
logger.debug("Validating blue user %s has alliance group assigned." % auth.user)
|
||||
alliance_group, c = Group.objects.get_or_create(name=alliancename)
|
||||
elif auth.state == MEMBER_STATE and settings.MEMBER_ALLIANCE_GROUPS:
|
||||
logger.debug("Validating member %s has alliance group assigned." % auth.user)
|
||||
alliance_group, c = Group.objects.get_or_create(name=alliancename)
|
||||
else:
|
||||
logger.debug("Ensuring %s has no alliance groups assigned." % auth.user)
|
||||
else:
|
||||
logger.debug("User %s main character %s not in an alliance. Ensuring no alliance group assigned." % (
|
||||
auth.user, char))
|
||||
if alliance_group:
|
||||
if alliance_group not in auth.user.groups.all():
|
||||
logger.info("Adding user %s to alliance group %s" % (auth.user, alliance_group))
|
||||
auth.user.groups.add(alliance_group)
|
||||
for g in auth.user.groups.all():
|
||||
if str.startswith(str(g.name), "Alliance_"):
|
||||
if g != alliance_group:
|
||||
logger.info("Removing user %s from old alliance group %s" % (auth.user, g))
|
||||
auth.user.groups.remove(g)
|
||||
94
authentication/templates/authentication/dashboard.html
Normal file
94
authentication/templates/authentication/dashboard.html
Normal file
@@ -0,0 +1,94 @@
|
||||
{% extends "registered/base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans "Dashboard" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-lg-12">
|
||||
<h1 class="page-header text-center">{% trans "Dashboard" %}</h1>
|
||||
<div class="col-lg-12 container">
|
||||
<div class="row">
|
||||
<div class="col-lg-6 text-center">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">{% trans "Main Character" %}</div>
|
||||
<div class="panel-body">
|
||||
{% if request.user.profile.main_character %}
|
||||
{% with request.user.profile.main_character as main %}
|
||||
<div class="col-lg-4 col-sm-2">
|
||||
<table class="table">
|
||||
<tr><td class="text-center"><img class="ra-avatar" src="https://image.eveonline.com/Character/{{ main.character_id }}_128.jpg"></td></tr>
|
||||
<tr><td class="text-center">{{ main.character_name }}</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-lg-4 col-sm-2">
|
||||
<table class="table">
|
||||
<tr><td class="text-center"><img class="ra-avatar" src="https://image.eveonline.com/Corporation/{{ main.corporation_id }}_128.png"></td></tr>
|
||||
<tr><td class="text-center">{{ main.corporation_name }}</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-lg-4 col-sm-2">
|
||||
{% if main.alliance_id %}
|
||||
<table class="table">
|
||||
<tr><td class="text-center"><img class="ra-avatar" src="https://image.eveonline.com/Alliance/{{ main.alliance_id }}_128.png"></td></tr>
|
||||
<tr><td class="text-center">{{ main.alliance_name }}</td><tr>
|
||||
</table>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
<div class="alert alert-danger" role="alert">{% trans "Missing main character model." %}</div>
|
||||
{% endif %}
|
||||
<div class="clearfix"></div>
|
||||
<div class="col-xs-6">
|
||||
<a href="{% url 'authentication:add_character' %}" class="btn btn-block btn-info" title="Add Character">{% trans 'Add Character' %}</a>
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<a href="{% url 'authentication:change_main_character' %}" class="btn btn-block btn-info" title="Change Main Character">{% trans "Change Main" %}</a>
|
||||
</div>
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" style="display:flex;">{% trans 'Characters' %}</div>
|
||||
<div class="panel-body">
|
||||
<table class="table table-hover">
|
||||
<tr>
|
||||
<th class="text-center"></th>
|
||||
<th class="text-center">{% trans 'Name' %}</th>
|
||||
<th class="text-center">{% trans 'Corp' %}</th>
|
||||
<th class="text-center">{% trans 'Alliance' %}</th>
|
||||
</tr>
|
||||
{% for ownership in request.user.character_ownerships.all %}
|
||||
{% with ownership.character as char %}
|
||||
<tr>
|
||||
<td class="text-center">{{ char.character_name }}</td>
|
||||
<td class="text-center">{{ char.corporation_name }}</td>
|
||||
<td class="text-center">{{ char.alliance_name }}</td>
|
||||
</tr>
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
49
authentication/templates/public/base.html
Normal file
49
authentication/templates/public/base.html
Normal file
@@ -0,0 +1,49 @@
|
||||
<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>{% block title %}{{ SITE_NAME }}{% endblock %}</title>
|
||||
|
||||
{% include 'bundles/bootstrap-css.html' %}
|
||||
{% include 'bundles/fontawesome.html' %}
|
||||
{% block extra_include %}
|
||||
{% endblock %}
|
||||
|
||||
<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 {
|
||||
|
||||
}
|
||||
|
||||
#lang_select {
|
||||
width: 40%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
{% block extra_style %}
|
||||
{% endblock %}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container" style="margin-top:150px">
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
15
authentication/templates/public/lang_select.html
Normal file
15
authentication/templates/public/lang_select.html
Normal file
@@ -0,0 +1,15 @@
|
||||
{% load i18n %}
|
||||
<div class="dropdown">
|
||||
<form action="{% url 'set_language' %}" method="post">
|
||||
{% csrf_token %}
|
||||
<input name="next" type="hidden" value="{{ request.get_full_path|slice:'3:' }}" />
|
||||
<select onchange="this.form.submit()" class="form-control" id="lang_select" name="language">
|
||||
{% get_language_info_list for LANGUAGES as languages %}
|
||||
{% for language in languages %}
|
||||
<option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected="selected"{% endif %}>
|
||||
{{ language.name_local }} ({{ language.code }})
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</form>
|
||||
</div>
|
||||
27
authentication/templates/public/login.html
Normal file
27
authentication/templates/public/login.html
Normal file
@@ -0,0 +1,27 @@
|
||||
{% extends 'public/base.html' %}
|
||||
{% 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 %}
|
||||
|
||||
24
authentication/templates/public/register.html
Normal file
24
authentication/templates/public/register.html
Normal file
@@ -0,0 +1,24 @@
|
||||
{% load staticfiles %}
|
||||
{% load bootstrap %}
|
||||
{% load i18n %}
|
||||
{% extends 'public/base.html' %}
|
||||
{% block title %}Registration{% endblock %}
|
||||
{% block extra_include %}
|
||||
{% include 'bundles/bootstrap-css.html' %}
|
||||
{% include 'bundles/fontawesome.html' %}
|
||||
{% include 'bundles/bootstrap-js.html' %}
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="col-md-4 col-md-offset-4">
|
||||
<div class="panel panel-default panel-transparent">
|
||||
<div class="panel-body">
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
{{ form|bootstrap }}
|
||||
<button class="btn btn-lg btn-primary btn-block" type="submit">{% trans "Register" %}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% include 'public/lang_select.html' %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
215
authentication/templates/registered/base.html
Normal file
215
authentication/templates/registered/base.html
Normal file
@@ -0,0 +1,215 @@
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load navactive %}
|
||||
{% load menu_items %}
|
||||
<!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>{% block title %}{% block page_title %}{% endblock page_title %} - Alliance Auth{% endblock title %}</title>
|
||||
|
||||
{% include 'bundles/bootstrap-css.html' %}
|
||||
{% include 'bundles/fontawesome.html' %}
|
||||
|
||||
<link href="{% static 'css/auth-base.css' %}" type="text/css" rel="stylesheet">
|
||||
{% block extra_css %}{% endblock extra_css %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{% if user.is_authenticated %}
|
||||
<div id="wrapper">
|
||||
<!-- Navigation -->
|
||||
|
||||
<nav class="navbar navbar-inverse navbar-static-top auth-navbar-top" role="navigation">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand">
|
||||
{{ SITE_NAME }}
|
||||
</a>
|
||||
<!-- /.navbar-header -->
|
||||
|
||||
<ul class="nav navbar-top-links navbar-right">
|
||||
<li class="nav-link">
|
||||
<form id="f_lang_select" action="{% url 'set_language' %}" method="post">
|
||||
{% csrf_token %}
|
||||
<input name="next" type="hidden" value="{{ request.get_full_path|slice:'3:' }}" />
|
||||
<select onchange="this.form.submit()" class="form-control" id="lang_select" name="language">
|
||||
{% get_language_info_list for LANGUAGES as languages %}
|
||||
{% for language in languages %}
|
||||
<option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected="selected"{% endif %}>
|
||||
{{ language.name_local }} ({{ language.code }})
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</form>
|
||||
</li>
|
||||
{% if notifications %}
|
||||
<li class="nav-link active">
|
||||
<a href="{% url 'auth_notification_list' %}">
|
||||
<span class="fa-stack">
|
||||
<i class="fa fa-bell fa-stack-2x"></i>
|
||||
<i class="fa fa-inverse fa-stack-1x">{{ notifications }}</i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="nav-link"><a href="{% url 'auth_notification_list' %}">
|
||||
<i class="fa fa-bell-o"></i></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if user.is_authenticated %}
|
||||
{% 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>
|
||||
{% else %}
|
||||
<li><a href="{% url 'auth_login_user' %}">{% trans "Login" %}</a></li>
|
||||
{% endif %}
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- /.navbar-top-links -->
|
||||
|
||||
<div class="navbar-default sidebar auth-sidebar" role="navigation">
|
||||
<div class="sidebar-nav navbar-collapse">
|
||||
<ul class="nav" id="side-menu">
|
||||
<li class="text-center divider-horizontal">
|
||||
<h5>{% trans "Main Navigation" %}</h5>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="{% navactive request 'auth_dashboard' %}" href="{% url 'auth_dashboard' %}">
|
||||
<i class="fa fa-dashboard fa-fw grayiconecolor"></i>{% trans " Dashboard" %}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="{% navactive request 'auth_groups' %}" href="{% url 'auth_groups' %}">
|
||||
<i class="fa fa-cogs fa-fw fa-sitemap grayiconecolor"></i>{% trans " Groups" %}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="{% navactive request 'auth_help' %}" href="{% url 'auth_help' %}">
|
||||
<i class="fa fa-question fa-fw grayiconecolor"></i>{% trans " Help" %}
|
||||
</a>
|
||||
</li>
|
||||
{% menu_main %}
|
||||
|
||||
<li class="text-center divider-horizontal">
|
||||
<h5>{% trans "Aux Navigation" %}</h5>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="{% navactive request 'auth_services' %}" href="{% url 'auth_services' %}">
|
||||
<i class="fa fa-cogs fa-fw grayiconecolor"></i>{% trans " Services" %}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="{% navactive request 'auth_hrapplications_view auth_hrapplication_create_view auth_hrapplication_personal_view auth_hrapplication_search auth_hrapplication_view' %}"
|
||||
href="{% url 'auth_hrapplications_view' %}">
|
||||
<i class="fa fa-file-o fa-fw grayiconecolor"></i>{% trans " Applications" %}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
{% if perms.corputils.view_corp_corpstats or perms.corputils.view_alliance_corpstats or perms.corputils.view_blue_corpstats %}
|
||||
<li>
|
||||
<a class="{% navactive request 'corputils:view corputils:search' %}" href="{% url 'corputils:view' %}">
|
||||
<i class="fa fa-share-alt fa-fw grayiconecolor"></i>{% trans " Corporation Stats" %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% if can_manage_groups %}
|
||||
<li>
|
||||
<a class="{% navactive request 'auth_group_management auth_group_membership auth_group_membership_list' %}" href="{% url 'auth_group_management' %}">
|
||||
<i class="fa fa-lock fa-sitemap fa-fw grayiconecolor"></i>{% trans " Group Management" %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% if perms.auth.view_fleetup %}
|
||||
<li>
|
||||
<a class="{% navactive request 'auth_fleetup_view auth_fleetup_fittings auth_fleetup_fitting auth_fleetup_doctrines auth_fleetup_doctrine auth_fleetup_characters' %}" href="{% url 'auth_fleetup_view' %}">
|
||||
<i class="fa fa-clock-o fa-fw grayiconecolor"></i> Fleet-Up
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% if perms.auth.optimer_view %}
|
||||
<li>
|
||||
<a class="{% navactive request 'auth_optimer_view auth_add_optimer_view auth_edit_optimer' %}" href="{% url 'auth_optimer_view' %}">
|
||||
<i class="fa fa-exclamation fa-fw grayiconecolor"></i>{% trans " Fleet Operations" %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% if perms.auth.timer_view %}
|
||||
<li>
|
||||
<a class="{% navactive request 'auth_timer_view auth_add_timer_view auth_edit_timer' %}" href="{% url 'auth_timer_view' %}">
|
||||
<i class="fa fa-clock-o fa-fw grayiconecolor"></i>{% trans " Structure Timers" %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
<li>
|
||||
<a class="{% navactive request 'auth_fatlink_view auth_fatlink_view_statistics auth_fatlink_view_statistics_month auth_fatlink_view_personal_statistics auth_fatlink_view_personal_statistics_year auth_fatlink_view_personal_statistics_month auth_fatlink_view_user_statistics_month auth_create_fatlink_view auth_modify_fatlink_view auth_click_fatlink_view' %}" href="{% url 'auth_fatlink_view' %}">
|
||||
<i class="fa fa-users fa-lightbulb-o fa-fw grayiconecolor"></i>{% trans " Fleet Activity Tracking" %}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="{% navactive request 'auth_srp_management_view auth_srp_management_all_view auth_srp_fleet_view auth_srp_fleet_add_view auth_srp_fleet_edit_view auth_srp_request_view auth_srp_request_update_amount_view' %}" href="{% url 'auth_srp_management_view' %}">
|
||||
<i class="fa fa-money fa-fw grayiconecolor"></i>{% trans " Ship Replacement" %}
|
||||
</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>
|
||||
<a class="{% navactive request 'auth_fleet_format_tool_view' %}" href="{% url 'auth_fleet_format_tool_view' %}">
|
||||
<i class="fa fa-space-shuttle fa-fw grayiconecolor"></i>{% trans " Fleet Broadcast Formatter" %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% menu_util %}
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
<!-- /.sidebar-collapse -->
|
||||
</div>
|
||||
<!-- /.navbar-static-side -->
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper">
|
||||
<div class="container">
|
||||
{% if messages %}
|
||||
<br>
|
||||
{% for message in messages %}
|
||||
<div class="alert alert-{{ message.level_tag}}">{{ message }}</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% block content %}
|
||||
{% endblock content %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% include 'bundles/bootstrap-js.html' %}
|
||||
{% block extra_javascript %}
|
||||
{% endblock extra_javascript %}
|
||||
<script>
|
||||
{% block extra_script %}
|
||||
{% endblock extra_script %}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,24 @@
|
||||
{% 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 %}
|
||||
@@ -0,0 +1,26 @@
|
||||
{% 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 %}
|
||||
@@ -0,0 +1,44 @@
|
||||
{% 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>
|
||||
@@ -0,0 +1,22 @@
|
||||
{% 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 %}
|
||||
@@ -0,0 +1,32 @@
|
||||
{% 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 %}
|
||||
@@ -0,0 +1,23 @@
|
||||
{% 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 %}
|
||||
@@ -0,0 +1,15 @@
|
||||
{% load i18n %}{% autoescape off %}
|
||||
{% blocktrans %}You're receiving this email because you requested a password reset for your
|
||||
user account.{% endblocktrans %}
|
||||
|
||||
{% trans "Please go to the following page and choose a new password:" %}
|
||||
{% block reset_link %}
|
||||
{{domain}}{% url 'password_reset_confirm' uidb64=uid token=token %}
|
||||
{% endblock %}
|
||||
{% trans "Your username, in case you've forgotten:" %} {{ user.get_username }}
|
||||
|
||||
{% trans "Thanks for using our site!" %}
|
||||
|
||||
{% blocktrans %}Your IT Team{% endblocktrans %}
|
||||
|
||||
{% endautoescape %}
|
||||
@@ -0,0 +1,27 @@
|
||||
{% 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 %}
|
||||
@@ -1,84 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
try:
|
||||
# Py3
|
||||
from unittest import mock
|
||||
except ImportError:
|
||||
# Py2
|
||||
import mock
|
||||
|
||||
from django.test import TestCase
|
||||
from django.contrib.auth.models import Group, Permission
|
||||
|
||||
from alliance_auth.tests.auth_utils import AuthUtils
|
||||
|
||||
from authentication.tasks import disable_member, disable_user
|
||||
|
||||
|
||||
class AuthenticationTasksTestCase(TestCase):
|
||||
def setUp(self):
|
||||
self.member = AuthUtils.create_member('auth_member')
|
||||
self.none_user = AuthUtils.create_user('none_user', disconnect_signals=True)
|
||||
|
||||
@mock.patch('services.signals.transaction')
|
||||
def test_disable_member(self, transaction):
|
||||
# Inert signals action
|
||||
transaction.on_commit.side_effect = lambda fn: fn()
|
||||
|
||||
# Add permission
|
||||
perm = Permission.objects.create(codename='test_perm', name='test perm', content_type_id=1)
|
||||
|
||||
# Add public group
|
||||
pub_group = Group.objects.create(name="A Public group")
|
||||
pub_group.authgroup.internal = False
|
||||
pub_group.authgroup.public = True
|
||||
pub_group.save()
|
||||
|
||||
# Setup member
|
||||
self.member.user_permissions.add(perm)
|
||||
self.member.groups.add(pub_group)
|
||||
|
||||
# Pre assertion
|
||||
self.assertIn(pub_group, self.member.groups.all())
|
||||
self.assertGreater(len(self.member.groups.all()), 1)
|
||||
|
||||
# Act
|
||||
disable_member(self.member)
|
||||
|
||||
# Assert
|
||||
self.assertIn(pub_group, self.member.groups.all())
|
||||
# Everything but the single public group wiped
|
||||
self.assertEqual(len(self.member.groups.all()), 1)
|
||||
# All permissions wiped
|
||||
self.assertEqual(len(self.member.user_permissions.all()), 0)
|
||||
|
||||
@mock.patch('services.signals.transaction')
|
||||
def test_disable_user(self, transaction):
|
||||
# Inert signals action
|
||||
transaction.on_commit.side_effect = lambda fn: fn()
|
||||
|
||||
# Add permission
|
||||
perm = Permission.objects.create(codename='test_perm', name='test perm', content_type_id=1)
|
||||
|
||||
# Add public group
|
||||
pub_group = Group.objects.create(name="A Public group")
|
||||
pub_group.authgroup.internal = False
|
||||
pub_group.authgroup.public = True
|
||||
pub_group.save()
|
||||
|
||||
# Setup member
|
||||
self.member.user_permissions.add(perm)
|
||||
self.member.groups.add(pub_group)
|
||||
|
||||
# Pre assertion
|
||||
self.assertIn(pub_group, self.member.groups.all())
|
||||
self.assertGreater(len(self.member.groups.all()), 1)
|
||||
|
||||
# Act
|
||||
disable_user(self.member)
|
||||
|
||||
# Assert
|
||||
# All groups wiped
|
||||
self.assertEqual(len(self.member.groups.all()), 0)
|
||||
# All permissions wiped
|
||||
self.assertEqual(len(self.member.user_permissions.all()), 0)
|
||||
21
authentication/urls.py
Normal file
21
authentication/urls.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from django.conf.urls import url, include
|
||||
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'^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'),
|
||||
]
|
||||
@@ -1,116 +1,123 @@
|
||||
from __future__ import unicode_literals
|
||||
from django.contrib.auth import login
|
||||
from django.contrib.auth import logout
|
||||
from django.contrib.auth import authenticate
|
||||
from django.shortcuts import render, redirect
|
||||
from django.contrib.auth import login, authenticate
|
||||
from django.shortcuts import redirect
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from eveonline.managers import EveManager
|
||||
from eveonline.models import EveCharacter
|
||||
from authentication.models import AuthServicesInfo
|
||||
from authentication.forms import LoginForm, RegistrationForm
|
||||
from django.contrib.auth.models import User
|
||||
from authentication.forms import RegistrationForm
|
||||
from authentication.models import CharacterOwnership
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.models import User
|
||||
from django.conf import settings
|
||||
from django.core import signing
|
||||
from esi.decorators import token_required
|
||||
from registration.backends.hmac.views import RegistrationView as BaseRegistrationView, \
|
||||
ActivationView as BaseActivationView, REGISTRATION_SALT
|
||||
from registration.signals import user_registered
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def login_user(request):
|
||||
logger.debug("login_user called by user %s" % request.user)
|
||||
if request.method == 'POST':
|
||||
form = LoginForm(request.POST)
|
||||
logger.debug("Request of type POST, received form, valid: %s" % form.is_valid())
|
||||
if form.is_valid():
|
||||
user = authenticate(username=form.cleaned_data['username'], password=form.cleaned_data['password'])
|
||||
logger.debug("Authentication attempt with supplied credentials. Received user %s" % user)
|
||||
if user is not None:
|
||||
if user.is_active:
|
||||
logger.info("Successful login attempt from user %s" % user)
|
||||
login(request, user)
|
||||
redirect_to = request.POST.get('next', request.GET.get('next', ''))
|
||||
if not redirect_to:
|
||||
redirect_to = 'auth_dashboard'
|
||||
return redirect(redirect_to)
|
||||
else:
|
||||
logger.info("Login attempt failed for user %s: user marked inactive." % user)
|
||||
messages.warning(request, _('Your account has been disabled.'))
|
||||
else:
|
||||
logger.info("Failed login attempt: provided username %s" % form.cleaned_data['username'])
|
||||
messages.error(request, _('Username/password invalid.'))
|
||||
return render(request, 'public/login.html', context={'form': form})
|
||||
else:
|
||||
logger.debug("Providing new login form.")
|
||||
form = LoginForm()
|
||||
|
||||
return render(request, 'public/login.html', context={'form': form})
|
||||
|
||||
|
||||
def logout_user(request):
|
||||
logger.debug("logout_user called by user %s" % request.user)
|
||||
temp_user = request.user
|
||||
logout(request)
|
||||
logger.info("Successful logout for user %s" % temp_user)
|
||||
return redirect("auth_index")
|
||||
|
||||
|
||||
def register_user_view(request):
|
||||
logger.debug("register_user_view called by user %s" % request.user)
|
||||
if request.method == 'POST':
|
||||
form = RegistrationForm(request.POST)
|
||||
logger.debug("Request type POST contains form valid: %s" % form.is_valid())
|
||||
if form.is_valid():
|
||||
|
||||
if not User.objects.filter(username=form.cleaned_data['username']).exists():
|
||||
user = User.objects.create_user(form.cleaned_data['username'],
|
||||
form.cleaned_data['email'], form.cleaned_data['password'])
|
||||
|
||||
user.save()
|
||||
logger.info("Created new user %s" % user)
|
||||
login(request, user)
|
||||
messages.warning(request, _('Add an API key to set up your account.'))
|
||||
return redirect("auth_dashboard")
|
||||
|
||||
else:
|
||||
logger.error("Unable to register new user: username %s already exists." % form.cleaned_data['username'])
|
||||
return render(request, 'public/register.html', context={'form': form, 'error': True})
|
||||
else:
|
||||
logger.debug("Registration form invalid. Returning for user %s to make corrections." % request.user)
|
||||
|
||||
else:
|
||||
logger.debug("Returning blank registration form.")
|
||||
form = RegistrationForm()
|
||||
|
||||
return render(request, 'public/register.html', context={'form': form})
|
||||
|
||||
|
||||
def index_view(request):
|
||||
logger.debug("index_view called by user %s" % request.user)
|
||||
return render(request, 'public/index.html')
|
||||
|
||||
|
||||
@login_required
|
||||
def help_view(request):
|
||||
logger.debug("help_view called by user %s" % request.user)
|
||||
return render(request, 'registered/help.html')
|
||||
|
||||
|
||||
@token_required(new=True)
|
||||
def sso_login(request, token):
|
||||
@token_required(scopes=settings.LOGIN_TOKEN_SCOPES)
|
||||
def main_character_change(request, token):
|
||||
logger.debug("main_character_change called by user %s for character %s" % (request.user, token.character_name))
|
||||
try:
|
||||
char = EveCharacter.objects.get(character_id=token.character_id)
|
||||
if char.user:
|
||||
if char.user.is_active:
|
||||
login(request, char.user)
|
||||
token.user = char.user
|
||||
token.save()
|
||||
return redirect('auth_dashboard')
|
||||
else:
|
||||
messages.error(request, _('Your account has been disabled.'))
|
||||
else:
|
||||
messages.warning(request,
|
||||
_('Authenticated character has no owning account. Please log in with username and password.'))
|
||||
except EveCharacter.DoesNotExist:
|
||||
messages.error(request, _('No account exists with the authenticated character. Please create an account first.'))
|
||||
return redirect(login_user)
|
||||
co = CharacterOwnership.objects.get(character__character_id=token.character_id)
|
||||
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")
|
||||
|
||||
|
||||
@token_required(new=True, scopes=settings.LOGIN_TOKEN_SCOPES)
|
||||
def add_character(request, token):
|
||||
if CharacterOwnership.objects.filter(character__character_id=token.character_id).filter(
|
||||
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})))
|
||||
return redirect('authentication:dashboard')
|
||||
|
||||
|
||||
"""
|
||||
Override the HMAC two-step registration view to accommodate the three-step registration required.
|
||||
Step 1: OAuth token to create user and profile.
|
||||
Step 2: Get email and send activation link (but do not save email).
|
||||
Step 3: Get link, save email and activate.
|
||||
|
||||
Step 1 is necessary to automatically assign character ownership and a main character, both of which require a saved User
|
||||
model - this means the ensuing registration form cannot create the user because it already exists.
|
||||
|
||||
Email is not saved to the user model in Step 2 as a way of differentiating users who have not yet completed registration
|
||||
(is_active=False) and users who have been disabled by an admin (is_active=False, email present).
|
||||
|
||||
Because of this, the email address needs to be assigned in Step 3 after clicking the link, which means the link must
|
||||
have the email address embedded much like the username. Key creation and decoding is overridden to support this action.
|
||||
"""
|
||||
|
||||
|
||||
# Step 1
|
||||
@token_required(new=True, scopes=settings.LOGIN_TOKEN_SCOPES)
|
||||
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')))
|
||||
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')
|
||||
else:
|
||||
messages.error(request, _('Unable to authenticate as the selected character.'))
|
||||
return redirect(settings.LOGIN_URL)
|
||||
|
||||
|
||||
# Step 2
|
||||
class RegistrationView(BaseRegistrationView):
|
||||
form_class = RegistrationForm
|
||||
success_url = 'authentication:dashboard'
|
||||
|
||||
def dispatch(self, *args, **kwargs):
|
||||
# We're storing a key in the session to pass user information from OAuth response. Make sure it's there.
|
||||
if not self.request.session.get('registration_uid', None) or not User.objects.filter(
|
||||
pk=self.request.session.get('registration_uid')).exists():
|
||||
messages.error(self.request, _('Registration token has expired.'))
|
||||
return redirect(settings.LOGIN_URL)
|
||||
return super(RegistrationView, self).dispatch(*args, **kwargs)
|
||||
|
||||
def register(self, form):
|
||||
user = User.objects.get(pk=self.request.session.get('registration_uid'))
|
||||
user.email = form.cleaned_data['email']
|
||||
user_registered.send(self.__class__, user=user, request=self.request)
|
||||
# Go to Step 3
|
||||
self.send_activation_email(user)
|
||||
return user
|
||||
|
||||
def get_activation_key(self, user):
|
||||
return signing.dumps(obj=[getattr(user, User.USERNAME_FIELD), user.email], salt=REGISTRATION_SALT)
|
||||
|
||||
|
||||
# Step 3
|
||||
class ActivationView(BaseActivationView):
|
||||
def validate_key(self, activation_key):
|
||||
try:
|
||||
dump = signing.loads(activation_key, salt=REGISTRATION_SALT,
|
||||
max_age=settings.ACCOUNT_ACTIVATION_DAYS * 86400)
|
||||
return dump
|
||||
except signing.BadSignature:
|
||||
return None
|
||||
|
||||
def activate(self, *args, **kwargs):
|
||||
dump = self.validate_key(kwargs.get('activation_key'))
|
||||
if dump:
|
||||
user = self.get_user(dump[0])
|
||||
if user:
|
||||
user.email = dump[1]
|
||||
user.is_active = True
|
||||
user.save()
|
||||
return user
|
||||
return False
|
||||
|
||||
Reference in New Issue
Block a user