Files
test-auth/sso/models.py

264 lines
9.6 KiB
Python

import re
import unicodedata
import logging
import types
from django.db import models
from django.db.models import signals
from django.contrib.auth.models import User, UserManager, Group
from django.utils import simplejson as json
from jsonfield.fields import JSONField
from eve_api.models import EVEAccount, EVEPlayerCorporation, EVEPlayerAlliance
from reddit.models import RedditAccount
from services import get_api
## Exceptions
class CorporateOnlyService(Exception):
pass
class ExistingUser(Exception):
pass
class ServiceError(Exception):
pass
## Models
class SSOUser(models.Model):
""" Extended SSO User Profile options """
user = models.ForeignKey(User, unique=True, related_name='profile')
api_service_password = models.CharField("API Services Password", max_length=200, blank=True)
@property
def _log(self):
if not hasattr(self, '__log'):
self.__log = logging.getLogger(self.__class__.__name__)
return self.__log
def update_access(self):
""" Steps through each Eve API registered to the user and updates their group
access accordingly """
self._log.debug("Update - User %s" % self.user)
# Create a list of all Corp and Alliance groups
corpgroups = []
for corp in EVEPlayerCorporation.objects.filter(group__isnull=False):
if corp.group:
corpgroups.append(corp.group)
for alliance in EVEPlayerAlliance.objects.filter(group__isnull=False):
if alliance.group:
corpgroups.append(alliance.group)
# Create a list of Char groups
chargroups = []
for eacc in EVEAccount.objects.filter(user=self.user):
if eacc.api_status in [1,3]:
for char in eacc.characters.all():
if char.corporation.group:
chargroups.append(char.corporation.group)
if char.corporation.alliance:
if char.corporation.alliance.group:
chargroups.append(char.corporation.alliance.group)
# Generate the list of groups to add/remove
delgroups = set(set(self.user.groups.all()) & set(corpgroups)) - set(chargroups)
addgroups = set(chargroups) - set(set(self.user.groups.all()) & set(corpgroups))
for g in delgroups:
self.user.groups.remove(g)
for g in addgroups:
self.user.groups.add(g)
# For users set to not active, delete all accounts
if not self.user.is_active:
self._log.debug("Inactive - User %s" % (self.user))
for servacc in ServiceAccount.objects.filter(user=self.user):
servacc.active = 0
servacc.save()
pass
# For each of the user's services, check they're in a valid group for it and enable/disable as needed.
for servacc in ServiceAccount.objects.filter(user=self.user):
if not (set(self.user.groups.all()) & set(servacc.service.groups.all())):
if servacc.active:
servacc.active = 0
servacc.save()
self._log.debug("Disabled - User %s, Acc %s" % (self.user, servacc.service))
servacc.user.message_set.create(message="Your %s account has been disabled due to lack of permissions. If this is incorrect, check your API keys to see if they are valid" % (servacc.service))
pass
else:
if not servacc.active:
servacc.active = 1
servacc.save()
self._log.debug("Enabled - User %s, Acc %s" % (self.user, servacc.service))
servacc.user.message_set.create(message="Your %s account has been re-enabled, you may need to reset your password to access this service again" % (servacc.service))
pass
def __str__(self):
return self.user.__str__()
@staticmethod
def create_user_profile(sender, instance, created, **kwargs):
if created:
profile, created = SSOUser.objects.get_or_create(user=instance)
@staticmethod
def update_service_groups(sender, instance, created, **kwargs):
if not created:
for acc in instance.serviceaccount_set.all():
cls = acc.service.api_class
cls.update_groups(acc.service_uid, instance.groups.all())
signals.post_save.connect(SSOUser.create_user_profile, sender=User)
#signals.post_save.connect(SSOUser.update_service_groups, sender=User)
class SSOUserNote(models.Model):
""" Notes bound to a user's account. Used to store information regarding the user """
user = models.ForeignKey(User, blank=False, null=False, related_name='notes')
note = models.TextField("Note", blank=False, null=False)
created_by = models.ForeignKey(User, blank=False, null=False)
date_created = models.DateTimeField(auto_now_add=True, blank=False, null=False,
verbose_name="Date/Time the note was added",
help_text="Shows the date and time the note was added to the account")
class Meta:
verbose_name = 'User Note'
verbose_name_plural = 'User Notes'
ordering = ['date_created']
class Service(models.Model):
"""
Service model represents a service available to users, either a website or
a connection service like Jabber or IRC.
"""
name = models.CharField("Service Name", max_length=200)
url = models.CharField("Service URL", max_length=200, blank=True)
active = models.BooleanField(default=True)
api = models.CharField("API", max_length=200)
groups = models.ManyToManyField(Group, blank=False)
settings_json = JSONField("Service Settings", blank=True)
class Meta:
verbose_name = 'Service'
verbose_name_plural = 'Services'
ordering = ['id']
@property
def provide_login(self):
return self.settings['provide_login']
@property
def api_class(self):
api = get_api(self.api)
api.settings = self.settings
return api
def __str__(self):
return self.name
def save(self):
if not self.settings_json:
if self.api:
self.settings_json = self.settings
else:
self.settings_json = {}
else:
if isinstance(self.settings_json, types.StringTypes):
self.settings_json = eval(self.settings_json)
return models.Model.save(self)
@property
def settings(self):
if self.settings_json:
setdict = self.settings_json
else:
setdict = {}
# Load defaults from the module's settings dict
if self.api:
modset = get_api(self.api).settings
for k in modset:
if not k in setdict:
setdict[k] = modset[k]
return setdict
class ServiceAccount(models.Model):
"""
ServiceAccount represents the user's account on a Service.
"""
user = models.ForeignKey(User, blank=False)
service = models.ForeignKey(Service, blank=False)
service_uid = models.CharField("Service UID", max_length=200, blank=False)
active = models.BooleanField(default=True)
character = None
username = None
password = None
class Meta:
verbose_name = 'Service Account'
verbose_name_plural = 'Service Accounts'
ordering = ['user']
def __str__(self):
return "%s: %s (%s)" % (self.service.name, self.user.username, self.service_uid)
def save(self):
""" Override default save to setup accounts as needed """
# Grab the API class and load the settings
api = self.service.api_class
if not self.service_uid:
# Create a account if we've not got a UID
if self.active:
# Force username to be the same as their selected character
# Fix unicode first of all
name = unicodedata.normalize('NFKD', self.character.name).encode('ASCII', 'ignore')
# Remove spaces and non-acceptable characters
self.username = re.sub('[^a-zA-Z0-9_-]+', '', name)
if not api.check_user(self.username):
eveapi = None
for eacc in EVEAccount.objects.filter(user=self.user):
if self.character in eacc.characters.all():
eveapi = eacc
break
reddit = RedditAccount.objects.filter(user=self.user)
self.service_uid = api.add_user(self.username, self.password, user=self.user, character=self.character, eveapi=eveapi, reddit=reddit)
if not self.service_uid:
raise ServiceError('Error occured while trying to create the Service Account, please try again later')
else:
raise ExistingUser('Username %s has already been took' % self.username)
else:
return
# Disable account marked as inactive
if self.service_uid and not self.active:
api.disable_user(self.service_uid)
# All went OK, save to the DB
return models.Model.save(self)
@staticmethod
def pre_delete_listener( **kwargs ):
api = kwargs['instance'].service.api_class
if not api.delete_user(kwargs['instance'].service_uid):
raise ServiceError('Unable to delete account on related service')
signals.pre_delete.connect(ServiceAccount.pre_delete_listener, sender=ServiceAccount)